Code enhancements.

This commit is contained in:
Stephen Chung 2020-12-26 13:05:57 +08:00
parent e1ac6cc90e
commit dc4e52e795
31 changed files with 621 additions and 391 deletions

View File

@ -13,11 +13,13 @@ Breaking changes
* `Engine::load_package` is renamed `Engine::register_global_module` and now must explicitly pass a shared [`Module`].
* `Engine::register_module` is renamed `Engine::register_static_module` and now must explicitly pass a shared [`Module`].
* `Package::get` is renamed `Package::as_shared_module`.
* `Engine::set_module_resolver` now takes a straight module resolver instead of an `Option`. To disable module resolving, use the new `DummyModuleResolver`.
Enhancements
------------
* `Scope` is now `Clone + Hash`.
* `Engine::register_static_module` now supports sub-module paths (e.g. `foo::bar::baz`).
Version 0.19.8

View File

@ -21,7 +21,7 @@
//! let m = exported_module!(advanced_math);
//! let mut r = StaticModuleResolver::new();
//! r.insert("Math::Advanced", m);
//! engine.set_module_resolver(Some(r));
//! engine.set_module_resolver(r);
//!
//! assert_eq!(engine.eval::<FLOAT>(
//! r#"
@ -51,7 +51,7 @@
//! set_exported_fn!(m, "euclidean_distance", distance_function);
//! let mut r = StaticModuleResolver::new();
//! r.insert("Math::Advanced", m);
//! engine.set_module_resolver(Some(r));
//! engine.set_module_resolver(r);
//!
//! assert_eq!(engine.eval::<FLOAT>(
//! r#"

View File

@ -18,14 +18,11 @@ fn raw_fn_test() -> Result<(), Box<EvalAltResult>> {
engine.register_fn("get_mystic_number", || 42 as FLOAT);
let mut m = Module::new();
rhai::set_exported_fn!(m, "euclidean_distance", raw_fn::distance_function);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let x = math::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#
r#"let x = Math::Advanced::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#
)?,
41.0
);
@ -48,16 +45,13 @@ fn raw_fn_mut_test() -> Result<(), Box<EvalAltResult>> {
engine.register_fn("get_mystic_number", || 42 as FLOAT);
let mut m = Module::new();
rhai::set_exported_fn!(m, "add_in_place", raw_fn_mut::add_in_place);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let x = get_mystic_number();
math::add_in_place(x, 1.0);
x"#
r#"let x = get_mystic_number();
Math::Advanced::add_in_place(x, 1.0);
x"#
)?,
43.0
);
@ -80,16 +74,10 @@ fn raw_fn_str_test() -> Result<(), Box<EvalAltResult>> {
engine.register_fn("get_mystic_number", || 42 as FLOAT);
let mut m = Module::new();
rhai::set_exported_fn!(m, "write_out_str", raw_fn_str::write_out_str);
let mut r = StaticModuleResolver::new();
r.insert("Host::IO", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Host::IO", m.into());
assert_eq!(
engine.eval::<bool>(
r#"import "Host::IO" as io;
let x = io::write_out_str("hello world!");
x"#
)?,
engine.eval::<bool>(r#"let x = Host::IO::write_out_str("hello world!"); x"#)?,
true
);
Ok(())
@ -138,18 +126,16 @@ fn mut_opaque_ref_test() -> Result<(), Box<EvalAltResult>> {
rhai::set_exported_fn!(m, "new_message", mut_opaque_ref::new_message);
rhai::set_exported_fn!(m, "new_os_message", mut_opaque_ref::new_os_message);
rhai::set_exported_fn!(m, "write_out_message", mut_opaque_ref::write_out_message);
let mut r = StaticModuleResolver::new();
r.insert("Host::Msg", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Host::Msg", m.into());
assert_eq!(
engine.eval::<bool>(
r#"import "Host::Msg" as msg;
let message1 = msg::new_message(true, "it worked");
let ok1 = msg::write_out_message(message1);
let message2 = msg::new_os_message(true, 0);
let ok2 = msg::write_out_message(message2);
ok1 && ok2"#
r#"
let message1 = Host::Msg::new_message(true, "it worked");
let ok1 = Host::Msg::write_out_message(message1);
let message2 = Host::Msg::new_os_message(true, 0);
let ok2 = Host::Msg::write_out_message(message2);
ok1 && ok2"#
)?,
true
);
@ -179,14 +165,11 @@ fn raw_returning_fn_test() -> Result<(), Box<EvalAltResult>> {
engine.register_fn("get_mystic_number", || 42 as FLOAT);
let mut m = Module::new();
rhai::set_exported_fn!(m, "euclidean_distance", raw_returning_fn::distance_function);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let x = math::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#
r#"let x = Math::Advanced::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#
)?,
41.0
);

View File

@ -12,14 +12,8 @@ pub mod empty_module {
fn empty_module_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::empty_module::EmptyModule);
let mut r = StaticModuleResolver::new();
r.insert("Module::Empty", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Module::Empty", m.into());
assert_eq!(
engine.eval::<INT>(r#"import "Module::Empty" as m; 42"#)?,
42
);
Ok(())
}
@ -39,16 +33,10 @@ pub mod one_fn_module {
fn one_fn_module_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::one_fn_module::advanced_math);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let m = math::get_mystic_number();
m"#
)?,
engine.eval::<FLOAT>(r#"let m = Math::Advanced::get_mystic_number();m"#)?,
42.0
);
Ok(())
@ -73,16 +61,14 @@ pub mod one_fn_and_const_module {
fn one_fn_and_const_module_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::one_fn_and_const_module::advanced_math);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let m = math::MYSTIC_NUMBER;
let x = math::euclidean_distance(0.0, 1.0, 0.0, m);
x"#
r#"
let m = Math::Advanced::MYSTIC_NUMBER;
let x = Math::Advanced::euclidean_distance(0.0, 1.0, 0.0, m);
x"#
)?,
41.0
);
@ -105,16 +91,10 @@ pub mod raw_fn_str_module {
fn raw_fn_str_module_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::raw_fn_str_module::host_io);
let mut r = StaticModuleResolver::new();
r.insert("Host::IO", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Host::IO", m.into());
assert_eq!(
engine.eval::<bool>(
r#"import "Host::IO" as io;
let x = io::write_out_str("hello world!");
x"#
)?,
engine.eval::<bool>(r#"let x = Host::IO::write_out_str("hello world!"); x"#)?,
true
);
Ok(())
@ -162,19 +142,17 @@ pub mod mut_opaque_ref_module {
fn mut_opaque_ref_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::mut_opaque_ref_module::host_msg);
let mut r = StaticModuleResolver::new();
r.insert("Host::Msg", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Host::Msg", m.into());
assert_eq!(
engine.eval::<bool>(
r#"import "Host::Msg" as msg;
let success = "it worked";
let message1 = msg::new_message(true, success);
let ok1 = msg::write_out_message(message1);
let message2 = msg::new_os_message(true, 0);
let ok2 = msg::write_out_message(message2);
ok1 && ok2"#
r#"
let success = "it worked";
let message1 = Host::Msg::new_message(true, success);
let ok1 = Host::Msg::write_out_message(message1);
let message2 = Host::Msg::new_os_message(true, 0);
let ok2 = Host::Msg::write_out_message(message2);
ok1 && ok2"#
)?,
true
);
@ -204,18 +182,16 @@ fn duplicate_fn_rename_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
engine.register_fn("get_mystic_number", || 42 as FLOAT);
let m = rhai::exported_module!(crate::duplicate_fn_rename::my_adds);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
let output_array = engine.eval::<Array>(
r#"import "Math::Advanced" as math;
let fx = get_mystic_number();
let fy = math::add(fx, 1.0);
let ix = 42;
let iy = math::add(ix, 1);
[fy, iy]
"#,
r#"
let fx = get_mystic_number();
let fy = Math::Advanced::add(fx, 1.0);
let ix = 42;
let iy = Math::Advanced::add(ix, 1);
[fy, iy]
"#,
)?;
assert_eq!(&output_array[0].as_float().unwrap(), &43.0);
assert_eq!(&output_array[1].as_int().unwrap(), &43);
@ -329,20 +305,18 @@ mod export_by_prefix {
fn export_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::export_by_prefix::my_adds);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
let output_array = engine.eval::<Array>(
r#"import "Math::Advanced" as math;
let ex = 41.0;
let fx = math::foo_add_f(ex, 1.0);
let gx = math::foo_m(41.0, 1.0);
let ei = 41;
let fi = math::bar_add_i(ei, 1);
let gi = math::foo_n(41, 1);
[fx, gx, fi, gi]
"#,
r#"
let ex = 41.0;
let fx = Math::Advanced::foo_add_f(ex, 1.0);
let gx = Math::Advanced::foo_m(41.0, 1.0);
let ei = 41;
let fi = Math::Advanced::bar_add_i(ei, 1);
let gi = Math::Advanced::foo_n(41, 1);
[fx, gx, fi, gi]
"#,
)?;
assert_eq!(&output_array[0].as_float().unwrap(), &42.0);
assert_eq!(&output_array[1].as_float().unwrap(), &42.0);
@ -350,24 +324,24 @@ fn export_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
assert_eq!(&output_array[3].as_int().unwrap(), &42);
assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let ex = 41.0;
let fx = math::foo_add_float2(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::foo_add_float2 (f64, f64)"
&& p == rhai::Position::new(3, 23)));
r#"
let ex = 41.0;
let fx = Math::Advanced::foo_add_float2(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "Math::Advanced::foo_add_float2 (f64, f64)"
&& p == rhai::Position::new(3, 34)));
assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let ex = 41.0;
let fx = math::bar_m(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::bar_m (f64, f64)"
&& p == rhai::Position::new(3, 23)));
r#"
let ex = 41.0;
let fx = Math::Advanced::bar_m(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "Math::Advanced::bar_m (f64, f64)"
&& p == rhai::Position::new(3, 34)));
Ok(())
}
@ -413,20 +387,18 @@ mod export_all {
fn export_all_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::export_all::my_adds);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
let output_array = engine.eval::<Array>(
r#"import "Math::Advanced" as math;
let ex = 41.0;
let fx = math::foo_add_f(ex, 1.0);
let gx = math::foo_m(41.0, 1.0);
let ei = 41;
let fi = math::foo_add_i(ei, 1);
let gi = math::foo_n(41, 1);
[fx, gx, fi, gi]
"#,
r#"
let ex = 41.0;
let fx = Math::Advanced::foo_add_f(ex, 1.0);
let gx = Math::Advanced::foo_m(41.0, 1.0);
let ei = 41;
let fi = Math::Advanced::foo_add_i(ei, 1);
let gi = Math::Advanced::foo_n(41, 1);
[fx, gx, fi, gi]
"#,
)?;
assert_eq!(&output_array[0].as_float().unwrap(), &42.0);
assert_eq!(&output_array[1].as_float().unwrap(), &42.0);
@ -434,14 +406,14 @@ fn export_all_test() -> Result<(), Box<EvalAltResult>> {
assert_eq!(&output_array[3].as_int().unwrap(), &42);
assert!(matches!(*engine.eval::<INT>(
r#"import "Math::Advanced" as math;
let ex = 41;
let fx = math::foo_p(ex, 1);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::foo_p (i64, i64)"
&& p == rhai::Position::new(3, 23)));
r#"
let ex = 41;
let fx = Math::Advanced::foo_p(ex, 1);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "Math::Advanced::foo_p (i64, i64)"
&& p == rhai::Position::new(3, 34)));
Ok(())
}

View File

@ -20,16 +20,10 @@ pub mod one_fn_module_nested_attr {
fn one_fn_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::one_fn_module_nested_attr::advanced_math);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let m = math::get_mystic_number();
m"#
)?,
engine.eval::<FLOAT>(r#"let m = Math::Advanced::get_mystic_number(); m"#)?,
42.0
);
Ok(())
@ -56,16 +50,10 @@ pub mod one_fn_submodule_nested_attr {
fn one_fn_submodule_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::one_fn_submodule_nested_attr::advanced_math);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let m = math::constants::get_mystic_number();
m"#
)?,
engine.eval::<FLOAT>(r#"let m = Math::Advanced::constants::get_mystic_number(); m"#)?,
42.0
);
Ok(())
@ -131,26 +119,24 @@ mod export_nested_by_prefix {
fn export_nested_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::export_nested_by_prefix::my_adds);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
engine.register_static_module("Math::Advanced", m.into());
let output_array = engine.eval::<Array>(
r#"import "Math::Advanced" as math;
let ex = 41.0;
let fx = math::foo_first_adders::add_float(ex, 1.0);
r#"
let ex = 41.0;
let fx = Math::Advanced::foo_first_adders::add_float(ex, 1.0);
let ei = 41;
let fi = math::foo_first_adders::add_int(ei, 1);
let ei = 41;
let fi = Math::Advanced::foo_first_adders::add_int(ei, 1);
let gx = 41.0;
let hx = math::foo_second_adders::add_float(gx, 1.0);
let gx = 41.0;
let hx = Math::Advanced::foo_second_adders::add_float(gx, 1.0);
let gi = 41;
let hi = math::foo_second_adders::add_int(gi, 1);
let gi = 41;
let hi = Math::Advanced::foo_second_adders::add_int(gi, 1);
[fx, hx, fi, hi]
"#,
[fx, hx, fi, hi]
"#,
)?;
assert_eq!(&output_array[0].as_float().unwrap(), &42.0);
assert_eq!(&output_array[1].as_float().unwrap(), &42.0);
@ -158,44 +144,44 @@ fn export_nested_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
assert_eq!(&output_array[3].as_int().unwrap(), &42);
assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let ex = 41.0;
let fx = math::foo_third_adders::add_float(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::foo_third_adders::add_float (f64, f64)"
&& p == rhai::Position::new(3, 41)));
r#"
let ex = 41.0;
let fx = Math::Advanced::foo_third_adders::add_float(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "Math::Advanced::foo_third_adders::add_float (f64, f64)"
&& p == rhai::Position::new(3, 52)));
assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let ex = 41;
let fx = math::foo_third_adders::add_int(ex, 1);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::foo_third_adders::add_int (i64, i64)"
&& p == rhai::Position::new(3, 41)));
r#"
let ex = 41;
let fx = Math::Advanced::foo_third_adders::add_int(ex, 1);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "Math::Advanced::foo_third_adders::add_int (i64, i64)"
&& p == rhai::Position::new(3, 52)));
assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let ex = 41;
let fx = math::bar_fourth_adders::add_int(ex, 1);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::bar_fourth_adders::add_int (i64, i64)"
&& p == rhai::Position::new(3, 42)));
r#"
let ex = 41;
let fx = Math::Advanced::bar_fourth_adders::add_int(ex, 1);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "Math::Advanced::bar_fourth_adders::add_int (i64, i64)"
&& p == rhai::Position::new(3, 53)));
assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let ex = 41.0;
let fx = math::bar_fourth_adders::add_float(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::bar_fourth_adders::add_float (f64, f64)"
&& p == rhai::Position::new(3, 42)));
r#"
let ex = 41.0;
let fx = Math::Advanced::bar_fourth_adders::add_float(ex, 1.0);
fx
"#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "Math::Advanced::bar_fourth_adders::add_float (f64, f64)"
&& p == rhai::Position::new(3, 53)));
Ok(())
}

View File

@ -25,7 +25,6 @@ Use Case 1 - Make the `Module` Globally Available
`Engine::register_global_module` registers a shared [module] into the _global_ namespace.
All [functions] and [type iterators] can be accessed without _namespace qualifiers_.
Variables and sub-modules are **ignored**.
This is by far the easiest way to expose a module's functionalities to Rhai.
@ -35,11 +34,11 @@ use rhai::{Engine, Module};
let mut module = Module::new(); // new module
// Use the 'set_fn_XXX' API to add functions.
let hash = module.set_fn_1("inc", |x: i64| Ok(x+1));
// Use the 'Module::set_fn_XXX' API to add functions.
let hash = module.set_fn_1("inc", |x: i64| Ok(x + 1));
// Remember to update the parameter names/types and return type metadata.
// 'set_fn_XXX' by default does not set function metadata.
// 'Module::set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: i64", "i64"]);
// Register the module into the global namespace of the Engine.
@ -49,6 +48,19 @@ engine.register_global_module(module.into());
engine.eval::<i64>("inc(41)")? == 42; // no need to import module
```
Registering a [module] via `Engine::register_global_module` is essentially the _same_
as calling `Engine::register_fn` (or any of the `Engine::register_XXX` API) individually
on each top-level function within that [module]. In fact, the actual implementation of
`Engine::register_fn` etc. simply adds the function to an internal [module]!
```rust
// The above is essentially the same as:
let mut engine = Engine::new();
engine.register_fn("inc", |x: i64| x + 1);
engine.eval::<i64>("inc(41)")? == 42; // no need to import module
```
Use Case 2 - Make the `Module` a Static Module
---------------------------------------------
@ -60,25 +72,27 @@ use rhai::{Engine, Module};
let mut module = Module::new(); // new module
// Use the 'set_fn_XXX' API to add functions.
let hash = module.set_fn_1("inc", |x: i64| Ok(x+1));
// Use the 'Module::set_fn_XXX' API to add functions.
let hash = module.set_fn_1("inc", |x: i64| Ok(x + 1));
// Remember to update the parameter names/types and return type metadata.
// 'set_fn_XXX' by default does not set function metadata.
// 'Module::set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: i64", "i64"]);
// Register the module into the Engine as a static module namespace 'calc'
// Register the module into the Engine as the static module namespace path
// 'services::calc'
let mut engine = Engine::new();
engine.register_static_module("calc", module.into());
engine.register_static_module("services::calc", module.into());
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
// refer to the 'services::calc' module
engine.eval::<i64>("services::calc::inc(41)")? == 42;
```
### Expose Functions to the Global Namespace
`Module::set_fn_mut` and `Module::set_fn_XXX_mut` can optionally expose functions (usually _methods_)
in the module to the _global_ namespace, so [getters/setters] and [indexers] for [custom types]
can work as expected.
The `Module::set_fn_XXX_mut` API methods can optionally expose functions in the [module]
to the _global_ namespace by setting the `namespace` parameter to `FnNamespace::Global`,
so [getters/setters] and [indexers] for [custom types] can work as expected.
[Type iterators], because of their special nature, are _always_ exposed to the _global_ namespace.
@ -87,19 +101,24 @@ use rhai::{Engine, Module, FnNamespace};
let mut module = Module::new(); // new module
// Expose method 'inc' to the global namespace (default is 'Internal')
let hash = module.set_fn_1_mut("inc", FnNamespace::Global, |x: &mut i64| Ok(x+1));
// Expose method 'inc' to the global namespace (default is 'FnNamespace::Internal')
let hash = module.set_fn_1_mut("inc", FnNamespace::Global, |x: &mut i64| Ok(x + 1));
// Remember to update the parameter names/types and return type metadata.
// 'set_fn_XXX' by default does not set function metadata.
// 'Module::set_fn_XXX_mut' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: &mut i64", "i64"]);
// Register the module into the Engine as a static module namespace 'calc'
let mut engine = Engine::new();
engine.register_static_module("calc", module.into());
// The method 'inc' works as expected because it is exposed to the global namespace
// 'inc' works when qualified by the namespace
engine.eval::<i64>("calc::inc(41)")? == 42;
// 'inc' also works without a namespace qualifier
// because it is exposed to the global namespace
engine.eval::<i64>("let x = 41; x.inc()")? == 42;
engine.eval::<i64>("let x = 41; inc(x)")? == 42;
```
@ -118,7 +137,7 @@ use rhai::module_resolvers::StaticModuleResolver;
let mut module = Module::new(); // new module
module.set_var("answer", 41_i64); // variable 'answer' under module
module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add functions
module.set_fn_1("inc", |x: i64| Ok(x + 1)); // use the 'set_fn_XXX' API to add functions
// Create the module resolver
let mut resolver = StaticModuleResolver::new();
@ -129,7 +148,7 @@ resolver.insert("question", module);
// Set the module resolver into the 'Engine'
let mut engine = Engine::new();
engine.set_module_resolver(Some(resolver));
engine.set_module_resolver(resolver);
// Use namespace-qualified variables
engine.eval::<i64>(r#"import "question" as q; q::answer + 1"#)? == 42;

View File

@ -62,7 +62,7 @@ impl ModuleResolver for MyModuleResolver {
let mut engine = Engine::new();
// Set the custom module resolver into the 'Engine'.
engine.set_module_resolver(Some(MyModuleResolver {}));
engine.set_module_resolver(MyModuleResolver {});
engine.consume(r#"
import "hello" as foo; // this 'import' statement will call

View File

@ -153,13 +153,19 @@ A collection of module resolvers. Modules will be resolved from each resolver in
This is useful when multiple types of modules are needed simultaneously.
`DummyResolversCollection`
-------------------------
This module resolver acts as a _dummy_ and always fails all module resolution calls.
Set into `Engine`
-----------------
An [`Engine`]'s module resolver is set via a call to `Engine::set_module_resolver`:
```rust
use rhai::module_resolvers::StaticModuleResolver;
use rhai::module_resolvers::{DummyModuleResolver, StaticModuleResolver};
// Create a module resolver
let resolver = StaticModuleResolver::new();
@ -167,8 +173,9 @@ let resolver = StaticModuleResolver::new();
// Register functions into 'resolver'...
// Use the module resolver
engine.set_module_resolver(Some(resolver));
engine.set_module_resolver(resolver);
// Effectively disable 'import' statements by setting module resolver to 'None'
engine.set_module_resolver(None);
// Effectively disable 'import' statements by setting module resolver to
// the 'DummyModuleResolver' which acts as... well... a dummy.
engine.set_module_resolver(DummyModuleResolver::new());
```

View File

@ -748,13 +748,16 @@ impl Dynamic {
Self(Union::Variant(Box::new(boxed), AccessMode::ReadWrite))
}
/// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::RefCell]`<`[`Dynamic`]`>>`
/// or [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` depending on the `sync` feature.
/// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an
/// [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::RefCell]`<`[`Dynamic`]`>>` or
/// [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>`
/// depending on the `sync` feature.
///
/// Shared [`Dynamic`] values are relatively cheap to clone as they simply increment the
/// reference counts.
///
/// Shared [`Dynamic`] values can be converted seamlessly to and from ordinary [`Dynamic`] values.
/// Shared [`Dynamic`] values can be converted seamlessly to and from ordinary [`Dynamic`]
/// values.
///
/// If the [`Dynamic`] value is already shared, this method returns itself.
///
@ -970,8 +973,8 @@ impl Dynamic {
///
/// If the [`Dynamic`] is not a shared value, it returns itself.
///
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are
/// no outstanding references, or a cloned copy.
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are no
/// outstanding references, or a cloned copy.
#[inline(always)]
pub fn flatten(self) -> Self {
match self.0 {

View File

@ -18,7 +18,7 @@ use crate::stdlib::{
collections::{HashMap, HashSet},
fmt, format,
hash::{Hash, Hasher},
iter::{empty, once},
iter::{empty, once, FromIterator},
num::NonZeroU64,
num::NonZeroUsize,
ops::DerefMut,
@ -137,6 +137,22 @@ impl Imports {
}
}
impl<'a, T: IntoIterator<Item = (&'a ImmutableString, &'a Shared<Module>)>> From<T> for Imports {
fn from(value: T) -> Self {
Self(
value
.into_iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
)
}
}
impl FromIterator<(ImmutableString, Shared<Module>)> for Imports {
fn from_iter<T: IntoIterator<Item = (ImmutableString, Shared<Module>)>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 8;
@ -609,11 +625,11 @@ pub struct Engine {
/// A collection of all modules loaded into the global namespace of the Engine.
pub(crate) global_modules: StaticVec<Shared<Module>>,
/// A collection of all sub-modules directly loaded into the Engine.
pub(crate) global_sub_modules: Imports,
pub(crate) global_sub_modules: HashMap<ImmutableString, Shared<Module>>,
/// A module resolution service.
#[cfg(not(feature = "no_module"))]
pub(crate) module_resolver: Option<Box<dyn crate::ModuleResolver>>,
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
/// A hashmap mapping type names to pretty-print names.
pub(crate) type_names: HashMap<String, String>,
@ -745,7 +761,7 @@ impl Engine {
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))]
module_resolver: Some(Box::new(crate::module::resolvers::FileModuleResolver::new())),
module_resolver: Box::new(crate::module::resolvers::FileModuleResolver::new()),
#[cfg(not(feature = "no_module"))]
#[cfg(any(feature = "no_std", target_arch = "wasm32",))]
module_resolver: None,
@ -808,7 +824,7 @@ impl Engine {
global_sub_modules: Default::default(),
#[cfg(not(feature = "no_module"))]
module_resolver: None,
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
type_names: Default::default(),
disabled_symbols: Default::default(),
@ -849,15 +865,15 @@ impl Engine {
/// Search for a variable within the scope or within imports,
/// depending on whether the variable name is namespace-qualified.
pub(crate) fn search_namespace<'s, 'a>(
pub(crate) fn search_namespace<'s>(
&self,
scope: &'s mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &[&Module],
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr,
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
expr: &Expr,
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
match expr {
Expr::Variable(v) => match v.as_ref() {
// Qualified variable
@ -876,7 +892,7 @@ impl Engine {
// Module variables are constant
let mut target = target.clone();
target.set_access_mode(AccessMode::ReadOnly);
Ok((target.into(), name, *pos))
Ok((target.into(), *pos))
}
// Normal variable access
_ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
@ -886,15 +902,15 @@ impl Engine {
}
/// Search for a variable within the scope
pub(crate) fn search_scope_only<'s, 'a>(
pub(crate) fn search_scope_only<'s>(
&self,
scope: &'s mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &[&Module],
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr,
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
expr: &Expr,
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
let (index, _, Ident { name, pos }) = match expr {
Expr::Variable(v) => v.as_ref(),
_ => unreachable!(),
@ -903,7 +919,7 @@ impl Engine {
// Check if the variable is `this`
if name.as_str() == KEYWORD_THIS {
if let Some(val) = this_ptr {
return Ok(((*val).into(), KEYWORD_THIS, *pos));
return Ok(((*val).into(), *pos));
} else {
return EvalAltResult::ErrorUnboundThis(*pos).into();
}
@ -931,7 +947,7 @@ impl Engine {
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
{
result.set_access_mode(AccessMode::ReadOnly);
return Ok((result.into(), name, *pos));
return Ok((result.into(), *pos));
}
}
@ -945,7 +961,7 @@ impl Engine {
.0
};
let val = scope.get_mut(index);
let val = scope.get_mut_by_index(index);
// Check for data race - probably not necessary because the only place it should conflict is in a method call
// when the object variable is also used as a parameter.
@ -953,7 +969,7 @@ impl Engine {
// return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
// }
Ok((val.into(), name, *pos))
Ok((val.into(), *pos))
}
/// Chain-evaluate a dot/index chain.
@ -1300,7 +1316,7 @@ impl Engine {
self.inc_operations(state, *var_pos)?;
let (target, _, pos) =
let (target, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
// Constants cannot be modified
@ -1594,7 +1610,7 @@ impl Engine {
match expr {
// var - point directly to the value
Expr::Variable(_) => {
let (mut target, _, pos) =
let (mut target, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
// If necessary, constants are cloned
@ -1720,7 +1736,7 @@ impl Engine {
}
}
Expr::Variable(_) => {
let (val, _, _) = self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
let (val, _) = self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
Ok(val.take_or_clone())
}
Expr::Property(_) => unreachable!(),
@ -1926,11 +1942,15 @@ impl Engine {
let mut rhs_val = self
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
.flatten();
let (mut lhs_ptr, name, pos) =
let (mut lhs_ptr, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
if !lhs_ptr.is_ref() {
return EvalAltResult::ErrorAssignmentToConstant(name.to_string(), pos).into();
return EvalAltResult::ErrorAssignmentToConstant(
lhs_expr.get_variable_access(false).unwrap().to_string(),
pos,
)
.into();
}
self.inc_operations(state, pos)?;
@ -1938,7 +1958,7 @@ impl Engine {
if lhs_ptr.as_ref().is_read_only() {
// Assignment to constant variable
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
name.to_string(),
lhs_expr.get_variable_access(false).unwrap().to_string(),
pos,
)))
} else if op.is_empty() {
@ -2194,7 +2214,7 @@ impl Engine {
state.scope_level += 1;
for iter_value in func(iter_obj) {
let loop_var = scope.get_mut(index);
let loop_var = scope.get_mut_by_index(index);
let value = iter_value.flatten();
if cfg!(not(feature = "no_closure")) && loop_var.is_shared() {
@ -2360,31 +2380,24 @@ impl Engine {
.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
.try_cast::<ImmutableString>()
{
if let Some(resolver) = &self.module_resolver {
let module = resolver.resolve(self, &path, expr.position())?;
let module = self.module_resolver.resolve(self, &path, expr.position())?;
if let Some(name_def) = alias {
if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index();
mods.push(name_def.name.clone(), module);
} else {
mods.push(name_def.name.clone(), module);
}
// When imports list is modified, clear the functions lookup cache
state.functions_cache.clear();
if let Some(name_def) = alias {
if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index();
mods.push(name_def.name.clone(), module);
} else {
mods.push(name_def.name.clone(), module);
}
state.modules += 1;
Ok(Dynamic::UNIT)
} else {
Err(
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
.into(),
)
// When imports list is modified, clear the functions lookup cache
state.functions_cache.clear();
}
state.modules += 1;
Ok(Dynamic::UNIT)
} else {
Err(self.make_type_mismatch_err::<ImmutableString>("", expr.position()))
}
@ -2410,7 +2423,7 @@ impl Engine {
#[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => {
if let Some((index, _)) = scope.get_index(&x.name) {
let val = scope.get_mut(index);
let val = scope.get_mut_by_index(index);
if !val.is_shared() {
// Replace the variable with a shared value.

View File

@ -209,9 +209,10 @@ impl Engine {
pub fn register_get<T: Variant + Clone, U: Variant + Clone>(
&mut self,
name: &str,
callback: impl Fn(&mut T) -> U + SendSync + 'static,
get_fn: impl Fn(&mut T) -> U + SendSync + 'static,
) -> &mut Self {
crate::RegisterFn::register_fn(self, &crate::engine::make_getter(name), callback)
use crate::{engine::make_getter, RegisterFn};
self.register_fn(&make_getter(name), get_fn)
}
/// Register a getter function for a member of a registered type with the [`Engine`].
///
@ -255,13 +256,10 @@ impl Engine {
pub fn register_get_result<T: Variant + Clone>(
&mut self,
name: &str,
callback: impl Fn(&mut T) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
get_fn: impl Fn(&mut T) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self {
crate::RegisterResultFn::register_result_fn(
self,
&crate::engine::make_getter(name),
callback,
)
use crate::{engine::make_getter, RegisterResultFn};
self.register_result_fn(&make_getter(name), get_fn)
}
/// Register a setter function for a member of a registered type with the [`Engine`].
///
@ -304,9 +302,10 @@ impl Engine {
pub fn register_set<T: Variant + Clone, U: Variant + Clone>(
&mut self,
name: &str,
callback: impl Fn(&mut T, U) + SendSync + 'static,
set_fn: impl Fn(&mut T, U) + SendSync + 'static,
) -> &mut Self {
crate::RegisterFn::register_fn(self, &crate::engine::make_setter(name), callback)
use crate::{engine::make_setter, RegisterFn};
self.register_fn(&make_setter(name), set_fn)
}
/// Register a setter function for a member of a registered type with the [`Engine`].
///
@ -352,13 +351,12 @@ impl Engine {
pub fn register_set_result<T: Variant + Clone, U: Variant + Clone>(
&mut self,
name: &str,
callback: impl Fn(&mut T, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
set_fn: impl Fn(&mut T, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self {
crate::RegisterResultFn::register_result_fn(
self,
&crate::engine::make_setter(name),
move |obj: &mut T, value: U| callback(obj, value).map(Into::into),
)
use crate::{engine::make_setter, RegisterResultFn};
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| {
set_fn(obj, value).map(Into::into)
})
}
/// Short-hand for registering both getter and setter functions
/// of a registered type with the [`Engine`].
@ -453,7 +451,7 @@ impl Engine {
#[inline(always)]
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
&mut self,
callback: impl Fn(&mut T, X) -> U + SendSync + 'static,
get_fn: impl Fn(&mut T, X) -> U + SendSync + 'static,
) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
@ -469,7 +467,8 @@ impl Engine {
panic!("Cannot register indexer for strings.");
}
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_GET, callback)
use crate::{engine::FN_IDX_GET, RegisterFn};
self.register_fn(FN_IDX_GET, get_fn)
}
/// Register an index getter for a custom type with the [`Engine`].
///
@ -518,7 +517,7 @@ impl Engine {
#[inline(always)]
pub fn register_indexer_get_result<T: Variant + Clone, X: Variant + Clone>(
&mut self,
callback: impl Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
get_fn: impl Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
@ -534,7 +533,8 @@ impl Engine {
panic!("Cannot register indexer for strings.");
}
crate::RegisterResultFn::register_result_fn(self, crate::engine::FN_IDX_GET, callback)
use crate::{engine::FN_IDX_GET, RegisterResultFn};
self.register_result_fn(FN_IDX_GET, get_fn)
}
/// Register an index setter for a custom type with the [`Engine`].
///
@ -581,7 +581,7 @@ impl Engine {
#[inline(always)]
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
&mut self,
callback: impl Fn(&mut T, X, U) + SendSync + 'static,
set_fn: impl Fn(&mut T, X, U) + SendSync + 'static,
) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
@ -597,7 +597,8 @@ impl Engine {
panic!("Cannot register indexer for strings.");
}
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_SET, callback)
use crate::{engine::FN_IDX_SET, RegisterFn};
self.register_fn(FN_IDX_SET, set_fn)
}
/// Register an index setter for a custom type with the [`Engine`].
///
@ -651,7 +652,7 @@ impl Engine {
U: Variant + Clone,
>(
&mut self,
callback: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
set_fn: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
@ -667,11 +668,10 @@ impl Engine {
panic!("Cannot register indexer for strings.");
}
crate::RegisterResultFn::register_result_fn(
self,
crate::engine::FN_IDX_SET,
move |obj: &mut T, index: X, value: U| callback(obj, index, value).map(Into::into),
)
use crate::{engine::FN_IDX_SET, RegisterResultFn};
self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| {
set_fn(obj, index, value).map(Into::into)
})
}
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
///
@ -755,7 +755,7 @@ impl Engine {
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::{Engine, Module};
/// use rhai::{Engine, Shared, Module};
///
/// let mut engine = Engine::new();
///
@ -763,9 +763,18 @@ impl Engine {
/// let mut module = Module::new();
/// module.set_fn_1("calc", |x: i64| Ok(x + 1));
///
/// // Register the module as a fixed sub-module
/// engine.register_static_module("CalcService", module.into());
/// let module: Shared<Module> = module.into();
///
/// // Register the module as a fixed sub-module
/// engine.register_static_module("foo::bar::baz", module.clone());
///
/// // Multiple registrations to the same partial path is also OK!
/// engine.register_static_module("foo::bar::hello", module.clone());
///
/// engine.register_static_module("CalcService", module);
///
/// assert_eq!(engine.eval::<i64>("foo::bar::baz::calc(41)")?, 42);
/// assert_eq!(engine.eval::<i64>("foo::bar::hello::calc(41)")?, 42);
/// assert_eq!(engine.eval::<i64>("CalcService::calc(41)")?, 42);
/// # Ok(())
/// # }
@ -773,19 +782,49 @@ impl Engine {
#[cfg(not(feature = "no_module"))]
pub fn register_static_module(
&mut self,
name: impl Into<crate::ImmutableString>,
name: impl AsRef<str>,
module: Shared<Module>,
) -> &mut Self {
if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index();
self.global_sub_modules.push(name, module);
} else {
self.global_sub_modules.push(name, module);
fn register_static_module_raw(
root: &mut crate::stdlib::collections::HashMap<crate::ImmutableString, Shared<Module>>,
name: impl AsRef<str>,
module: Shared<Module>,
) {
let separator = crate::token::Token::DoubleColon.syntax();
if !name.as_ref().contains(separator.as_ref()) {
if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index();
root.insert(name.as_ref().trim().into(), module.into());
} else {
root.insert(name.as_ref().trim().into(), module);
}
} else {
let mut iter = name.as_ref().splitn(2, separator.as_ref());
let sub_module = iter.next().unwrap().trim();
let remainder = iter.next().unwrap().trim();
if !root.contains_key(sub_module) {
let mut m: Module = Default::default();
register_static_module_raw(m.sub_modules_mut(), remainder, module);
m.build_index();
root.insert(sub_module.into(), m.into());
} else {
let m = root.remove(sub_module).unwrap();
let mut m = crate::fn_native::shared_take_or_clone(m);
register_static_module_raw(m.sub_modules_mut(), remainder, module);
m.build_index();
root.insert(sub_module.into(), m.into());
}
}
}
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module);
self
}
/// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`].
///
/// ## Deprecated
@ -796,7 +835,7 @@ impl Engine {
#[deprecated = "use `register_static_module` instead"]
pub fn register_module(
&mut self,
name: impl Into<crate::ImmutableString>,
name: impl AsRef<str>,
module: impl Into<Shared<Module>>,
) -> &mut Self {
self.register_static_module(name, module.into())
@ -1407,7 +1446,7 @@ impl Engine {
scope: &mut Scope,
ast: &AST,
) -> Result<T, Box<EvalAltResult>> {
let mods = &mut self.global_sub_modules.clone();
let mods = &mut (&self.global_sub_modules).into();
let result = self.eval_ast_with_scope_raw(scope, mods, ast)?;
@ -1493,7 +1532,7 @@ impl Engine {
scope: &mut Scope,
ast: &AST,
) -> Result<(), Box<EvalAltResult>> {
let mods = &mut self.global_sub_modules.clone();
let mods = &mut (&self.global_sub_modules).into();
let state = &mut State {
source: ast.clone_source(),
..Default::default()
@ -1539,12 +1578,12 @@ impl Engine {
/// ```
#[cfg(not(feature = "no_function"))]
#[inline]
pub fn call_fn<A: crate::fn_args::FuncArgs, T: Variant + Clone>(
pub fn call_fn<T: Variant + Clone>(
&self,
scope: &mut Scope,
ast: &AST,
name: &str,
args: A,
args: impl crate::fn_args::FuncArgs,
) -> Result<T, Box<EvalAltResult>> {
let mut arg_values = args.into_vec();
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
@ -1650,7 +1689,7 @@ impl Engine {
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
let mut state = Default::default();
let mut mods = self.global_sub_modules.clone();
let mut mods = (&self.global_sub_modules).into();
// Check for data race.
if cfg!(not(feature = "no_closure")) {

View File

@ -168,9 +168,9 @@ impl Engine {
#[inline(always)]
pub fn set_module_resolver(
&mut self,
resolver: Option<impl crate::ModuleResolver + 'static>,
resolver: impl crate::ModuleResolver + 'static,
) -> &mut Self {
self.module_resolver = resolver.map(|f| Box::new(f) as Box<dyn crate::ModuleResolver>);
self.module_resolver = Box::new(resolver);
self
}
/// Disable a particular keyword or operator in the language.

View File

@ -1041,7 +1041,7 @@ impl Engine {
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
.collect::<Result<_, _>>()?;
let (mut target, _, pos) =
let (mut target, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
if target.as_ref().is_read_only() {
@ -1137,7 +1137,7 @@ impl Engine {
.collect::<Result<_, _>>()?;
// Get target reference to first argument
let (target, _, pos) =
let (target, pos) =
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
self.inc_operations(state, pos)?;

View File

@ -27,15 +27,15 @@ pub trait Func<ARGS, RET> {
/// // Func takes two type parameters:
/// // 1) a tuple made up of the types of the script function's parameters
/// // 2) the return type of the script function
/// //
///
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
/// let func = Func::<(i64, String), bool>::create_from_ast(
/// // ^^^^^^^^^^^^^ function parameter types in tuple
///
/// engine, // the 'Engine' is consumed into the closure
/// ast, // the 'AST'
/// "calc" // the entry-point function name
/// );
/// engine, // the 'Engine' is consumed into the closure
/// ast, // the 'AST'
/// "calc" // the entry-point function name
/// );
///
/// func(123, "hello".to_string())? == false; // call the anonymous function
/// # Ok(())
@ -58,15 +58,15 @@ pub trait Func<ARGS, RET> {
/// // Func takes two type parameters:
/// // 1) a tuple made up of the types of the script function's parameters
/// // 2) the return type of the script function
/// //
///
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
/// let func = Func::<(i64, String), bool>::create_from_script(
/// // ^^^^^^^^^^^^^ function parameter types in tuple
///
/// engine, // the 'Engine' is consumed into the closure
/// script, // the script, notice number of parameters must match
/// "calc" // the entry-point function name
/// )?;
/// engine, // the 'Engine' is consumed into the closure
/// script, // the script, notice number of parameters must match
/// "calc" // the entry-point function name
/// )?;
///
/// func(123, "hello".to_string())? == false; // call the anonymous function
/// # Ok(())

View File

@ -3,7 +3,14 @@
use crate::ast::{FnAccess, ScriptFnDef};
use crate::engine::Imports;
use crate::plugin::PluginFunction;
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String};
use crate::stdlib::{
boxed::Box,
convert::{TryFrom, TryInto},
fmt,
iter::empty,
mem,
string::String,
};
use crate::token::is_valid_identifier;
use crate::{
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module,
@ -153,25 +160,13 @@ impl<'e, 's, 'a, 'm, 'pm> NativeCallContext<'e, 's, 'a, 'm, 'pm> {
args: &mut [&mut Dynamic],
def_value: Option<&Dynamic>,
) -> Result<Dynamic, Box<EvalAltResult>> {
let mut mods = self.mods.cloned().unwrap_or_default();
let hash_script = calc_script_fn_hash(
empty(),
fn_name,
if is_method {
args.len() - 1
} else {
args.len()
},
);
self.engine()
.exec_fn_call(
&mut mods,
&mut self.mods.cloned().unwrap_or_default(),
&mut Default::default(),
self.lib,
fn_name,
hash_script,
calc_script_fn_hash(empty(), fn_name, args.len() - if is_method { 1 } else { 0 }),
args,
is_method,
is_method,
@ -225,11 +220,15 @@ pub type FnCallArgs<'a> = [&'a mut Dynamic];
/// A general function pointer, which may carry additional (i.e. curried) argument values
/// to be passed onto a function during a call.
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone)]
pub struct FnPtr(ImmutableString, StaticVec<Dynamic>);
impl FnPtr {
/// Create a new function pointer.
pub fn new(name: impl Into<ImmutableString>) -> Result<Self, Box<EvalAltResult>> {
name.into().try_into()
}
/// Create a new function pointer without checking its parameters.
#[inline(always)]
pub(crate) fn new_unchecked(
name: impl Into<ImmutableString>,
@ -257,7 +256,24 @@ impl FnPtr {
pub fn curry(&self) -> &[Dynamic] {
self.1.as_ref()
}
/// Does this function pointer refer to an anonymous function?
/// Add a new curried argument.
#[inline(always)]
pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
self.1.push(value);
self
}
/// Set curried arguments to the function pointer.
#[inline(always)]
pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
self.1 = values.into_iter().collect();
self
}
/// Is the function pointer curried?
#[inline(always)]
pub fn is_curried(&self) -> bool {
!self.1.is_empty()
}
/// Does the function pointer refer to an anonymous function?
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn is_anonymous(&self) -> bool {

View File

@ -35,9 +35,9 @@ use crate::Map;
/// A type representing the namespace of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnNamespace {
/// Global namespace.
/// Expose to global namespace.
Global,
/// Internal only.
/// Module namespace only.
Internal,
}
@ -465,6 +465,16 @@ impl Module {
.map(|FuncInfo { func, .. }| func.get_fn_def())
}
/// Get a mutable reference to the underlying [`HashMap`] of sub-modules.
#[inline(always)]
pub(crate) fn sub_modules_mut(&mut self) -> &mut HashMap<ImmutableString, Shared<Module>> {
// We must assume that the user has changed the sub-modules
// (otherwise why take a mutable reference?)
self.indexed = false;
&mut self.modules
}
/// Does a sub-module exist in the module?
///
/// # Example
@ -1699,7 +1709,7 @@ impl Module {
ast: &crate::AST,
engine: &crate::Engine,
) -> Result<Self, Box<EvalAltResult>> {
let mut mods = engine.global_sub_modules.clone();
let mut mods: crate::engine::Imports = (&engine.global_sub_modules).into();
let orig_mods_len = mods.len();
// Run the script

View File

@ -16,7 +16,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
/// collection.push(resolver);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(collection));
/// engine.set_module_resolver(collection);
/// ```
#[derive(Default)]
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
@ -36,16 +36,41 @@ impl ModuleResolversCollection {
/// collection.push(resolver);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(collection));
/// engine.set_module_resolver(collection);
/// ```
#[inline(always)]
pub fn new() -> Self {
Default::default()
}
/// Add a module keyed by its path.
/// Append a module resolver to the end.
#[inline(always)]
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) {
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) -> &mut Self {
self.0.push(Box::new(resolver));
self
}
/// Insert a module resolver to an offset index.
///
/// # Panics
///
/// Panics if the index is out of bounds.
#[inline(always)]
pub fn insert(&mut self, index: usize, resolver: impl ModuleResolver + 'static) -> &mut Self {
self.0.insert(index, Box::new(resolver));
self
}
/// Remove the last module resolver from the end, if any.
#[inline(always)]
pub fn pop(&mut self) -> Option<Box<dyn ModuleResolver>> {
self.0.pop()
}
/// Remove a module resolver at an offset index.
///
/// # Panics
///
/// Panics if the index is out of bounds.
#[inline(always)]
pub fn remove(&mut self, index: usize) -> Box<dyn ModuleResolver> {
self.0.remove(index)
}
/// Get an iterator of all the module resolvers.
#[inline(always)]
@ -59,8 +84,9 @@ impl ModuleResolversCollection {
}
/// Remove all module resolvers.
#[inline(always)]
pub fn clear(&mut self) {
pub fn clear(&mut self) -> &mut Self {
self.0.clear();
self
}
/// Is this [`ModuleResolversCollection`] empty?
#[inline(always)]
@ -75,8 +101,9 @@ impl ModuleResolversCollection {
/// Add another [`ModuleResolversCollection`] to the end of this collection.
/// The other [`ModuleResolversCollection`] is consumed.
#[inline(always)]
pub fn append(&mut self, other: Self) {
pub fn append(&mut self, other: Self) -> &mut Self {
self.0.extend(other.0.into_iter());
self
}
}

View File

@ -0,0 +1,48 @@
use crate::stdlib::boxed::Box;
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
/// Empty/disabled module resolution service that acts as a dummy.
///
/// # Example
///
/// ```
/// use rhai::{Engine, Module};
/// use rhai::module_resolvers::DummyModuleResolver;
///
/// let resolver = DummyModuleResolver::new();
/// let mut engine = Engine::new();
/// engine.set_module_resolver(resolver);
/// ```
#[derive(Debug, Copy, Eq, PartialEq, Clone, Default, Hash)]
pub struct DummyModuleResolver;
impl DummyModuleResolver {
/// Create a new [`DummyModuleResolver`].
///
/// # Example
///
/// ```
/// use rhai::{Engine, Module};
/// use rhai::module_resolvers::DummyModuleResolver;
///
/// let resolver = DummyModuleResolver::new();
/// let mut engine = Engine::new();
/// engine.set_module_resolver(resolver);
/// ```
#[inline(always)]
pub fn new() -> Self {
Default::default()
}
}
impl ModuleResolver for DummyModuleResolver {
#[inline(always)]
fn resolve(
&self,
_: &Engine,
path: &str,
pos: Position,
) -> Result<Shared<Module>, Box<EvalAltResult>> {
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
}
}

View File

@ -1,5 +1,9 @@
use crate::stdlib::{
boxed::Box, collections::HashMap, io::Error as IoError, path::PathBuf, string::String,
boxed::Box,
collections::HashMap,
io::Error as IoError,
path::{Path, PathBuf},
string::String,
};
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
@ -31,7 +35,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
///
/// let mut engine = Engine::new();
///
/// engine.set_module_resolver(Some(resolver));
/// engine.set_module_resolver(resolver);
/// ```
#[derive(Debug)]
pub struct FileModuleResolver {
@ -65,10 +69,10 @@ impl FileModuleResolver {
/// let resolver = FileModuleResolver::new_with_path("./scripts");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// engine.set_module_resolver(resolver);
/// ```
#[inline(always)]
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
pub fn new_with_path(path: impl Into<PathBuf>) -> Self {
Self::new_with_path_and_extension(path, "rhai")
}
@ -87,12 +91,12 @@ impl FileModuleResolver {
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// engine.set_module_resolver(resolver);
/// ```
#[inline(always)]
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
path: P,
extension: E,
pub fn new_with_path_and_extension(
path: impl Into<PathBuf>,
extension: impl Into<String>,
) -> Self {
Self {
path: path.into(),
@ -114,12 +118,64 @@ impl FileModuleResolver {
/// let resolver = FileModuleResolver::new();
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// engine.set_module_resolver(resolver);
/// ```
#[inline(always)]
pub fn new() -> Self {
Default::default()
}
/// Get the base path for script files.
#[inline(always)]
pub fn path(&self) -> &Path {
self.path.as_ref()
}
/// Set the base path for script files.
#[inline(always)]
pub fn set_path(&mut self, path: impl Into<PathBuf>) -> &mut Self {
self.path = path.into();
self
}
/// Get the script file extension.
#[inline(always)]
pub fn extension(&self) -> &str {
&self.extension
}
/// Set the script file extension.
#[inline(always)]
pub fn set_extension(&mut self, extension: impl Into<String>) -> &mut Self {
self.extension = extension.into();
self
}
/// Empty the internal cache.
#[inline(always)]
pub fn clear_cache(&mut self) {
#[cfg(not(feature = "sync"))]
self.cache.borrow_mut().clear();
#[cfg(feature = "sync")]
self.cache.write().unwrap().clear();
}
/// Empty the internal cache.
#[inline(always)]
pub fn clear_cache_for_path(&mut self, path: impl AsRef<Path>) -> Option<Shared<Module>> {
#[cfg(not(feature = "sync"))]
return self
.cache
.borrow_mut()
.remove_entry(path.as_ref())
.map(|(_, v)| v);
#[cfg(feature = "sync")]
return self
.cache
.write()
.unwrap()
.remove_entry(path.as_ref())
.map(|(_, v)| v);
}
}
impl ModuleResolver for FileModuleResolver {

View File

@ -2,6 +2,9 @@ use crate::fn_native::SendSync;
use crate::stdlib::boxed::Box;
use crate::{Engine, EvalAltResult, Module, Position, Shared};
mod dummy;
pub use dummy::DummyModuleResolver;
mod collection;
pub use collection::ModuleResolversCollection;

View File

@ -16,7 +16,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
///
/// let mut engine = Engine::new();
///
/// engine.set_module_resolver(Some(resolver));
/// engine.set_module_resolver(resolver);
/// ```
#[derive(Debug, Clone, Default)]
pub struct StaticModuleResolver(HashMap<String, Shared<Module>>);
@ -36,7 +36,7 @@ impl StaticModuleResolver {
/// resolver.insert("hello", module);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// engine.set_module_resolver(resolver);
/// ```
#[inline(always)]
pub fn new() -> Self {
@ -60,8 +60,13 @@ impl StaticModuleResolver {
}
/// Get an iterator of all the modules.
#[inline(always)]
pub fn iter(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
self.0.iter().map(|(k, v)| (k.as_str(), v.clone()))
pub fn iter(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
self.0.iter().map(|(k, v)| (k.as_str(), v))
}
/// Get a mutable iterator of all the modules.
#[inline(always)]
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Shared<Module>)> {
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
}
/// Get a mutable iterator of all the modules.
#[inline(always)]
@ -75,8 +80,8 @@ impl StaticModuleResolver {
}
/// Get an iterator of all the modules.
#[inline(always)]
pub fn values<'a>(&'a self) -> impl Iterator<Item = Shared<Module>> + 'a {
self.0.values().map(|m| m.clone())
pub fn values(&self) -> impl Iterator<Item = &Shared<Module>> {
self.0.values().map(|m| m)
}
/// Remove all modules.
#[inline(always)]
@ -95,6 +100,8 @@ impl StaticModuleResolver {
}
/// Merge another [`StaticModuleResolver`] into this.
/// The other [`StaticModuleResolver`] is consumed.
///
/// Existing modules of the same path name are overwritten.
#[inline(always)]
pub fn merge(&mut self, other: Self) {
if !other.is_empty() {

View File

@ -2,7 +2,7 @@
use crate::ast::{Expr, ScriptFnDef, Stmt};
use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::engine::{Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::fn_call::run_builtin_binary_op;
use crate::parser::map_dynamic_to_expr;
use crate::stdlib::{
@ -62,6 +62,8 @@ struct State<'a> {
variables: Vec<(String, AccessMode, Expr)>,
/// An [`Engine`] instance for eager function evaluation.
engine: &'a Engine,
/// Collection of sub-modules.
mods: Imports,
/// [Module] containing script-defined functions.
lib: &'a [&'a Module],
/// Optimization level.
@ -76,6 +78,7 @@ impl<'a> State<'a> {
changed: false,
variables: vec![],
engine,
mods: (&engine.global_sub_modules).into(),
lib,
optimization_level: level,
}
@ -655,7 +658,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
// Search for overloaded operators (can override built-in).
if !state.engine.has_override_by_name_and_arguments(Some(&state.engine.global_sub_modules), state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
.ok().flatten()
.and_then(|result| map_dynamic_to_expr(result, *pos))

View File

@ -91,13 +91,16 @@ pub enum ParseErrorType {
UnknownOperator(String),
/// Expecting a particular token but not finding one. Wrapped values are the token and description.
MissingToken(String, String),
/// An expression in function call arguments `()` has syntax error. Wrapped value is the error description (if any).
/// An expression in function call arguments `()` has syntax error. Wrapped value is the error
/// description (if any).
MalformedCallExpr(String),
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error description (if any).
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error
/// description (if any).
///
/// Never appears under the `no_index` feature.
MalformedIndexExpr(String),
/// An expression in an `in` expression has syntax error. Wrapped value is the error description (if any).
/// An expression in an `in` expression has syntax error. Wrapped value is the error description
/// (if any).
///
/// Never appears under the `no_object` and `no_index` features combination.
MalformedInExpr(String),
@ -137,7 +140,8 @@ pub enum ParseErrorType {
///
/// Never appears under the `no_function` feature.
FnMissingParams(String),
/// A function definition has duplicated parameters. Wrapped values are the function name and parameter name.
/// A function definition has duplicated parameters. Wrapped values are the function name and
/// parameter name.
///
/// Never appears under the `no_function` feature.
FnDuplicatedParam(String, String),

View File

@ -1325,6 +1325,7 @@ fn parse_unary(
}
}
/// Make an assignment statement.
fn make_assignment_stmt<'a>(
fn_name: Cow<'static, str>,
state: &mut ParseState,

View File

@ -13,8 +13,8 @@ pub use rhai_codegen::*;
pub use rhai_codegen::{export_fn, register_exported_fn};
/// Trait implemented by a _plugin function_.
/// This trait should not be used directly.
///
/// This trait should not be used directly.
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
pub trait PluginFunction {
/// Call the plugin function with the arguments provided.

View File

@ -55,8 +55,8 @@ pub enum EvalAltResult {
/// String indexing out-of-bounds.
/// Wrapped values are the current number of characters in the string and the index number.
ErrorStringBounds(usize, INT, Position),
/// Trying to index into a type that is not an array, an object map, or a string, and has no indexer function defined.
/// Wrapped value is the type name.
/// Trying to index into a type that is not an array, an object map, or a string, and has no
/// indexer function defined. Wrapped value is the type name.
ErrorIndexingType(String, Position),
/// Invalid arguments for `in` operator.
ErrorInExpr(Position),

View File

@ -41,9 +41,11 @@ use crate::{Dynamic, ImmutableString, StaticVec};
// [`Scope`] is implemented as two [`Vec`]'s of exactly the same length. Variables data (name, type, etc.)
// is manually split into two equal-length arrays. That's because variable names take up the most space,
// with [`Cow<str>`][Cow] being four words long, but in the vast majority of cases the name is NOT used to
/// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
//
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
//
// The alias is `Box`'ed because it occurs infrequently.
#[derive(Debug, Clone, Hash)]
pub struct Scope<'a> {
/// Current value of the entry.
@ -356,8 +358,35 @@ impl<'a> Scope<'a> {
self
}
/// Get a mutable reference to an entry in the [`Scope`].
///
/// If the entry by the specified name is not found, of if it is read-only,
/// [`None`] is returned.
///
/// # Example
///
/// ```
/// use rhai::Scope;
///
/// let mut my_scope = Scope::new();
///
/// my_scope.push("x", 42_i64);
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
///
/// let ptr = my_scope.get_mut("x").unwrap();
/// *ptr = 123_i64.into();
///
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 123);
/// ```
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
self.get_index(name)
.and_then(move |(index, access)| match access {
AccessMode::ReadWrite => Some(self.get_mut_by_index(index)),
AccessMode::ReadOnly => None,
})
}
/// Get a mutable reference to an entry in the [`Scope`] based on the index.
#[inline(always)]
pub(crate) fn get_mut(&mut self, index: usize) -> &mut Dynamic {
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
self.values.get_mut(index).expect("invalid index in Scope")
}
/// Update the access type of an entry in the [`Scope`].

View File

@ -63,7 +63,8 @@ pub fn get_hasher() -> impl Hasher {
s
}
/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and parameter types.
/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and
/// parameter types.
/// Exported under the `internals` feature only.
///
/// Module names are passed in via `&str` references from an iterator.
@ -108,6 +109,7 @@ pub fn calc_script_fn_hash<'a>(
/// # Note
///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline(always)]
fn calc_fn_hash<'a>(
mut modules: impl Iterator<Item = &'a str>,
fn_name: &str,

View File

@ -82,7 +82,7 @@ fn test_call_fn_private() -> Result<(), Box<EvalAltResult>> {
let ast = engine.compile("private fn add(x, n, ) { x + n }")?;
assert!(matches!(
*engine.call_fn::<_, INT>(&mut scope, &ast, "add", (40 as INT, 2 as INT))
*(engine.call_fn(&mut scope, &ast, "add", (40 as INT, 2 as INT)) as Result<INT, Box<EvalAltResult>>)
.expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "add"
));

View File

@ -239,7 +239,7 @@ fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
let f = move |p1: TestStruct, p2: TestStruct| -> Result<(), Box<EvalAltResult>> {
let action_ptr = res["action"].clone().cast::<FnPtr>();
let name = action_ptr.fn_name();
engine.call_fn::<_, ()>(&mut Scope::new(), &ast, name, (p1, p2))
engine.call_fn(&mut Scope::new(), &ast, name, (p1, p2))
};
// Test closure

View File

@ -101,7 +101,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
resolver.insert("hello", module);
let mut engine = Engine::new();
engine.set_module_resolver(Some(resolver));
engine.set_module_resolver(resolver);
assert_eq!(
engine.eval::<INT>(
@ -295,13 +295,13 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
"#,
)?;
engine.set_module_resolver(Some(resolver1));
engine.set_module_resolver(resolver1);
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
let mut resolver2 = StaticModuleResolver::new();
resolver2.insert("testing", module);
engine.set_module_resolver(Some(resolver2));
engine.set_module_resolver(resolver2);
assert_eq!(
engine.eval::<INT>(r#"import "testing" as ttt; ttt::abc"#)?,
@ -384,7 +384,7 @@ fn test_module_str() -> Result<(), Box<EvalAltResult>> {
let mut static_modules = rhai::module_resolvers::StaticModuleResolver::new();
static_modules.insert("test", module);
engine.set_module_resolver(Some(static_modules));
engine.set_module_resolver(static_modules);
assert_eq!(
engine.eval::<INT>(r#"import "test" as test; test::test("test");"#)?,
@ -418,7 +418,7 @@ fn test_module_ast_namespace() -> Result<(), Box<EvalAltResult>> {
let mut resolver = StaticModuleResolver::new();
resolver.insert("testing", module);
engine.set_module_resolver(Some(resolver));
engine.set_module_resolver(resolver);
assert_eq!(
engine.eval::<INT>(r#"import "testing" as t; t::foo(41)"#)?,
@ -466,7 +466,7 @@ fn test_module_ast_namespace2() -> Result<(), Box<EvalAltResult>> {
let module = Module::eval_ast_as_new(Scope::new(), &module_ast, &engine)?;
let mut static_modules = rhai::module_resolvers::StaticModuleResolver::new();
static_modules.insert("test_module", module);
engine.set_module_resolver(Some(static_modules));
engine.set_module_resolver(static_modules);
engine.consume(SCRIPT)?;