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::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`]. * `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`. * `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 Enhancements
------------ ------------
* `Scope` is now `Clone + Hash`. * `Scope` is now `Clone + Hash`.
* `Engine::register_static_module` now supports sub-module paths (e.g. `foo::bar::baz`).
Version 0.19.8 Version 0.19.8

View File

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

View File

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

View File

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

View File

@ -20,16 +20,10 @@ pub mod one_fn_module_nested_attr {
fn one_fn_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> { fn one_fn_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();
let m = rhai::exported_module!(crate::one_fn_module_nested_attr::advanced_math); let m = rhai::exported_module!(crate::one_fn_module_nested_attr::advanced_math);
let mut r = StaticModuleResolver::new(); engine.register_static_module("Math::Advanced", m.into());
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
assert_eq!( assert_eq!(
engine.eval::<FLOAT>( engine.eval::<FLOAT>(r#"let m = Math::Advanced::get_mystic_number(); m"#)?,
r#"import "Math::Advanced" as math;
let m = math::get_mystic_number();
m"#
)?,
42.0 42.0
); );
Ok(()) Ok(())
@ -56,16 +50,10 @@ pub mod one_fn_submodule_nested_attr {
fn one_fn_submodule_nested_attr_test() -> Result<(), Box<EvalAltResult>> { fn one_fn_submodule_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();
let m = rhai::exported_module!(crate::one_fn_submodule_nested_attr::advanced_math); let m = rhai::exported_module!(crate::one_fn_submodule_nested_attr::advanced_math);
let mut r = StaticModuleResolver::new(); engine.register_static_module("Math::Advanced", m.into());
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
assert_eq!( assert_eq!(
engine.eval::<FLOAT>( engine.eval::<FLOAT>(r#"let m = Math::Advanced::constants::get_mystic_number(); m"#)?,
r#"import "Math::Advanced" as math;
let m = math::constants::get_mystic_number();
m"#
)?,
42.0 42.0
); );
Ok(()) Ok(())
@ -131,26 +119,24 @@ mod export_nested_by_prefix {
fn export_nested_by_prefix_test() -> Result<(), Box<EvalAltResult>> { fn export_nested_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();
let m = rhai::exported_module!(crate::export_nested_by_prefix::my_adds); let m = rhai::exported_module!(crate::export_nested_by_prefix::my_adds);
let mut r = StaticModuleResolver::new(); engine.register_static_module("Math::Advanced", m.into());
r.insert("Math::Advanced", m);
engine.set_module_resolver(Some(r));
let output_array = engine.eval::<Array>( let output_array = engine.eval::<Array>(
r#"import "Math::Advanced" as math; r#"
let ex = 41.0; let ex = 41.0;
let fx = math::foo_first_adders::add_float(ex, 1.0); let fx = Math::Advanced::foo_first_adders::add_float(ex, 1.0);
let ei = 41; let ei = 41;
let fi = math::foo_first_adders::add_int(ei, 1); let fi = Math::Advanced::foo_first_adders::add_int(ei, 1);
let gx = 41.0; let gx = 41.0;
let hx = math::foo_second_adders::add_float(gx, 1.0); let hx = Math::Advanced::foo_second_adders::add_float(gx, 1.0);
let gi = 41; let gi = 41;
let hi = math::foo_second_adders::add_int(gi, 1); 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[0].as_float().unwrap(), &42.0);
assert_eq!(&output_array[1].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_eq!(&output_array[3].as_int().unwrap(), &42);
assert!(matches!(*engine.eval::<FLOAT>( assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math; r#"
let ex = 41.0; let ex = 41.0;
let fx = math::foo_third_adders::add_float(ex, 1.0); let fx = Math::Advanced::foo_third_adders::add_float(ex, 1.0);
fx fx
"#).unwrap_err(), "#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p) EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::foo_third_adders::add_float (f64, f64)" if s == "Math::Advanced::foo_third_adders::add_float (f64, f64)"
&& p == rhai::Position::new(3, 41))); && p == rhai::Position::new(3, 52)));
assert!(matches!(*engine.eval::<FLOAT>( assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math; r#"
let ex = 41; let ex = 41;
let fx = math::foo_third_adders::add_int(ex, 1); let fx = Math::Advanced::foo_third_adders::add_int(ex, 1);
fx fx
"#).unwrap_err(), "#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p) EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::foo_third_adders::add_int (i64, i64)" if s == "Math::Advanced::foo_third_adders::add_int (i64, i64)"
&& p == rhai::Position::new(3, 41))); && p == rhai::Position::new(3, 52)));
assert!(matches!(*engine.eval::<FLOAT>( assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math; r#"
let ex = 41; let ex = 41;
let fx = math::bar_fourth_adders::add_int(ex, 1); let fx = Math::Advanced::bar_fourth_adders::add_int(ex, 1);
fx fx
"#).unwrap_err(), "#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p) EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::bar_fourth_adders::add_int (i64, i64)" if s == "Math::Advanced::bar_fourth_adders::add_int (i64, i64)"
&& p == rhai::Position::new(3, 42))); && p == rhai::Position::new(3, 53)));
assert!(matches!(*engine.eval::<FLOAT>( assert!(matches!(*engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math; r#"
let ex = 41.0; let ex = 41.0;
let fx = math::bar_fourth_adders::add_float(ex, 1.0); let fx = Math::Advanced::bar_fourth_adders::add_float(ex, 1.0);
fx fx
"#).unwrap_err(), "#).unwrap_err(),
EvalAltResult::ErrorFunctionNotFound(s, p) EvalAltResult::ErrorFunctionNotFound(s, p)
if s == "math::bar_fourth_adders::add_float (f64, f64)" if s == "Math::Advanced::bar_fourth_adders::add_float (f64, f64)"
&& p == rhai::Position::new(3, 42))); && p == rhai::Position::new(3, 53)));
Ok(()) 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. `Engine::register_global_module` registers a shared [module] into the _global_ namespace.
All [functions] and [type iterators] can be accessed without _namespace qualifiers_. All [functions] and [type iterators] can be accessed without _namespace qualifiers_.
Variables and sub-modules are **ignored**. Variables and sub-modules are **ignored**.
This is by far the easiest way to expose a module's functionalities to Rhai. 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 let mut module = Module::new(); // new module
// Use the 'set_fn_XXX' API to add functions. // Use the 'Module::set_fn_XXX' API to add functions.
let hash = module.set_fn_1("inc", |x: i64| Ok(x+1)); let hash = module.set_fn_1("inc", |x: i64| Ok(x + 1));
// Remember to update the parameter names/types and return type metadata. // 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"]); module.update_fn_metadata(hash, ["x: i64", "i64"]);
// Register the module into the global namespace of the Engine. // 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 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 Use Case 2 - Make the `Module` a Static Module
--------------------------------------------- ---------------------------------------------
@ -60,25 +72,27 @@ use rhai::{Engine, Module};
let mut module = Module::new(); // new module let mut module = Module::new(); // new module
// Use the 'set_fn_XXX' API to add functions. // Use the 'Module::set_fn_XXX' API to add functions.
let hash = module.set_fn_1("inc", |x: i64| Ok(x+1)); let hash = module.set_fn_1("inc", |x: i64| Ok(x + 1));
// Remember to update the parameter names/types and return type metadata. // 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"]); 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(); 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 ### Expose Functions to the Global Namespace
`Module::set_fn_mut` and `Module::set_fn_XXX_mut` can optionally expose functions (usually _methods_) The `Module::set_fn_XXX_mut` API methods can optionally expose functions in the [module]
in the module to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] to the _global_ namespace by setting the `namespace` parameter to `FnNamespace::Global`,
can work as expected. 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. [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 let mut module = Module::new(); // new module
// Expose method 'inc' to the global namespace (default is 'Internal') // 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)); 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. // 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"]); module.update_fn_metadata(hash, ["x: &mut i64", "i64"]);
// Register the module into the Engine as a static module namespace 'calc' // Register the module into the Engine as a static module namespace 'calc'
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_static_module("calc", module.into()); 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; 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 let mut module = Module::new(); // new module
module.set_var("answer", 41_i64); // variable 'answer' under 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 // Create the module resolver
let mut resolver = StaticModuleResolver::new(); let mut resolver = StaticModuleResolver::new();
@ -129,7 +148,7 @@ resolver.insert("question", module);
// Set the module resolver into the 'Engine' // Set the module resolver into the 'Engine'
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_module_resolver(Some(resolver)); engine.set_module_resolver(resolver);
// Use namespace-qualified variables // Use namespace-qualified variables
engine.eval::<i64>(r#"import "question" as q; q::answer + 1"#)? == 42; 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(); let mut engine = Engine::new();
// Set the custom module resolver into the 'Engine'. // Set the custom module resolver into the 'Engine'.
engine.set_module_resolver(Some(MyModuleResolver {})); engine.set_module_resolver(MyModuleResolver {});
engine.consume(r#" engine.consume(r#"
import "hello" as foo; // this 'import' statement will call 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. 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` Set into `Engine`
----------------- -----------------
An [`Engine`]'s module resolver is set via a call to `Engine::set_module_resolver`: An [`Engine`]'s module resolver is set via a call to `Engine::set_module_resolver`:
```rust ```rust
use rhai::module_resolvers::StaticModuleResolver; use rhai::module_resolvers::{DummyModuleResolver, StaticModuleResolver};
// Create a module resolver // Create a module resolver
let resolver = StaticModuleResolver::new(); let resolver = StaticModuleResolver::new();
@ -167,8 +173,9 @@ let resolver = StaticModuleResolver::new();
// Register functions into 'resolver'... // Register functions into 'resolver'...
// Use the module 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' // Effectively disable 'import' statements by setting module resolver to
engine.set_module_resolver(None); // 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)) 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`]`>>` /// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an
/// or [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` depending on the `sync` feature. /// [`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 /// Shared [`Dynamic`] values are relatively cheap to clone as they simply increment the
/// reference counts. /// 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. /// 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 not a shared value, it returns itself.
/// ///
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are /// If the [`Dynamic`] is a shared value, it returns the shared value if there are no
/// no outstanding references, or a cloned copy. /// outstanding references, or a cloned copy.
#[inline(always)] #[inline(always)]
pub fn flatten(self) -> Self { pub fn flatten(self) -> Self {
match self.0 { match self.0 {

View File

@ -18,7 +18,7 @@ use crate::stdlib::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fmt, format, fmt, format,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
iter::{empty, once}, iter::{empty, once, FromIterator},
num::NonZeroU64, num::NonZeroU64,
num::NonZeroUsize, num::NonZeroUsize,
ops::DerefMut, 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(not(feature = "unchecked"))]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 8; 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. /// A collection of all modules loaded into the global namespace of the Engine.
pub(crate) global_modules: StaticVec<Shared<Module>>, pub(crate) global_modules: StaticVec<Shared<Module>>,
/// A collection of all sub-modules directly loaded into the Engine. /// 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. /// A module resolution service.
#[cfg(not(feature = "no_module"))] #[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. /// A hashmap mapping type names to pretty-print names.
pub(crate) type_names: HashMap<String, String>, pub(crate) type_names: HashMap<String, String>,
@ -745,7 +761,7 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))] #[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(not(feature = "no_module"))]
#[cfg(any(feature = "no_std", target_arch = "wasm32",))] #[cfg(any(feature = "no_std", target_arch = "wasm32",))]
module_resolver: None, module_resolver: None,
@ -808,7 +824,7 @@ impl Engine {
global_sub_modules: Default::default(), global_sub_modules: Default::default(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
module_resolver: None, module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
type_names: Default::default(), type_names: Default::default(),
disabled_symbols: Default::default(), disabled_symbols: Default::default(),
@ -849,15 +865,15 @@ impl Engine {
/// Search for a variable within the scope or within imports, /// Search for a variable within the scope or within imports,
/// depending on whether the variable name is namespace-qualified. /// depending on whether the variable name is namespace-qualified.
pub(crate) fn search_namespace<'s, 'a>( pub(crate) fn search_namespace<'s>(
&self, &self,
scope: &'s mut Scope, scope: &'s mut Scope,
mods: &mut Imports, mods: &mut Imports,
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
this_ptr: &'s mut Option<&mut Dynamic>, this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr, expr: &Expr,
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> { ) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
match expr { match expr {
Expr::Variable(v) => match v.as_ref() { Expr::Variable(v) => match v.as_ref() {
// Qualified variable // Qualified variable
@ -876,7 +892,7 @@ impl Engine {
// Module variables are constant // Module variables are constant
let mut target = target.clone(); let mut target = target.clone();
target.set_access_mode(AccessMode::ReadOnly); target.set_access_mode(AccessMode::ReadOnly);
Ok((target.into(), name, *pos)) Ok((target.into(), *pos))
} }
// Normal variable access // Normal variable access
_ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), _ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
@ -886,15 +902,15 @@ impl Engine {
} }
/// Search for a variable within the scope /// Search for a variable within the scope
pub(crate) fn search_scope_only<'s, 'a>( pub(crate) fn search_scope_only<'s>(
&self, &self,
scope: &'s mut Scope, scope: &'s mut Scope,
mods: &mut Imports, mods: &mut Imports,
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
this_ptr: &'s mut Option<&mut Dynamic>, this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr, expr: &Expr,
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> { ) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
let (index, _, Ident { name, pos }) = match expr { let (index, _, Ident { name, pos }) = match expr {
Expr::Variable(v) => v.as_ref(), Expr::Variable(v) => v.as_ref(),
_ => unreachable!(), _ => unreachable!(),
@ -903,7 +919,7 @@ impl Engine {
// Check if the variable is `this` // Check if the variable is `this`
if name.as_str() == KEYWORD_THIS { if name.as_str() == KEYWORD_THIS {
if let Some(val) = this_ptr { if let Some(val) = this_ptr {
return Ok(((*val).into(), KEYWORD_THIS, *pos)); return Ok(((*val).into(), *pos));
} else { } else {
return EvalAltResult::ErrorUnboundThis(*pos).into(); return EvalAltResult::ErrorUnboundThis(*pos).into();
} }
@ -931,7 +947,7 @@ impl Engine {
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))? resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
{ {
result.set_access_mode(AccessMode::ReadOnly); result.set_access_mode(AccessMode::ReadOnly);
return Ok((result.into(), name, *pos)); return Ok((result.into(), *pos));
} }
} }
@ -945,7 +961,7 @@ impl Engine {
.0 .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 // 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. // when the object variable is also used as a parameter.
@ -953,7 +969,7 @@ impl Engine {
// return EvalAltResult::ErrorDataRace(name.into(), *pos).into(); // return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
// } // }
Ok((val.into(), name, *pos)) Ok((val.into(), *pos))
} }
/// Chain-evaluate a dot/index chain. /// Chain-evaluate a dot/index chain.
@ -1300,7 +1316,7 @@ impl Engine {
self.inc_operations(state, *var_pos)?; self.inc_operations(state, *var_pos)?;
let (target, _, pos) = let (target, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?; self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
// Constants cannot be modified // Constants cannot be modified
@ -1594,7 +1610,7 @@ impl Engine {
match expr { match expr {
// var - point directly to the value // var - point directly to the value
Expr::Variable(_) => { Expr::Variable(_) => {
let (mut target, _, pos) = let (mut target, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?; self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
// If necessary, constants are cloned // If necessary, constants are cloned
@ -1720,7 +1736,7 @@ impl Engine {
} }
} }
Expr::Variable(_) => { 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()) Ok(val.take_or_clone())
} }
Expr::Property(_) => unreachable!(), Expr::Property(_) => unreachable!(),
@ -1926,11 +1942,15 @@ impl Engine {
let mut rhs_val = self let mut rhs_val = self
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
.flatten(); .flatten();
let (mut lhs_ptr, name, pos) = let (mut lhs_ptr, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?; self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
if !lhs_ptr.is_ref() { 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)?; self.inc_operations(state, pos)?;
@ -1938,7 +1958,7 @@ impl Engine {
if lhs_ptr.as_ref().is_read_only() { if lhs_ptr.as_ref().is_read_only() {
// Assignment to constant variable // Assignment to constant variable
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
name.to_string(), lhs_expr.get_variable_access(false).unwrap().to_string(),
pos, pos,
))) )))
} else if op.is_empty() { } else if op.is_empty() {
@ -2194,7 +2214,7 @@ impl Engine {
state.scope_level += 1; state.scope_level += 1;
for iter_value in func(iter_obj) { 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(); let value = iter_value.flatten();
if cfg!(not(feature = "no_closure")) && loop_var.is_shared() { 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)? .eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
.try_cast::<ImmutableString>() .try_cast::<ImmutableString>()
{ {
if let Some(resolver) = &self.module_resolver { let module = self.module_resolver.resolve(self, &path, expr.position())?;
let module = resolver.resolve(self, &path, expr.position())?;
if let Some(name_def) = alias { if let Some(name_def) = alias {
if !module.is_indexed() { if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not 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); let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index(); module.build_index();
mods.push(name_def.name.clone(), module); mods.push(name_def.name.clone(), module);
} else { } else {
mods.push(name_def.name.clone(), module); mods.push(name_def.name.clone(), module);
}
// When imports list is modified, clear the functions lookup cache
state.functions_cache.clear();
} }
// When imports list is modified, clear the functions lookup cache
state.modules += 1; state.functions_cache.clear();
Ok(Dynamic::UNIT)
} else {
Err(
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
.into(),
)
} }
state.modules += 1;
Ok(Dynamic::UNIT)
} else { } else {
Err(self.make_type_mismatch_err::<ImmutableString>("", expr.position())) Err(self.make_type_mismatch_err::<ImmutableString>("", expr.position()))
} }
@ -2410,7 +2423,7 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => { Stmt::Share(x) => {
if let Some((index, _)) = scope.get_index(&x.name) { 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() { if !val.is_shared() {
// Replace the variable with a shared value. // 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>( pub fn register_get<T: Variant + Clone, U: Variant + Clone>(
&mut self, &mut self,
name: &str, name: &str,
callback: impl Fn(&mut T) -> U + SendSync + 'static, get_fn: impl Fn(&mut T) -> U + SendSync + 'static,
) -> &mut Self { ) -> &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`]. /// 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>( pub fn register_get_result<T: Variant + Clone>(
&mut self, &mut self,
name: &str, 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 { ) -> &mut Self {
crate::RegisterResultFn::register_result_fn( use crate::{engine::make_getter, RegisterResultFn};
self, self.register_result_fn(&make_getter(name), get_fn)
&crate::engine::make_getter(name),
callback,
)
} }
/// Register a setter function for a member of a registered type with the [`Engine`]. /// 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>( pub fn register_set<T: Variant + Clone, U: Variant + Clone>(
&mut self, &mut self,
name: &str, name: &str,
callback: impl Fn(&mut T, U) + SendSync + 'static, set_fn: impl Fn(&mut T, U) + SendSync + 'static,
) -> &mut Self { ) -> &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`]. /// 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>( pub fn register_set_result<T: Variant + Clone, U: Variant + Clone>(
&mut self, &mut self,
name: &str, 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 { ) -> &mut Self {
crate::RegisterResultFn::register_result_fn( use crate::{engine::make_setter, RegisterResultFn};
self, self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| {
&crate::engine::make_setter(name), set_fn(obj, value).map(Into::into)
move |obj: &mut T, value: U| callback(obj, value).map(Into::into), })
)
} }
/// Short-hand for registering both getter and setter functions /// Short-hand for registering both getter and setter functions
/// of a registered type with the [`Engine`]. /// of a registered type with the [`Engine`].
@ -453,7 +451,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>( pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
&mut self, &mut self,
callback: impl Fn(&mut T, X) -> U + SendSync + 'static, get_fn: impl Fn(&mut T, X) -> U + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
@ -469,7 +467,8 @@ impl Engine {
panic!("Cannot register indexer for strings."); 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`]. /// Register an index getter for a custom type with the [`Engine`].
/// ///
@ -518,7 +517,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn register_indexer_get_result<T: Variant + Clone, X: Variant + Clone>( pub fn register_indexer_get_result<T: Variant + Clone, X: Variant + Clone>(
&mut self, &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 { ) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
@ -534,7 +533,8 @@ impl Engine {
panic!("Cannot register indexer for strings."); 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`]. /// Register an index setter for a custom type with the [`Engine`].
/// ///
@ -581,7 +581,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>( pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
&mut self, &mut self,
callback: impl Fn(&mut T, X, U) + SendSync + 'static, set_fn: impl Fn(&mut T, X, U) + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
@ -597,7 +597,8 @@ impl Engine {
panic!("Cannot register indexer for strings."); 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`]. /// Register an index setter for a custom type with the [`Engine`].
/// ///
@ -651,7 +652,7 @@ impl Engine {
U: Variant + Clone, U: Variant + Clone,
>( >(
&mut self, &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 { ) -> &mut Self {
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
@ -667,11 +668,10 @@ impl Engine {
panic!("Cannot register indexer for strings."); panic!("Cannot register indexer for strings.");
} }
crate::RegisterResultFn::register_result_fn( use crate::{engine::FN_IDX_SET, RegisterResultFn};
self, self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| {
crate::engine::FN_IDX_SET, set_fn(obj, index, value).map(Into::into)
move |obj: &mut T, index: X, value: U| callback(obj, index, value).map(Into::into), })
)
} }
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`]. /// 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>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::{Engine, Module}; /// use rhai::{Engine, Shared, Module};
/// ///
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// ///
@ -763,9 +763,18 @@ impl Engine {
/// let mut module = Module::new(); /// let mut module = Module::new();
/// module.set_fn_1("calc", |x: i64| Ok(x + 1)); /// module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// ///
/// // Register the module as a fixed sub-module /// let module: Shared<Module> = module.into();
/// engine.register_static_module("CalcService", 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); /// assert_eq!(engine.eval::<i64>("CalcService::calc(41)")?, 42);
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -773,19 +782,49 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub fn register_static_module( pub fn register_static_module(
&mut self, &mut self,
name: impl Into<crate::ImmutableString>, name: impl AsRef<str>,
module: Shared<Module>, module: Shared<Module>,
) -> &mut Self { ) -> &mut Self {
if !module.is_indexed() { fn register_static_module_raw(
// Index the module (making a clone copy if necessary) if it is not indexed root: &mut crate::stdlib::collections::HashMap<crate::ImmutableString, Shared<Module>>,
let mut module = crate::fn_native::shared_take_or_clone(module); name: impl AsRef<str>,
module.build_index(); module: Shared<Module>,
self.global_sub_modules.push(name, module); ) {
} else { let separator = crate::token::Token::DoubleColon.syntax();
self.global_sub_modules.push(name, module);
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 self
} }
/// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`]. /// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`].
/// ///
/// ## Deprecated /// ## Deprecated
@ -796,7 +835,7 @@ impl Engine {
#[deprecated = "use `register_static_module` instead"] #[deprecated = "use `register_static_module` instead"]
pub fn register_module( pub fn register_module(
&mut self, &mut self,
name: impl Into<crate::ImmutableString>, name: impl AsRef<str>,
module: impl Into<Shared<Module>>, module: impl Into<Shared<Module>>,
) -> &mut Self { ) -> &mut Self {
self.register_static_module(name, module.into()) self.register_static_module(name, module.into())
@ -1407,7 +1446,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<T, Box<EvalAltResult>> { ) -> 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)?; let result = self.eval_ast_with_scope_raw(scope, mods, ast)?;
@ -1493,7 +1532,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
let mods = &mut self.global_sub_modules.clone(); let mods = &mut (&self.global_sub_modules).into();
let state = &mut State { let state = &mut State {
source: ast.clone_source(), source: ast.clone_source(),
..Default::default() ..Default::default()
@ -1539,12 +1578,12 @@ impl Engine {
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn call_fn<A: crate::fn_args::FuncArgs, T: Variant + Clone>( pub fn call_fn<T: Variant + Clone>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
args: A, args: impl crate::fn_args::FuncArgs,
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
let mut arg_values = args.into_vec(); let mut arg_values = args.into_vec();
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect(); 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))?; .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
let mut state = Default::default(); 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. // Check for data race.
if cfg!(not(feature = "no_closure")) { if cfg!(not(feature = "no_closure")) {

View File

@ -168,9 +168,9 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn set_module_resolver( pub fn set_module_resolver(
&mut self, &mut self,
resolver: Option<impl crate::ModuleResolver + 'static>, resolver: impl crate::ModuleResolver + 'static,
) -> &mut Self { ) -> &mut Self {
self.module_resolver = resolver.map(|f| Box::new(f) as Box<dyn crate::ModuleResolver>); self.module_resolver = Box::new(resolver);
self self
} }
/// Disable a particular keyword or operator in the language. /// 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)) .map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
let (mut target, _, pos) = let (mut target, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?; self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
if target.as_ref().is_read_only() { if target.as_ref().is_read_only() {
@ -1137,7 +1137,7 @@ impl Engine {
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
// Get target reference to first argument // 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.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
self.inc_operations(state, pos)?; self.inc_operations(state, pos)?;

View File

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

View File

@ -3,7 +3,14 @@
use crate::ast::{FnAccess, ScriptFnDef}; use crate::ast::{FnAccess, ScriptFnDef};
use crate::engine::Imports; use crate::engine::Imports;
use crate::plugin::PluginFunction; 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::token::is_valid_identifier;
use crate::{ use crate::{
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, 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], args: &mut [&mut Dynamic],
def_value: Option<&Dynamic>, def_value: Option<&Dynamic>,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> 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() self.engine()
.exec_fn_call( .exec_fn_call(
&mut mods, &mut self.mods.cloned().unwrap_or_default(),
&mut Default::default(), &mut Default::default(),
self.lib, self.lib,
fn_name, fn_name,
hash_script, calc_script_fn_hash(empty(), fn_name, args.len() - if is_method { 1 } else { 0 }),
args, args,
is_method, is_method,
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 /// A general function pointer, which may carry additional (i.e. curried) argument values
/// to be passed onto a function during a call. /// to be passed onto a function during a call.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub struct FnPtr(ImmutableString, StaticVec<Dynamic>); pub struct FnPtr(ImmutableString, StaticVec<Dynamic>);
impl FnPtr { impl FnPtr {
/// Create a new function pointer. /// 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)] #[inline(always)]
pub(crate) fn new_unchecked( pub(crate) fn new_unchecked(
name: impl Into<ImmutableString>, name: impl Into<ImmutableString>,
@ -257,7 +256,24 @@ impl FnPtr {
pub fn curry(&self) -> &[Dynamic] { pub fn curry(&self) -> &[Dynamic] {
self.1.as_ref() 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"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn is_anonymous(&self) -> bool { pub fn is_anonymous(&self) -> bool {

View File

@ -35,9 +35,9 @@ use crate::Map;
/// A type representing the namespace of a function. /// A type representing the namespace of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnNamespace { pub enum FnNamespace {
/// Global namespace. /// Expose to global namespace.
Global, Global,
/// Internal only. /// Module namespace only.
Internal, Internal,
} }
@ -465,6 +465,16 @@ impl Module {
.map(|FuncInfo { func, .. }| func.get_fn_def()) .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? /// Does a sub-module exist in the module?
/// ///
/// # Example /// # Example
@ -1699,7 +1709,7 @@ impl Module {
ast: &crate::AST, ast: &crate::AST,
engine: &crate::Engine, engine: &crate::Engine,
) -> Result<Self, Box<EvalAltResult>> { ) -> 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(); let orig_mods_len = mods.len();
// Run the script // Run the script

View File

@ -16,7 +16,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
/// collection.push(resolver); /// collection.push(resolver);
/// ///
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(collection)); /// engine.set_module_resolver(collection);
/// ``` /// ```
#[derive(Default)] #[derive(Default)]
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>); pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
@ -36,16 +36,41 @@ impl ModuleResolversCollection {
/// collection.push(resolver); /// collection.push(resolver);
/// ///
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(collection)); /// engine.set_module_resolver(collection);
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
/// Add a module keyed by its path. /// Append a module resolver to the end.
#[inline(always)] #[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.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. /// Get an iterator of all the module resolvers.
#[inline(always)] #[inline(always)]
@ -59,8 +84,9 @@ impl ModuleResolversCollection {
} }
/// Remove all module resolvers. /// Remove all module resolvers.
#[inline(always)] #[inline(always)]
pub fn clear(&mut self) { pub fn clear(&mut self) -> &mut Self {
self.0.clear(); self.0.clear();
self
} }
/// Is this [`ModuleResolversCollection`] empty? /// Is this [`ModuleResolversCollection`] empty?
#[inline(always)] #[inline(always)]
@ -75,8 +101,9 @@ impl ModuleResolversCollection {
/// Add another [`ModuleResolversCollection`] to the end of this collection. /// Add another [`ModuleResolversCollection`] to the end of this collection.
/// The other [`ModuleResolversCollection`] is consumed. /// The other [`ModuleResolversCollection`] is consumed.
#[inline(always)] #[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.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::{ 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}; 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(); /// let mut engine = Engine::new();
/// ///
/// engine.set_module_resolver(Some(resolver)); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct FileModuleResolver { pub struct FileModuleResolver {
@ -65,10 +69,10 @@ impl FileModuleResolver {
/// let resolver = FileModuleResolver::new_with_path("./scripts"); /// let resolver = FileModuleResolver::new_with_path("./scripts");
/// ///
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver)); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[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") 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 resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
/// ///
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver)); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>( pub fn new_with_path_and_extension(
path: P, path: impl Into<PathBuf>,
extension: E, extension: impl Into<String>,
) -> Self { ) -> Self {
Self { Self {
path: path.into(), path: path.into(),
@ -114,12 +118,64 @@ impl FileModuleResolver {
/// let resolver = FileModuleResolver::new(); /// let resolver = FileModuleResolver::new();
/// ///
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver)); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn new() -> Self { pub fn new() -> Self {
Default::default() 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 { impl ModuleResolver for FileModuleResolver {

View File

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

View File

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

View File

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

View File

@ -91,13 +91,16 @@ pub enum ParseErrorType {
UnknownOperator(String), UnknownOperator(String),
/// Expecting a particular token but not finding one. Wrapped values are the token and description. /// Expecting a particular token but not finding one. Wrapped values are the token and description.
MissingToken(String, String), 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), 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. /// Never appears under the `no_index` feature.
MalformedIndexExpr(String), 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. /// Never appears under the `no_object` and `no_index` features combination.
MalformedInExpr(String), MalformedInExpr(String),
@ -137,7 +140,8 @@ pub enum ParseErrorType {
/// ///
/// Never appears under the `no_function` feature. /// Never appears under the `no_function` feature.
FnMissingParams(String), 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. /// Never appears under the `no_function` feature.
FnDuplicatedParam(String, String), FnDuplicatedParam(String, String),

View File

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

View File

@ -13,8 +13,8 @@ pub use rhai_codegen::*;
pub use rhai_codegen::{export_fn, register_exported_fn}; pub use rhai_codegen::{export_fn, register_exported_fn};
/// Trait implemented by a _plugin function_. /// 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. /// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
pub trait PluginFunction { pub trait PluginFunction {
/// Call the plugin function with the arguments provided. /// Call the plugin function with the arguments provided.

View File

@ -55,8 +55,8 @@ pub enum EvalAltResult {
/// String indexing out-of-bounds. /// String indexing out-of-bounds.
/// Wrapped values are the current number of characters in the string and the index number. /// Wrapped values are the current number of characters in the string and the index number.
ErrorStringBounds(usize, INT, Position), 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. /// Trying to index into a type that is not an array, an object map, or a string, and has no
/// Wrapped value is the type name. /// indexer function defined. Wrapped value is the type name.
ErrorIndexingType(String, Position), ErrorIndexingType(String, Position),
/// Invalid arguments for `in` operator. /// Invalid arguments for `in` operator.
ErrorInExpr(Position), 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.) // [`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, // 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 // 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. // 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)] #[derive(Debug, Clone, Hash)]
pub struct Scope<'a> { pub struct Scope<'a> {
/// Current value of the entry. /// Current value of the entry.
@ -356,8 +358,35 @@ impl<'a> Scope<'a> {
self self
} }
/// Get a mutable reference to an entry in the [`Scope`]. /// 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)] #[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") self.values.get_mut(index).expect("invalid index in Scope")
} }
/// Update the access type of an entry in the [`Scope`]. /// Update the access type of an entry in the [`Scope`].

View File

@ -63,7 +63,8 @@ pub fn get_hasher() -> impl Hasher {
s 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. /// Exported under the `internals` feature only.
/// ///
/// Module names are passed in via `&str` references from an iterator. /// Module names are passed in via `&str` references from an iterator.
@ -108,6 +109,7 @@ pub fn calc_script_fn_hash<'a>(
/// # Note /// # Note
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline(always)]
fn calc_fn_hash<'a>( fn calc_fn_hash<'a>(
mut modules: impl Iterator<Item = &'a str>, mut modules: impl Iterator<Item = &'a str>,
fn_name: &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 }")?; let ast = engine.compile("private fn add(x, n, ) { x + n }")?;
assert!(matches!( 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"), .expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "add" 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 f = move |p1: TestStruct, p2: TestStruct| -> Result<(), Box<EvalAltResult>> {
let action_ptr = res["action"].clone().cast::<FnPtr>(); let action_ptr = res["action"].clone().cast::<FnPtr>();
let name = action_ptr.fn_name(); 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 // Test closure

View File

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