diff --git a/Cargo.toml b/Cargo.toml index db72ea25..0c47eb7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ] [dependencies] smallvec = { version = "1.4.2", default-features = false } -rhai_codegen = { version = "0.2", path = "codegen" } +rhai_codegen = { version = "0.3", path = "codegen" } [features] default = [] diff --git a/RELEASES.md b/RELEASES.md index 7d904b64..7f19875a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,11 +6,16 @@ Version 0.19.6 This version adds the `switch` statement. +It also allows exposing selected module functions (usually methods) to the global namespace. + New features ------------ * `switch` statement. -* `Engine::register_module` to register a module as a sub-module in the global namespace, while at the same time exposing its method functions globally. This is convenient when registering an API for a custom type. +* `Engine::register_module` to register a module as a sub-module in the global namespace. +* `Module::get_fn_namespace` and `Module::set_fn_namespace` can expose a module function to the global namespace. This is convenient when registering an API for a custom type. +* `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace. +* `#[rhai_fn(gobal)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type. Enhancements ------------ diff --git a/benches/eval_module.rs b/benches/eval_module.rs index 350877fe..de74ab24 100644 --- a/benches/eval_module.rs +++ b/benches/eval_module.rs @@ -3,7 +3,7 @@ ///! Test evaluating with scope extern crate test; -use rhai::{module_resolvers::StaticModuleResolver, Engine, Module, OptimizationLevel}; +use rhai::{Engine, Module, OptimizationLevel}; use test::Bencher; #[bench] @@ -20,9 +20,7 @@ fn bench_eval_module(bench: &mut Bencher) { let module = Module::eval_ast_as_new(Default::default(), &ast, &engine).unwrap(); - let mut resolver = StaticModuleResolver::new(); - resolver.insert("testing", module); - engine.set_module_resolver(Some(resolver)); + engine.register_module("testing", module); let ast = engine .compile( diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 377b8f91..8cd109f3 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "0.2.0" +version = "0.3.0" edition = "2018" authors = ["jhwgh1968"] description = "Procedural macro support package for Rhai, a scripting language for Rust" diff --git a/codegen/src/function.rs b/codegen/src/function.rs index c0d50588..64df0cd3 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -20,6 +20,12 @@ use syn::{ use crate::attrs::{ExportInfo, ExportScope, ExportedParams}; +#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash)] +pub enum FnNamespaceAccess { + Global, + Internal, +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum Index { Get, @@ -82,8 +88,9 @@ pub(crate) struct ExportedFnParams { pub name: Option>, pub return_raw: bool, pub skip: bool, - pub span: Option, pub special: FnSpecialAccess, + pub namespace: Option, + pub span: Option, } pub const FN_GET: &str = "get$"; @@ -119,6 +126,7 @@ impl ExportedParams for ExportedFnParams { let mut name = Vec::new(); let mut return_raw = false; let mut skip = false; + let mut namespace = None; let mut special = FnSpecialAccess::None; for attr in attrs { let crate::attrs::AttrItem { @@ -194,12 +202,30 @@ impl ExportedParams for ExportedFnParams { } } } - ("return_raw", None) => return_raw = true, ("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => { return Err(syn::Error::new(s.span(), "extraneous value")) } + ("return_raw", None) => return_raw = true, + ("return_raw", Some(s)) => { + return Err(syn::Error::new(s.span(), "extraneous value")) + } ("skip", None) => skip = true, ("skip", Some(s)) => return Err(syn::Error::new(s.span(), "extraneous value")), + ("global", Some(s)) | ("internal", Some(s)) => { + return Err(syn::Error::new(s.span(), "extraneous value")) + } + ("global", None) => { + if namespace.is_some() { + return Err(syn::Error::new(key.span(), "conflicting namespace")); + } + namespace = Some(FnNamespaceAccess::Global); + } + ("internal", None) => { + if namespace.is_some() { + return Err(syn::Error::new(key.span(), "conflicting namespace")); + } + namespace = Some(FnNamespaceAccess::Internal); + } (attr, _) => { return Err(syn::Error::new( key.span(), @@ -214,6 +240,7 @@ impl ExportedParams for ExportedFnParams { return_raw, skip, special, + namespace, span: Some(span), ..Default::default() }) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index f577c1df..065a9461 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -339,7 +339,48 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream }; let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let tokens = quote! { - #module_expr.set_fn(#export_name, FnAccess::Public, + #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, + #gen_mod_path::token_input_types().as_ref(), + #gen_mod_path::token_callable()); + }; + proc_macro::TokenStream::from(tokens) +} + +/// Macro to register a _plugin function_ into a Rhai `Module` and expose it globally. +/// +/// # Usage +/// +/// ``` +/// # use rhai::{Engine, EvalAltResult}; +/// use rhai::plugin::*; +/// +/// #[export_fn] +/// fn my_plugin_function(x: i64) -> i64 { +/// x * 2 +/// } +/// +/// # fn main() -> Result<(), Box> { +/// let mut engine = Engine::new(); +/// +/// let mut module = Module::new(); +/// set_exported_global_fn!(module, "func", my_plugin_function); +/// +/// engine.register_module("test", module); +/// +/// assert_eq!(engine.eval::("func(21)")?, 42); +/// # Ok(()) +/// # } +/// ``` +#[proc_macro] +pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { + let (module_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) + { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), + }; + let gen_mod_path = crate::register::generated_module_path(&rust_modpath); + let tokens = quote! { + #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, #gen_mod_path::token_input_types().as_ref(), #gen_mod_path::token_callable()); }; diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index b7d160bc..3981e3b9 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -4,7 +4,7 @@ use quote::{quote, ToTokens}; use crate::attrs::ExportScope; use crate::function::flatten_type_groups; -use crate::function::{ExportedFn, FnSpecialAccess}; +use crate::function::{ExportedFn, FnNamespaceAccess, FnSpecialAccess}; use crate::module::Module; pub(crate) type ExportedConst = (String, Box, syn::Expr); @@ -80,6 +80,7 @@ pub(crate) fn generate_body( function.name().span(), ); let reg_names = function.exported_names(); + let mut namespace = FnNamespaceAccess::Internal; let fn_input_types: Vec = function .arg_list() @@ -123,12 +124,22 @@ pub(crate) fn generate_body( }) .collect(); + if let Some(ns) = function.params().namespace { + namespace = ns; + } + for fn_literal in reg_names { set_fn_stmts.push( - syn::parse2::(quote! { - m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*], - #fn_token_name().into()); - }) + match namespace { + FnNamespaceAccess::Global => syn::parse2::(quote! { + m.set_fn(#fn_literal, FnNamespace::Global, FnAccess::Public, &[#(#fn_input_types),*], + #fn_token_name().into()); + }), + FnNamespaceAccess::Internal => syn::parse2::(quote! { + m.set_fn(#fn_literal, FnNamespace::Internal, FnAccess::Public, &[#(#fn_input_types),*], + #fn_token_name().into()); + }), + } .unwrap(), ); } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 554d1901..56d3290c 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -295,7 +295,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get_mystic_number", FnAccess::Public, &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -330,6 +330,68 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_single_arg_global_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_global_fn { + #[rhai_fn(global)] + pub fn add_one_to(x: INT) -> INT { + x + 1 + } + } + }; + + let expected_tokens = quote! { + pub mod one_global_fn { + pub fn add_one_to(x: INT) -> INT { + x + 1 + } + #[allow(unused_imports)] + use super::*; + + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + rhai_generate_into_module(&mut m, false); + m + } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_fn("add_one_to", FnNamespace::Global, FnAccess::Public, &[core::any::TypeId::of::()], + add_one_to_token().into()); + if flatten {} else {} + } + #[allow(non_camel_case_types)] + struct add_one_to_token(); + impl PluginFunction for add_one_to_token { + fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result> { + debug_assert_eq!(args.len(), 1usize, + "wrong arg count: {} != {}", args.len(), 1usize); + let arg0 = mem::take(args[0usize]).cast::(); + Ok(Dynamic::from(add_one_to(arg0))) + } + + fn is_method_call(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(add_one_to_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::()].into_boxed_slice() + } + } + pub fn add_one_to_token_callable() -> CallableFunction { + add_one_to_token().into() + } + pub fn add_one_to_token_input_types() -> Box<[TypeId]> { + add_one_to_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_single_arg_fn_module() { let input_tokens: TokenStream = quote! { @@ -355,7 +417,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_one_to", FnAccess::Public, &[core::any::TypeId::of::()], + m.set_fn("add_one_to", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], add_one_to_token().into()); if flatten {} else {} } @@ -427,10 +489,10 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::()], + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], add_one_to_token().into()); - m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_n_to_token().into()); if flatten {} else {} } @@ -519,8 +581,8 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} } @@ -584,14 +646,14 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("+", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("+", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} } @@ -831,7 +893,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get_mystic_number", FnAccess::Public, &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -921,7 +983,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("print_out_to", FnAccess::Public, + m.set_fn("print_out_to", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} @@ -983,7 +1045,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("print_out_to", FnAccess::Public, + m.set_fn("print_out_to", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} @@ -1045,7 +1107,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("increment", FnAccess::Public, + m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} @@ -1110,7 +1172,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("increment", FnAccess::Public, + m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} @@ -1195,7 +1257,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("increment", FnAccess::Public, + m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} @@ -1279,7 +1341,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1341,9 +1403,9 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("square", FnAccess::Public, &[core::any::TypeId::of::()], + m.set_fn("square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], int_foo_token().into()); - m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1405,7 +1467,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set$squared", FnAccess::Public, + m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); @@ -1470,11 +1532,11 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set_sq", FnAccess::Public, + m.set_fn("set_sq", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); - m.set_fn("set$squared", FnAccess::Public, + m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); @@ -1539,7 +1601,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("index$get$", FnAccess::Public, + m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1605,11 +1667,11 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get", FnAccess::Public, + m.set_fn("get", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); - m.set_fn("index$get$", FnAccess::Public, + m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1675,7 +1737,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("index$set$", FnAccess::Public, + m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1744,12 +1806,12 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set", FnAccess::Public, + m.set_fn("set", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], set_by_index_token().into()); - m.set_fn("index$set$", FnAccess::Public, + m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], diff --git a/codegen/ui_tests/rhai_fn_global_multiple.rs b/codegen/ui_tests/rhai_fn_global_multiple.rs new file mode 100644 index 00000000..45e2e325 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_global_multiple.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(global, global)] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0 + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_global_multiple.stderr b/codegen/ui_tests/rhai_fn_global_multiple.stderr new file mode 100644 index 00000000..f66ebbb7 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_global_multiple.stderr @@ -0,0 +1,11 @@ +error: conflicting namespace + --> $DIR/rhai_fn_global_multiple.rs:12:23 + | +12 | #[rhai_fn(global, global)] + | ^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_global_multiple.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/doc/src/language/for.md b/doc/src/language/for.md index 8fa560d2..4985b7aa 100644 --- a/doc/src/language/for.md +++ b/doc/src/language/for.md @@ -3,7 +3,7 @@ {{#include ../links.md}} -Iterating through a range or an [array], or any type with a registered _iterator_, +Iterating through a range or an [array], or any type with a registered _type iterator_, is provided by the `for` ... `in` loop. Like C, `continue` can be used to skip to the next iteration, by-passing all following statements; diff --git a/doc/src/language/modules/index.md b/doc/src/language/modules/index.md index 9df73faf..14931c3a 100644 --- a/doc/src/language/modules/index.md +++ b/doc/src/language/modules/index.md @@ -6,7 +6,7 @@ Modules Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_. Modules can be disabled via the [`no_module`] feature. -A module is of the type `Module` and holds a collection of functions, variables, iterators and sub-modules. +A module is of the type `Module` and holds a collection of functions, variables, type iterators and sub-modules. It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions and variables defined by that script. diff --git a/doc/src/language/try-catch.md b/doc/src/language/try-catch.md index 5c3b9e1f..c28236c5 100644 --- a/doc/src/language/try-catch.md +++ b/doc/src/language/try-catch.md @@ -91,7 +91,7 @@ Many script-oriented exceptions can be caught via `try` ... `catch`: | [Array]/[string] indexing out-of-bounds | error message [string] | | Indexing with an inappropriate data type | error message [string] | | Error in a dot expression | error message [string] | -| `for` statement without an iterator | error message [string] | +| `for` statement without an type iterator | error message [string] | | Error in an `in` expression | error message [string] | | Data race detected | error message [string] | diff --git a/doc/src/patterns/enums.md b/doc/src/patterns/enums.md index a33c0303..86f3504e 100644 --- a/doc/src/patterns/enums.md +++ b/doc/src/patterns/enums.md @@ -37,7 +37,7 @@ mod MyEnumModule { MyEnum::Baz(val1, val2) } // Access to fields - #[rhai_fn(get = "enum_type")] + #[rhai_fn(global, get = "enum_type")] pub fn get_type(a: &mut MyEnum) -> String { match a { MyEnum::Foo => "Foo".to_string(), @@ -45,7 +45,7 @@ mod MyEnumModule { MyEnum::Baz(_, _) => "Baz".to_string() } } - #[rhai_fn(get = "field_0")] + #[rhai_fn(global, get = "field_0")] pub fn get_field_0(a: &mut MyEnum) -> Dynamic { match a { MyEnum::Foo => Dynamic::UNIT, @@ -53,7 +53,7 @@ mod MyEnumModule { MyEnum::Baz(x, _) => Dynamic::from(x) } } - #[rhai_fn(get = "field_1")] + #[rhai_fn(global, get = "field_1")] pub fn get_field_1(a: &mut MyEnum) -> Dynamic { match a { MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT, @@ -61,41 +61,41 @@ mod MyEnumModule { } } // Printing - #[rhai(name = "to_string", name = "print", name = "debug")] + #[rhai(global, name = "to_string", name = "print", name = "debug")] pub fn to_string(a: &mut MyEnum) -> String { format!("{:?}", a)) } - #[rhai_fn(name = "+")] + #[rhai_fn(global, name = "+")] pub fn add_to_str(s: &str, a: MyEnum) -> String { format!("{}{:?}", s, a)) } - #[rhai_fn(name = "+")] + #[rhai_fn(global, name = "+")] pub fn add_str(a: &mut MyEnum, s: &str) -> String { format!("{:?}", a).push_str(s)) } - #[rhai_fn(name = "+=")] + #[rhai_fn(global, name = "+=")] pub fn append_to_str(s: &mut ImmutableString, a: MyEnum) -> String { s += a.to_string()) } // '==' and '!=' operators - #[rhai_fn(name = "==")] + #[rhai_fn(global, name = "==")] pub fn eq(a: &mut MyEnum, b: MyEnum) -> bool { a == &b } - #[rhai_fn(name = "!=")] + #[rhai_fn(global, name = "!=")] pub fn neq(a: &mut MyEnum, b: MyEnum) -> bool { a != &b } // Array functions - #[rhai_fn(name = "push")] + #[rhai_fn(global, name = "push")] pub fn append_to_array(list: &mut Array, item: MyEnum) { list.push(Dynamic::from(item))); } - #[rhai_fn(name = "+=")] + #[rhai_fn(global, name = "+=")] pub fn append_to_array_op(list: &mut Array, item: MyEnum) { list.push(Dynamic::from(item))); } - #[rhai_fn(name = "insert")] + #[rhai_fn(global, name = "insert")] pub fn insert_to_array(list: &mut Array, position: i64, item: MyEnum) { if position <= 0 { list.insert(0, Dynamic::from(item)); @@ -105,7 +105,7 @@ mod MyEnumModule { list.insert(position as usize, Dynamic::from(item)); } } - #[rhai_fn(name = "pad")] + #[rhai_fn(global, name = "pad")] pub fn pad_array(list: &mut Array, len: i64, item: MyEnum) { if len as usize > list.len() { list.resize(len as usize, item); } } diff --git a/doc/src/plugins/function.md b/doc/src/plugins/function.md index f539be19..6c47575f 100644 --- a/doc/src/plugins/function.md +++ b/doc/src/plugins/function.md @@ -11,11 +11,12 @@ individual functions instead of a full-blown [plugin module]. Macros ------ -| Macro | Signature | Description | -| ----------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------- | -| `#[export_fn]` | apply to rust function defined in a Rust module | exports the function | -| `register_exported_fn!` | `register_exported_fn!(&mut `_engine_`, "`_name_`", `_function_`)` | registers the function into an [`Engine`] under a specific name | -| `set_exported_fn!` | `set_exported_fn!(&mut `_module_`, "`_name_`", `_function_`)` | registers the function into a [`Module`] under a specific name | +| Macro | Signature | Description | +| ------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| `#[export_fn]` | apply to rust function defined in a Rust module | exports the function | +| `register_exported_fn!` | `register_exported_fn!(&mut `_engine_`, "`_name_`", `_function_`)` | registers the function into an [`Engine`] under a specific name | +| `set_exported_fn!` | `set_exported_fn!(&mut `_module_`, "`_name_`", `_function_`)` | registers the function into a [`Module`] under a specific name | +| `set_exported_global_fn!` | `set_exported_global_fn!(&mut `_module_`, "`_name_`", `_function_`)` | registers the function into a [`Module`] under a specific name, exposing it to the global namespace | `#[export_fn]` and `register_exported_fn!` diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 1bd676c5..ee55ab96 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -54,6 +54,8 @@ mod my_module { mystic_number() } // This function will be registered as 'increment'. + // It will also be exposed to the global namespace since 'global' is set. + #[rhai_fn(global)] pub fn increment(num: &mut i64) { *num += 1; } @@ -159,10 +161,13 @@ service::increment(x); x == 43; ``` -`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the -_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected. +All functions (usually _methods_) defined in the module and marked with `#[rhai_fn(global)]`, +as well as all _type iterators_, are automatically exposed to the _global_ namespace, so +[iteration]({{rootUrl}}/language/for.md), [getters/setters] and [indexers] for [custom types] +can work as expected. -Therefore, in the example able, `increment` works fine when called in method-call style: +Therefore, in the example above, the `increment` method (defined with `#[rhai_fn(global)]`) +works fine when called in method-call style: ```rust let x = 42; @@ -481,12 +486,14 @@ Inner attributes can be applied to the inner items of a module to tweak the expo Parameters should be set on inner attributes to specify the desired behavior. -| Attribute Parameter | Use with | Apply to | Description | -| ------------------- | --------------------------- | ----------------------------------------------------- | ------------------------------------------------------ | -| `skip` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | do not export this function/sub-module | -| `name = "..."` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | registers function/sub-module under the specified name | -| `get = "..."` | `#[rhai_fn]` | `pub fn (&mut Type) -> Value` | registers a getter for the named property | -| `set = "..."` | `#[rhai_fn]` | `pub fn (&mut Type, Value)` | registers a setter for the named property | -| `index_get` | `#[rhai_fn]` | `pub fn (&mut Type, INT) -> Value` | registers an index getter | -| `index_set` | `#[rhai_fn]` | `pub fn (&mut Type, INT, Value)` | registers an index setter | -| `return_raw` | `#[rhai_fn]` | `pub fn (...) -> Result>` | marks this as a [fallible function] | +| Attribute Parameter | Use with | Apply to | Description | +| ------------------- | --------------------------- | ----------------------------------------------------- | ------------------------------------------------------- | +| `skip` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | do not export this function/sub-module | +| `global` | `#[rhai_fn]` | function | expose this function to the global namespace | +| `internal` | `#[rhai_fn]` | function | keep this function within the internal module namespace | +| `name = "..."` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | registers function/sub-module under the specified name | +| `get = "..."` | `#[rhai_fn]` | `pub fn (&mut Type) -> Value` | registers a getter for the named property | +| `set = "..."` | `#[rhai_fn]` | `pub fn (&mut Type, Value)` | registers a setter for the named property | +| `index_get` | `#[rhai_fn]` | `pub fn (&mut Type, INT) -> Value` | registers an index getter | +| `index_set` | `#[rhai_fn]` | `pub fn (&mut Type, INT, Value)` | registers an index setter | +| `return_raw` | `#[rhai_fn]` | `pub fn (...) -> Result>` | marks this as a [fallible function] | diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index c47fa6bf..7f58b103 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -60,17 +60,22 @@ engine.register_module("calc", module); engine.eval::("calc::inc(41)")? == 42; // refer to the 'Calc' module ``` -`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the -_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected. +`Module::set_fn_namespace` can expose functions (usually _methods_) in the module +to the _global_ namespace, 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. ```rust -use rhai::{Engine, Module}; +use rhai::{Engine, Module, FnNamespace}; let mut module = Module::new(); // new module -module.set_fn_1_mut("inc", // add new method +let hash = module.set_fn_1_mut("inc", // add new method |x: &mut i64| Ok(x+1) ); +// Expose 'inc' to the global namespace (default is 'Internal') +module.set_fn_namespace(hash, FnNamespace::Global); + // Load the module into the Engine as a sub-module named 'calc' let mut engine = Engine::new(); engine.register_module("calc", module); diff --git a/doc/src/rust/modules/index.md b/doc/src/rust/modules/index.md index 9df73faf..14931c3a 100644 --- a/doc/src/rust/modules/index.md +++ b/doc/src/rust/modules/index.md @@ -6,7 +6,7 @@ Modules Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_. Modules can be disabled via the [`no_module`] feature. -A module is of the type `Module` and holds a collection of functions, variables, iterators and sub-modules. +A module is of the type `Module` and holds a collection of functions, variables, type iterators and sub-modules. It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions and variables defined by that script. diff --git a/src/ast.rs b/src/ast.rs index 70e1425f..575ccf53 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,23 +1,7 @@ //! Module defining the AST (abstract syntax tree). -use crate::dynamic::{Dynamic, Union}; -use crate::fn_native::{FnPtr, Shared}; -use crate::module::{Module, NamespaceRef}; -use crate::syntax::FnCustomSyntaxEval; -use crate::token::{Position, Token, NO_POS}; -use crate::utils::{ImmutableString, StraightHasherBuilder}; -use crate::StaticVec; -use crate::INT; - -#[cfg(not(feature = "no_float"))] -use crate::FLOAT; - -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - +use crate::dynamic::Union; +use crate::module::NamespaceRef; use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -30,14 +14,23 @@ use crate::stdlib::{ vec, vec::Vec, }; +use crate::syntax::FnCustomSyntaxEval; +use crate::token::Token; +use crate::utils::StraightHasherBuilder; +use crate::{ + Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT, NO_POS, +}; -#[cfg(not(feature = "no_closure"))] -use crate::stdlib::collections::HashSet; +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::stdlib::cmp::max; +#[cfg(not(feature = "no_index"))] +use crate::Array; -/// A type representing the access mode of a scripted function. +#[cfg(not(feature = "no_object"))] +use crate::Map; + +/// A type representing the access mode of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnAccess { /// Public function. @@ -46,31 +39,21 @@ pub enum FnAccess { Private, } -impl fmt::Display for FnAccess { - #[inline(always)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Private => write!(f, "private"), - Self::Public => write!(f, "public"), - } - } -} - impl FnAccess { /// Is this access mode private? #[inline(always)] pub fn is_private(self) -> bool { match self { - Self::Public => false, Self::Private => true, + Self::Public => false, } } /// Is this access mode public? #[inline(always)] pub fn is_public(self) -> bool { match self { - Self::Public => true, Self::Private => false, + Self::Public => true, } } } @@ -98,7 +81,7 @@ pub struct ScriptFnDef { pub params: StaticVec, /// Access to external variables. #[cfg(not(feature = "no_closure"))] - pub externals: HashSet, + pub externals: crate::stdlib::collections::HashSet, } impl fmt::Display for ScriptFnDef { @@ -187,7 +170,7 @@ impl AST { #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clone_functions_only(&self) -> Self { - self.clone_functions_only_filtered(|_, _, _| true) + self.clone_functions_only_filtered(|_, _, _, _, _| true) } /// Clone the `AST`'s functions into a new `AST` based on a filter predicate. /// No statements are cloned. @@ -197,7 +180,7 @@ impl AST { #[inline(always)] pub fn clone_functions_only_filtered( &self, - mut filter: impl FnMut(FnAccess, &str, usize) -> bool, + mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> Self { let mut functions: Module = Default::default(); functions.merge_filtered(&self.1, &mut filter); @@ -260,7 +243,7 @@ impl AST { /// ``` #[inline(always)] pub fn merge(&self, other: &Self) -> Self { - self.merge_filtered(other, |_, _, _| true) + self.merge_filtered(other, |_, _, _, _, _| true) } /// Combine one `AST` with another. The second `AST` is consumed. /// @@ -312,7 +295,7 @@ impl AST { /// ``` #[inline(always)] pub fn combine(&mut self, other: Self) -> &mut Self { - self.combine_filtered(other, |_, _, _| true) + self.combine_filtered(other, |_, _, _, _, _| true) } /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version /// is returned. @@ -348,7 +331,8 @@ impl AST { /// "#)?; /// /// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' - /// let ast = ast1.merge_filtered(&ast2, |_, name, params| name == "error" && params == 0); + /// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params| + /// script && name == "error" && params == 0); /// /// // 'ast' is essentially: /// // @@ -369,7 +353,7 @@ impl AST { pub fn merge_filtered( &self, other: &Self, - mut filter: impl FnMut(FnAccess, &str, usize) -> bool, + mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> Self { let Self(statements, functions) = self; @@ -422,7 +406,8 @@ impl AST { /// "#)?; /// /// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' - /// ast1.combine_filtered(ast2, |_, name, params| name == "error" && params == 0); + /// ast1.combine_filtered(ast2, |_, _, script, name, params| + /// script && name == "error" && params == 0); /// /// // 'ast1' is essentially: /// // @@ -443,7 +428,7 @@ impl AST { pub fn combine_filtered( &mut self, other: Self, - mut filter: impl FnMut(FnAccess, &str, usize) -> bool, + mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> &mut Self { let Self(ref mut statements, ref mut functions) = self; statements.extend(other.0.into_iter()); @@ -468,22 +453,25 @@ impl AST { /// "#)?; /// /// // Remove all functions except 'foo(_)' - /// ast.retain_functions(|_, name, params| name == "foo" && params == 1); + /// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1); /// # } /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "no_function"))] #[inline(always)] - pub fn retain_functions(&mut self, filter: impl FnMut(FnAccess, &str, usize) -> bool) { - self.1.retain_functions(filter); + pub fn retain_functions( + &mut self, + filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool, + ) { + self.1.retain_script_functions(filter); } /// Iterate through all functions #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn iter_functions<'a>( &'a self, - ) -> impl Iterator)> + 'a { + ) -> impl Iterator)> + 'a { self.1.iter_script_fn() } /// Clear all function definitions in the `AST`. @@ -940,14 +928,20 @@ impl Expr { #[cfg(not(feature = "no_index"))] Self::Array(x, _) if self.is_constant() => { - let mut arr = Array::with_capacity(max(crate::engine::TYPICAL_ARRAY_SIZE, x.len())); + let mut arr = Array::with_capacity(crate::stdlib::cmp::max( + crate::engine::TYPICAL_ARRAY_SIZE, + x.len(), + )); arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); Dynamic(Union::Array(Box::new(arr))) } #[cfg(not(feature = "no_object"))] Self::Map(x, _) if self.is_constant() => { - let mut map = Map::with_capacity(max(crate::engine::TYPICAL_MAP_SIZE, x.len())); + let mut map = Map::with_capacity(crate::stdlib::cmp::max( + crate::engine::TYPICAL_MAP_SIZE, + x.len(), + )); map.extend( x.iter() .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())), diff --git a/src/dynamic.rs b/src/dynamic.rs index ae23098d..2a6a0220 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1,19 +1,7 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. -use crate::fn_native::{FnPtr, SendSync}; +use crate::fn_native::SendSync; use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; -use crate::utils::ImmutableString; -use crate::INT; - -#[cfg(not(feature = "no_float"))] -use crate::FLOAT; - -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - use crate::stdlib::{ any::{type_name, Any, TypeId}, boxed::Box, @@ -23,6 +11,16 @@ use crate::stdlib::{ ops::{Deref, DerefMut}, string::{String, ToString}, }; +use crate::{FnPtr, ImmutableString, INT}; + +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; + +#[cfg(not(feature = "no_index"))] +use crate::Array; + +#[cfg(not(feature = "no_object"))] +use crate::Map; #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] diff --git a/src/engine.rs b/src/engine.rs index ec1aadb0..44af641d 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,20 +1,14 @@ //! Main module defining the script evaluation `Engine`. use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt}; -use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant}; +use crate::dynamic::{map_std_type_name, Union, Variant}; use crate::fn_call::run_builtin_op_assignment; -use crate::fn_native::{CallableFunction, Callback, FnPtr, IteratorFn, OnVarCallback, Shared}; -use crate::module::{Module, NamespaceRef}; +use crate::fn_native::{CallableFunction, Callback, IteratorFn, OnVarCallback}; +use crate::module::NamespaceRef; use crate::optimize::OptimizationLevel; use crate::packages::{Package, PackagesCollection, StandardPackage}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; -use crate::result::EvalAltResult; -use crate::scope::{EntryType as ScopeEntryType, Scope}; -use crate::syntax::CustomSyntax; -use crate::token::{Position, NO_POS}; -use crate::utils::{get_hasher, ImmutableString}; -use crate::{calc_native_fn_hash, StaticVec}; - +use crate::scope::EntryType as ScopeEntryType; use crate::stdlib::{ any::{type_name, TypeId}, borrow::Cow, @@ -27,21 +21,21 @@ use crate::stdlib::{ ops::DerefMut, string::{String, ToString}, }; +use crate::syntax::CustomSyntax; +use crate::utils::get_hasher; +use crate::{ + calc_native_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position, Scope, + Shared, StaticVec, NO_POS, +}; -/// Variable-sized array of `Dynamic` values. -/// -/// Not available under the `no_index` feature. #[cfg(not(feature = "no_index"))] -pub type Array = crate::stdlib::vec::Vec; +use crate::Array; #[cfg(not(feature = "no_index"))] pub const TYPICAL_ARRAY_SIZE: usize = 8; // Small arrays are typical -/// Hash map of `Dynamic` values with `ImmutableString` keys. -/// -/// Not available under the `no_object` feature. #[cfg(not(feature = "no_object"))] -pub type Map = HashMap; +use crate::Map; #[cfg(not(feature = "no_object"))] pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical @@ -58,7 +52,7 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical // We cannot use &str or Cow here because `eval` may load a module and the module name will live beyond // the AST of the eval script text. The best we can do is a shared reference. #[derive(Debug, Clone, Default)] -pub struct Imports(StaticVec<(ImmutableString, bool, Shared)>); +pub struct Imports(StaticVec<(ImmutableString, Shared)>); impl Imports { /// Get the length of this stack of imported modules. @@ -71,7 +65,7 @@ impl Imports { } /// Get the imported module at a particular index. pub fn get(&self, index: usize) -> Option> { - self.0.get(index).map(|(_, _, m)| m).cloned() + self.0.get(index).map(|(_, m)| m).cloned() } /// Get the index of an imported module by name. pub fn find(&self, name: &str) -> Option { @@ -79,21 +73,12 @@ impl Imports { .iter() .enumerate() .rev() - .find(|(_, (key, _, _))| key.as_str() == name) + .find(|(_, (key, _))| key.as_str() == name) .map(|(index, _)| index) } /// Push an imported module onto the stack. pub fn push(&mut self, name: impl Into, module: impl Into>) { - self.0.push((name.into(), false, module.into())); - } - /// Push a fixed module onto the stack. - #[cfg(not(feature = "no_module"))] - pub(crate) fn push_fixed( - &mut self, - name: impl Into, - module: impl Into>, - ) { - self.0.push((name.into(), true, module.into())); + self.0.push((name.into(), module.into())); } /// Truncate the stack of imported modules to a particular length. pub fn truncate(&mut self, size: usize) { @@ -101,58 +86,49 @@ impl Imports { } /// Get an iterator to this stack of imported modules. #[allow(dead_code)] - pub fn iter(&self) -> impl Iterator)> { + pub fn iter(&self) -> impl Iterator)> { self.0 .iter() - .map(|(name, fixed, module)| (name.as_str(), *fixed, module.clone())) + .map(|(name, module)| (name.as_str(), module.clone())) } /// Get an iterator to this stack of imported modules. #[allow(dead_code)] pub(crate) fn iter_raw<'a>( &'a self, - ) -> impl Iterator)> + 'a { + ) -> impl Iterator)> + 'a { self.0.iter().cloned() } /// Get a consuming iterator to this stack of imported modules. - pub fn into_iter(self) -> impl Iterator)> { + pub fn into_iter(self) -> impl Iterator)> { self.0.into_iter() } /// Add a stream of imported modules. - pub fn extend( - &mut self, - stream: impl Iterator)>, - ) { + pub fn extend(&mut self, stream: impl Iterator)>) { self.0.extend(stream) } /// Does the specified function hash key exist in this stack of imported modules? #[allow(dead_code)] pub fn contains_fn(&self, hash: u64) -> bool { - self.0 - .iter() - .any(|(_, fixed, m)| *fixed && m.contains_qualified_fn(hash)) + self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash)) } /// Get specified function via its hash key. pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { self.0 .iter() .rev() - .filter(|&&(_, fixed, _)| fixed) - .find_map(|(_, _, m)| m.get_qualified_fn(hash)) + .find_map(|(_, m)| m.get_qualified_fn(hash)) } /// Does the specified TypeId iterator exist in this stack of imported modules? #[allow(dead_code)] pub fn contains_iter(&self, id: TypeId) -> bool { - self.0 - .iter() - .any(|(_, fixed, m)| *fixed && m.contains_qualified_iter(id)) + self.0.iter().any(|(_, m)| m.contains_qualified_iter(id)) } /// Get the specified TypeId iterator. pub fn get_iter(&self, id: TypeId) -> Option { self.0 .iter() .rev() - .filter(|&&(_, fixed, _)| fixed) - .find_map(|(_, _, m)| m.get_qualified_iter(id)) + .find_map(|(_, m)| m.get_qualified_iter(id)) } } diff --git a/src/engine_api.rs b/src/engine_api.rs index dcff03a4..20187b2e 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1,32 +1,26 @@ //! Module that defines the extern API of `Engine`. -use crate::ast::AST; -use crate::dynamic::{Dynamic, Variant}; -use crate::engine::{Engine, EvalContext, Imports}; -use crate::fn_native::{FnCallArgs, NativeCallContext, SendSync}; +use crate::dynamic::Variant; +use crate::engine::{EvalContext, Imports}; +use crate::fn_native::{FnCallArgs, SendSync}; use crate::optimize::OptimizationLevel; -use crate::parse_error::ParseError; -use crate::result::EvalAltResult; -use crate::scope::Scope; -use crate::token::NO_POS; -use crate::utils::get_hasher; - -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, hash::{Hash, Hasher}, string::String, }; +use crate::utils::get_hasher; +use crate::{ + scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, NativeCallContext, + ParseError, AST, NO_POS, +}; -#[cfg(not(feature = "no_std"))] -#[cfg(not(target_arch = "wasm32"))] -use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf}; +#[cfg(not(feature = "no_index"))] +use crate::Array; + +#[cfg(not(feature = "no_object"))] +use crate::Map; /// Calculate a unique hash for a script. fn calc_hash_for_scripts<'a>(scripts: impl IntoIterator) -> u64 { @@ -62,7 +56,8 @@ impl Engine { + SendSync + 'static, ) -> &mut Self { - self.global_module.set_raw_fn(name, arg_types, func); + self.global_module + .set_raw_fn(name, FnNamespace::Global, FnAccess::Public, arg_types, func); self } /// Register a custom type for use with the `Engine`. @@ -204,15 +199,11 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn register_get( + pub fn register_get( &mut self, name: &str, callback: impl Fn(&mut T) -> U + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - { + ) -> &mut Self { crate::RegisterFn::register_fn(self, &crate::engine::make_getter(name), callback) } /// Register a getter function for a member of a registered type with the `Engine`. @@ -304,15 +295,11 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn register_set( + pub fn register_set( &mut self, name: &str, callback: impl Fn(&mut T, U) + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - { + ) -> &mut Self { crate::RegisterFn::register_fn(self, &crate::engine::make_setter(name), callback) } /// Register a setter function for a member of a registered type with the `Engine`. @@ -357,15 +344,11 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn register_set_result( + pub fn register_set_result( &mut self, name: &str, callback: impl Fn(&mut T, U) -> Result<(), Box> + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - { + ) -> &mut Self { crate::RegisterResultFn::register_result_fn( self, &crate::engine::make_setter(name), @@ -412,16 +395,12 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn register_get_set( + pub fn register_get_set( &mut self, name: &str, get_fn: impl Fn(&mut T) -> U + SendSync + 'static, set_fn: impl Fn(&mut T, U) + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - { + ) -> &mut Self { self.register_get(name, get_fn).register_set(name, set_fn) } /// Register an index getter for a custom type with the `Engine`. @@ -467,15 +446,10 @@ impl Engine { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn register_indexer_get( + pub fn register_indexer_get( &mut self, callback: impl Fn(&mut T, X) -> U + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - X: Variant + Clone, - { + ) -> &mut Self { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -538,14 +512,10 @@ impl Engine { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn register_indexer_get_result( + pub fn register_indexer_get_result( &mut self, callback: impl Fn(&mut T, X) -> Result> + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - X: Variant + Clone, - { + ) -> &mut Self { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -605,15 +575,10 @@ impl Engine { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn register_indexer_set( + pub fn register_indexer_set( &mut self, callback: impl Fn(&mut T, X, U) + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - X: Variant + Clone, - { + ) -> &mut Self { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -677,15 +642,14 @@ impl Engine { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn register_indexer_set_result( + pub fn register_indexer_set_result< + T: Variant + Clone, + X: Variant + Clone, + U: Variant + Clone, + >( &mut self, callback: impl Fn(&mut T, X, U) -> Result<(), Box> + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - X: Variant + Clone, - { + ) -> &mut Self { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -748,20 +712,15 @@ impl Engine { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn register_indexer_get_set( + pub fn register_indexer_get_set( &mut self, getter: impl Fn(&mut T, X) -> U + SendSync + 'static, setter: impl Fn(&mut T, X, U) -> () + SendSync + 'static, - ) -> &mut Self - where - T: Variant + Clone, - U: Variant + Clone, - X: Variant + Clone, - { + ) -> &mut Self { self.register_indexer_get(getter) .register_indexer_set(setter) } - /// Register a `Module` as a sub-module with the `Engine`. + /// Register a `Module` as a fixed module namespace with the `Engine`. /// /// # Example /// @@ -775,7 +734,7 @@ impl Engine { /// let mut module = Module::new(); /// module.set_fn_1("calc", |x: i64| Ok(x + 1)); /// - /// // Register the module as a sub-module + /// // Register the module as a fixed sub-module /// engine.register_module("CalcService", module); /// /// assert_eq!(engine.eval::("CalcService::calc(41)")?, 42); @@ -794,9 +753,9 @@ impl Engine { // Index the module (making a clone copy if necessary) if it is not indexed let mut module = crate::fn_native::shared_take_or_clone(module); module.build_index(); - self.global_sub_modules.push_fixed(name, module); + self.global_sub_modules.push(name, module); } else { - self.global_sub_modules.push_fixed(name, module); + self.global_sub_modules.push(name, module); } self } @@ -935,8 +894,10 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] #[inline] - fn read_file(path: PathBuf) -> Result> { - let mut f = File::open(path.clone()).map_err(|err| { + fn read_file(path: crate::stdlib::path::PathBuf) -> Result> { + use crate::stdlib::io::Read; + + let mut f = crate::stdlib::fs::File::open(path.clone()).map_err(|err| { EvalAltResult::ErrorSystem( format!("Cannot open script file '{}'", path.to_string_lossy()), err.into(), @@ -977,7 +938,10 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] #[inline(always)] - pub fn compile_file(&self, path: PathBuf) -> Result> { + pub fn compile_file( + &self, + path: crate::stdlib::path::PathBuf, + ) -> Result> { self.compile_file_with_scope(&Default::default(), path) } /// Compile a script file into an `AST` using own scope, which can be used later for evaluation. @@ -1017,7 +981,7 @@ impl Engine { pub fn compile_file_with_scope( &self, scope: &Scope, - path: PathBuf, + path: crate::stdlib::path::PathBuf, ) -> Result> { Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) } @@ -1201,7 +1165,10 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] #[inline(always)] - pub fn eval_file(&self, path: PathBuf) -> Result> { + pub fn eval_file( + &self, + path: crate::stdlib::path::PathBuf, + ) -> Result> { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } /// Evaluate a script file with own scope. @@ -1229,7 +1196,7 @@ impl Engine { pub fn eval_file_with_scope( &self, scope: &mut Scope, - path: PathBuf, + path: crate::stdlib::path::PathBuf, ) -> Result> { Self::read_file(path).and_then(|contents| self.eval_with_scope::(scope, &contents)) } @@ -1428,7 +1395,10 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] #[inline(always)] - pub fn consume_file(&self, path: PathBuf) -> Result<(), Box> { + pub fn consume_file( + &self, + path: crate::stdlib::path::PathBuf, + ) -> Result<(), Box> { Self::read_file(path).and_then(|contents| self.consume(&contents)) } /// Evaluate a file with own scope, but throw away the result and only return error (if any). @@ -1439,7 +1409,7 @@ impl Engine { pub fn consume_file_with_scope( &self, scope: &mut Scope, - path: PathBuf, + path: crate::stdlib::path::PathBuf, ) -> Result<(), Box> { Self::read_file(path).and_then(|contents| self.consume_with_scope(scope, &contents)) } diff --git a/src/engine_settings.rs b/src/engine_settings.rs index 2c87a48a..09484087 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -1,10 +1,9 @@ //! Configuration settings for `Engine`. -use crate::engine::Engine; use crate::packages::PackageLibrary; -use crate::token::{is_valid_identifier, Token}; - use crate::stdlib::{format, string::String}; +use crate::token::{is_valid_identifier, Token}; +use crate::Engine; #[cfg(not(feature = "no_module"))] use crate::stdlib::boxed::Box; diff --git a/src/fn_args.rs b/src/fn_args.rs index db0f3cd4..d0c9de63 100644 --- a/src/fn_args.rs +++ b/src/fn_args.rs @@ -2,8 +2,8 @@ #![allow(non_snake_case)] -use crate::dynamic::{Dynamic, Variant}; -use crate::StaticVec; +use crate::dynamic::Variant; +use crate::{Dynamic, StaticVec}; /// Trait that represents arguments to a function call. /// Any data type that can be converted into a `Vec` can be used diff --git a/src/fn_call.rs b/src/fn_call.rs index 2a1973f3..33bce76e 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1,29 +1,15 @@ //! Implement function-calling mechanism for `Engine`. use crate::ast::{Expr, Stmt}; -use crate::dynamic::Dynamic; use crate::engine::{ - search_imports, Engine, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, + search_imports, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; -use crate::fn_native::{FnCallArgs, FnPtr}; -use crate::module::{Module, NamespaceRef}; +use crate::fn_native::FnCallArgs; +use crate::module::NamespaceRef; use crate::optimize::OptimizationLevel; -use crate::parse_error::ParseErrorType; -use crate::result::EvalAltResult; -use crate::scope::{EntryType as ScopeEntryType, Scope}; -use crate::stdlib::ops::Deref; -use crate::token::NO_POS; -use crate::utils::ImmutableString; -use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT}; - -#[cfg(not(feature = "no_float"))] -use crate::FLOAT; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - +use crate::scope::EntryType as ScopeEntryType; use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, @@ -31,34 +17,45 @@ use crate::stdlib::{ format, iter::{empty, once}, mem, + ops::Deref, string::ToString, vec::Vec, }; +use crate::{ + calc_native_fn_hash, calc_script_fn_hash, Dynamic, Engine, EvalAltResult, FnPtr, + ImmutableString, Module, ParseErrorType, Scope, StaticVec, INT, NO_POS, +}; + +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; + +#[cfg(not(feature = "no_object"))] +use crate::Map; #[cfg(feature = "no_std")] #[cfg(not(feature = "no_float"))] use num_traits::float::Float; /// Extract the property name from a getter function name. +#[cfg(not(feature = "no_object"))] #[inline(always)] fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> { - #[cfg(not(feature = "no_object"))] if _fn_name.starts_with(crate::engine::FN_GET) { - return Some(&_fn_name[crate::engine::FN_GET.len()..]); + Some(&_fn_name[crate::engine::FN_GET.len()..]) + } else { + None } - - None } /// Extract the property name from a setter function name. +#[cfg(not(feature = "no_object"))] #[inline(always)] fn extract_prop_from_setter(_fn_name: &str) -> Option<&str> { - #[cfg(not(feature = "no_object"))] if _fn_name.starts_with(crate::engine::FN_SET) { - return Some(&_fn_name[crate::engine::FN_SET.len()..]); + Some(&_fn_name[crate::engine::FN_SET.len()..]) + } else { + None } - - None } /// A type that temporarily stores a mutable reference to a `Dynamic`, @@ -244,6 +241,7 @@ impl Engine { } // Getter function not found? + #[cfg(not(feature = "no_object"))] if let Some(prop) = extract_prop_from_getter(fn_name) { return EvalAltResult::ErrorDotExpr( format!( @@ -257,6 +255,7 @@ impl Engine { } // Setter function not found? + #[cfg(not(feature = "no_object"))] if let Some(prop) = extract_prop_from_setter(fn_name) { return EvalAltResult::ErrorDotExpr( format!( diff --git a/src/fn_func.rs b/src/fn_func.rs index 888043c9..0a52d478 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -3,14 +3,9 @@ #![cfg(not(feature = "no_function"))] #![allow(non_snake_case)] -use crate::ast::AST; use crate::dynamic::Variant; -use crate::engine::Engine; -use crate::parse_error::ParseError; -use crate::result::EvalAltResult; -use crate::scope::Scope; - use crate::stdlib::{boxed::Box, string::ToString}; +use crate::{Engine, EvalAltResult, ParseError, Scope, AST}; /// Trait to create a Rust closure from a script. pub trait Func { diff --git a/src/fn_native.rs b/src/fn_native.rs index 6715d98d..8b6734c7 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,29 +1,20 @@ //! Module defining interfaces to native-Rust functions. use crate::ast::{FnAccess, ScriptFnDef}; -use crate::dynamic::Dynamic; -use crate::engine::{Engine, EvalContext, Imports}; -use crate::module::Module; +use crate::engine::Imports; use crate::plugin::PluginFunction; -use crate::result::EvalAltResult; -use crate::token::{is_valid_identifier, NO_POS}; -use crate::utils::ImmutableString; -use crate::{calc_script_fn_hash, StaticVec}; - use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String}; +use crate::token::is_valid_identifier; +use crate::{ + calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, + StaticVec, NO_POS, +}; #[cfg(not(feature = "sync"))] use crate::stdlib::rc::Rc; #[cfg(feature = "sync")] use crate::stdlib::sync::Arc; -#[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] -#[cfg(not(feature = "sync"))] -use crate::stdlib::cell::RefCell; -#[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] -#[cfg(feature = "sync")] -use crate::stdlib::sync::RwLock; - /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] pub trait SendSync: Send + Sync {} @@ -48,11 +39,11 @@ pub type Shared = Arc; /// Synchronized shared object. #[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] #[cfg(not(feature = "sync"))] -pub type Locked = RefCell; +pub type Locked = crate::stdlib::cell::RefCell; /// Synchronized shared object. #[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] #[cfg(feature = "sync")] -pub type Locked = RwLock; +pub type Locked = crate::stdlib::sync::RwLock; /// Context of native Rust function call. #[derive(Debug, Copy, Clone)] diff --git a/src/fn_register.rs b/src/fn_register.rs index f1e1f50e..79e68958 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -2,15 +2,13 @@ #![allow(non_snake_case)] -use crate::ast::FnAccess; -use crate::dynamic::{Dynamic, DynamicWriteLock, Variant}; -use crate::engine::Engine; -use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, NativeCallContext, SendSync}; +use crate::dynamic::{DynamicWriteLock, Variant}; +use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::r#unsafe::unsafe_cast_box; -use crate::result::EvalAltResult; -use crate::utils::ImmutableString; - use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String}; +use crate::{ + Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, NativeCallContext, +}; /// Trait to register custom functions with the `Engine`. pub trait RegisterFn { @@ -189,7 +187,7 @@ macro_rules! def_register { { #[inline] fn register_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_module.set_fn(name, FnAccess::Public, + self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public, &[$(map_type_id::<$par>()),*], CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*)) ); @@ -204,7 +202,7 @@ macro_rules! def_register { { #[inline] fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_module.set_fn(name, FnAccess::Public, + self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public, &[$(map_type_id::<$par>()),*], CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*)) ); diff --git a/src/lib.rs b/src/lib.rs index 4ab6b3a5..292521f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,12 +111,12 @@ pub type FLOAT = f64; #[cfg(feature = "f32_float")] pub type FLOAT = f32; -pub use ast::AST; +pub use ast::{FnAccess, AST}; pub use dynamic::Dynamic; pub use engine::{Engine, EvalContext}; pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_register::{RegisterFn, RegisterResultFn}; -pub use module::Module; +pub use module::{FnNamespace, Module}; pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use result::EvalAltResult; pub use scope::Scope; @@ -135,17 +135,20 @@ pub(crate) use utils::{calc_native_fn_hash, calc_script_fn_hash}; pub use rhai_codegen::*; -#[cfg(not(feature = "no_function"))] -pub use ast::FnAccess; - #[cfg(not(feature = "no_function"))] pub use fn_func::Func; +/// Variable-sized array of `Dynamic` values. +/// +/// Not available under the `no_index` feature. #[cfg(not(feature = "no_index"))] -pub use engine::Array; +pub type Array = stdlib::vec::Vec; +/// Hash map of `Dynamic` values with `ImmutableString` keys. +/// +/// Not available under the `no_object` feature. #[cfg(not(feature = "no_object"))] -pub use engine::Map; +pub type Map = stdlib::collections::HashMap; #[cfg(not(feature = "no_module"))] pub use module::ModuleResolver; diff --git a/src/module/mod.rs b/src/module/mod.rs index b22da434..f14f191d 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1,24 +1,11 @@ //! Module defining external-loaded modules for Rhai. use crate::ast::{FnAccess, IdentX}; -use crate::dynamic::{Dynamic, Variant}; +use crate::dynamic::Variant; use crate::fn_native::{ - shared_make_mut, shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, - NativeCallContext, SendSync, Shared, + shared_make_mut, shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync, }; use crate::fn_register::by_value as cast_arg; -use crate::result::EvalAltResult; -use crate::token::{Token, NO_POS}; -use crate::utils::{ImmutableString, StraightHasherBuilder}; -use crate::StaticVec; - -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_index"))] -#[cfg(not(feature = "no_object"))] -use crate::Map; - use crate::stdlib::{ any::TypeId, boxed::Box, @@ -30,12 +17,57 @@ use crate::stdlib::{ string::{String, ToString}, vec::Vec, }; +use crate::token::Token; +use crate::utils::StraightHasherBuilder; +use crate::{ + Dynamic, EvalAltResult, ImmutableString, NativeCallContext, Shared, StaticVec, NO_POS, +}; + +#[cfg(not(feature = "no_index"))] +use crate::Array; + +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +use crate::Map; + +#[cfg(not(feature = "no_function"))] +pub type SharedScriptFnDef = Shared; + +/// A type representing the namespace of a function. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum FnNamespace { + /// Global namespace. + Global, + /// Internal only. + Internal, +} + +impl FnNamespace { + /// Is this namespace global? + #[inline(always)] + pub fn is_global(self) -> bool { + match self { + Self::Global => true, + Self::Internal => false, + } + } + /// Is this namespace internal? + #[inline(always)] + pub fn is_internal(self) -> bool { + match self { + Self::Global => false, + Self::Internal => true, + } + } +} /// Data structure containing a single registered function. #[derive(Debug, Clone)] pub struct FuncInfo { /// Function instance. pub func: CallableFunction, + /// Function namespace. + pub namespace: FnNamespace, /// Function access mode. pub access: FnAccess, /// Function name. @@ -282,7 +314,7 @@ impl Module { /// If there is an existing function of the same name and number of arguments, it is replaced. #[cfg(not(feature = "no_function"))] #[inline] - pub(crate) fn set_script_fn(&mut self, fn_def: Shared) -> u64 { + pub(crate) fn set_script_fn(&mut self, fn_def: SharedScriptFnDef) -> u64 { // None + function name + number of arguments. let num_params = fn_def.params.len(); let hash_script = crate::calc_script_fn_hash(empty(), &fn_def.name, num_params); @@ -290,6 +322,7 @@ impl Module { hash_script, FuncInfo { name: fn_def.name.to_string(), + namespace: FnNamespace::Internal, access: fn_def.access, params: num_params, types: None, @@ -308,7 +341,7 @@ impl Module { name: &str, num_params: usize, public_only: bool, - ) -> Option<&Shared> { + ) -> Option<&SharedScriptFnDef> { self.functions .values() .find( @@ -440,6 +473,7 @@ impl Module { pub fn set_fn( &mut self, name: impl Into, + namespace: FnNamespace, access: FnAccess, arg_types: &[TypeId], func: CallableFunction, @@ -464,6 +498,7 @@ impl Module { hash_fn, FuncInfo { name, + namespace, access, params: params.len(), types: Some(params), @@ -507,10 +542,11 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; + /// use rhai::{Module, FnNamespace, FnAccess}; /// /// let mut module = Module::new(); /// let hash = module.set_raw_fn("double_or_not", + /// FnNamespace::Internal, FnAccess::Public, /// // Pass parameter types via a slice with TypeId's /// &[std::any::TypeId::of::(), std::any::TypeId::of::()], /// // Fixed closure signature @@ -539,6 +575,8 @@ impl Module { pub fn set_raw_fn( &mut self, name: impl Into, + namespace: FnNamespace, + access: FnAccess, arg_types: &[TypeId], func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result> + SendSync @@ -546,14 +584,40 @@ impl Module { ) -> u64 { let f = move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); + self.set_fn( name, - FnAccess::Public, + namespace, + access, arg_types, CallableFunction::from_method(Box::new(f)), ) } + /// Get the namespace of a registered function. + /// Returns `None` if a function with the hash does not exist. + /// + /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. + #[inline(always)] + pub fn get_fn_namespace(&self, hash: u64) -> Option { + self.functions.get(&hash).map(|f| f.namespace) + } + + /// Set the namespace of a registered function. + /// Returns the original namespace or `None` if a function with the hash does not exist. + /// + /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. + #[inline] + pub fn set_fn_namespace(&mut self, hash: u64, namespace: FnNamespace) -> Option { + if let Some(f) = self.functions.get_mut(&hash) { + let old_ns = f.namespace; + f.namespace = namespace; + Some(old_ns) + } else { + None + } + } + /// Set a Rust function taking no parameters into the module, returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. @@ -577,6 +641,7 @@ impl Module { let arg_types = []; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_pure(Box::new(f)), @@ -608,6 +673,7 @@ impl Module { let arg_types = [TypeId::of::()]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_pure(Box::new(f)), @@ -639,6 +705,7 @@ impl Module { let arg_types = [TypeId::of::()]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -698,6 +765,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::()]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_pure(Box::new(f)), @@ -735,6 +803,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::()]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -848,6 +917,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_pure(Box::new(f)), @@ -891,6 +961,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -949,6 +1020,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( crate::engine::FN_IDX_SET, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -1039,6 +1111,7 @@ impl Module { ]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_pure(Box::new(f)), @@ -1089,6 +1162,7 @@ impl Module { ]; self.set_fn( name, + FnNamespace::Internal, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -1196,14 +1270,14 @@ impl Module { /// Merge another module into this module. #[inline(always)] pub fn merge(&mut self, other: &Self) -> &mut Self { - self.merge_filtered(other, &mut |_, _, _| true) + self.merge_filtered(other, &mut |_, _, _, _, _| true) } - /// Merge another module into this module, with only selected script-defined functions based on a filter predicate. + /// Merge another module into this module based on a filter predicate. pub(crate) fn merge_filtered( &mut self, other: &Self, - mut _filter: &mut impl FnMut(FnAccess, &str, usize) -> bool, + mut _filter: &mut impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> &mut Self { #[cfg(not(feature = "no_function"))] other.modules.iter().for_each(|(k, v)| { @@ -1221,13 +1295,27 @@ impl Module { other .functions .iter() - .filter(|(_, FuncInfo { func, .. })| match func { - #[cfg(not(feature = "no_function"))] - CallableFunction::Script(f) => { - _filter(f.access, f.name.as_str(), f.params.len()) - } - _ => true, - }) + .filter( + |( + _, + FuncInfo { + namespace, + access, + name, + params, + func, + .. + }, + )| { + _filter( + *namespace, + *access, + func.is_script(), + name.as_str(), + *params, + ) + }, + ) .map(|(&k, v)| (k, v.clone())), ); @@ -1239,18 +1327,30 @@ impl Module { self } - /// Filter out the functions, retaining only some based on a filter predicate. + /// Filter out the functions, retaining only some script-defined functions based on a filter predicate. #[cfg(not(feature = "no_function"))] #[inline] - pub(crate) fn retain_functions( + pub(crate) fn retain_script_functions( &mut self, - mut filter: impl FnMut(FnAccess, &str, usize) -> bool, + mut filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool, ) -> &mut Self { - self.functions - .retain(|_, FuncInfo { func, .. }| match func { - CallableFunction::Script(f) => filter(f.access, f.name.as_str(), f.params.len()), - _ => true, - }); + self.functions.retain( + |_, + FuncInfo { + namespace, + access, + name, + params, + func, + .. + }| { + if func.is_script() { + filter(*namespace, *access, name.as_str(), *params) + } else { + false + } + }, + ); self.all_functions.clear(); self.all_variables.clear(); @@ -1294,16 +1394,25 @@ impl Module { #[inline(always)] pub(crate) fn iter_script_fn<'a>( &'a self, - ) -> impl Iterator)> + 'a { - self.functions - .values() - .map(|f| &f.func) - .filter(|f| f.is_script()) - .map(CallableFunction::get_fn_def) - .map(|f| { - let func = f.clone(); - (f.access, f.name.as_str(), f.params.len(), func) - }) + ) -> impl Iterator + 'a { + self.functions.values().filter(|f| f.func.is_script()).map( + |FuncInfo { + namespace, + access, + name, + params, + func, + .. + }| { + ( + *namespace, + *access, + name.as_str(), + *params, + func.get_fn_def().clone(), + ) + }, + ) } /// Get an iterator over all script-defined functions in the module. @@ -1315,14 +1424,17 @@ impl Module { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "internals"))] #[inline(always)] - pub fn iter_script_fn_info(&self) -> impl Iterator { + pub fn iter_script_fn_info( + &self, + ) -> impl Iterator { self.functions.values().filter(|f| f.func.is_script()).map( |FuncInfo { name, + namespace, access, params, .. - }| (*access, name.as_str(), *params), + }| (*namespace, *access, name.as_str(), *params), ) } @@ -1339,7 +1451,7 @@ impl Module { #[inline(always)] pub fn iter_script_fn_info( &self, - ) -> impl Iterator)> { + ) -> impl Iterator { self.iter_script_fn() } @@ -1393,13 +1505,10 @@ impl Module { // Extra modules left in the scope become sub-modules let mut func_mods: crate::engine::Imports = Default::default(); - mods.into_iter() - .skip(orig_mods_len) - .filter(|&(_, fixed, _)| !fixed) - .for_each(|(alias, _, m)| { - func_mods.push(alias.clone(), m.clone()); - module.set_sub_module(alias, m); - }); + mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| { + func_mods.push(alias.clone(), m.clone()); + module.set_sub_module(alias, m); + }); // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] @@ -1407,8 +1516,8 @@ impl Module { let ast_lib: Shared = ast.lib().clone().into(); ast.iter_functions() - .filter(|(access, _, _, _)| !access.is_private()) - .for_each(|(_, _, _, func)| { + .filter(|(_, access, _, _, _)| !access.is_private()) + .for_each(|(_, _, _, _, func)| { // Encapsulate AST environment let mut func = func.as_ref().clone(); func.lib = Some(ast_lib.clone()); @@ -1464,6 +1573,7 @@ impl Module { &hash, FuncInfo { name, + namespace, params, types, func, @@ -1471,22 +1581,20 @@ impl Module { }, )| { // Flatten all methods so they can be available without namespace qualifiers - #[cfg(not(feature = "no_object"))] - if func.is_method() { + if namespace.is_global() { functions.insert(hash, func.clone()); } + // Qualifiers + function name + number of arguments. + let hash_qualified_script = + crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params); + if let Some(param_types) = types { assert_eq!(*params, param_types.len()); // Namespace-qualified Rust functions are indexed in two steps: // 1) Calculate a hash in a similar manner to script-defined functions, // i.e. qualifiers + function name + number of arguments. - let hash_qualified_script = crate::calc_script_fn_hash( - qualifiers.iter().cloned(), - name, - *params, - ); // 2) Calculate a second hash with no qualifiers, empty function name, // and the actual list of argument `TypeId`'.s let hash_fn_args = crate::calc_native_fn_hash( @@ -1499,17 +1607,6 @@ impl Module { functions.insert(hash_qualified_fn, func.clone()); } else if cfg!(not(feature = "no_function")) { - let hash_qualified_script = - if cfg!(feature = "no_object") && qualifiers.is_empty() { - hash - } else { - // Qualifiers + function name + number of arguments. - crate::calc_script_fn_hash( - qualifiers.iter().map(|&v| v), - &name, - *params, - ) - }; functions.insert(hash_qualified_script, func.clone()); } }, diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index f1f76e70..6b4f6d9a 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,10 +1,5 @@ -use crate::engine::Engine; -use crate::fn_native::Shared; -use crate::module::{Module, ModuleResolver}; -use crate::result::EvalAltResult; -use crate::token::Position; - use crate::stdlib::{boxed::Box, ops::AddAssign, vec::Vec}; +use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; /// Module resolution service that holds a collection of module resolves, /// to be searched in sequential order. diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index a76f26d0..4753937d 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -1,12 +1,7 @@ -use crate::engine::Engine; -use crate::fn_native::{Locked, Shared}; -use crate::module::{Module, ModuleResolver}; -use crate::result::EvalAltResult; -use crate::token::Position; - use crate::stdlib::{ boxed::Box, collections::HashMap, io::Error as IoError, path::PathBuf, string::String, }; +use crate::{Engine, EvalAltResult, Locked, Module, ModuleResolver, Position, Shared}; /// Module resolution service that loads module script files from the file system. /// diff --git a/src/module/resolvers/mod.rs b/src/module/resolvers/mod.rs index 62638130..d131a1d0 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -1,10 +1,6 @@ -use crate::engine::Engine; -use crate::fn_native::{SendSync, Shared}; -use crate::module::Module; -use crate::result::EvalAltResult; -use crate::token::Position; - +use crate::fn_native::SendSync; use crate::stdlib::boxed::Box; +use crate::{Engine, EvalAltResult, Module, Position, Shared}; mod collection; pub use collection::ModuleResolversCollection; diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index eabfd3e3..09deb1b7 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -1,10 +1,5 @@ -use crate::engine::Engine; -use crate::fn_native::Shared; -use crate::module::{Module, ModuleResolver}; -use crate::result::EvalAltResult; -use crate::token::Position; - use crate::stdlib::{boxed::Box, collections::HashMap, ops::AddAssign, string::String}; +use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; /// Module resolution service that serves modules added into it. /// diff --git a/src/optimize.rs b/src/optimize.rs index ab95b2fe..01beb455 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,19 +1,12 @@ //! Module implementing the AST optimizer. -use crate::ast::{Expr, ScriptFnDef, Stmt, AST}; -use crate::dynamic::Dynamic; +use crate::ast::{Expr, ScriptFnDef, Stmt}; use crate::engine::{ - Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, + KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::fn_call::run_builtin_binary_op; -use crate::module::Module; use crate::parser::map_dynamic_to_expr; -use crate::scope::Scope; -use crate::token::{is_valid_identifier, Position, NO_POS}; -use crate::utils::get_hasher; -use crate::{calc_native_fn_hash, StaticVec}; - use crate::stdlib::{ boxed::Box, hash::{Hash, Hasher}, @@ -23,6 +16,11 @@ use crate::stdlib::{ vec, vec::Vec, }; +use crate::token::is_valid_identifier; +use crate::utils::get_hasher; +use crate::{ + calc_native_fn_hash, Dynamic, Engine, Module, Position, Scope, StaticVec, AST, NO_POS, +}; /// Level of optimization performed. /// diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index c8398cea..5af2a2b8 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -1,10 +1,8 @@ #![allow(non_snake_case)] -use crate::def_package; use crate::plugin::*; -use crate::INT; - -use crate::{result::EvalAltResult, token::NO_POS}; +use crate::stdlib::{format, string::String}; +use crate::{def_package, EvalAltResult, INT, NO_POS}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; @@ -13,8 +11,6 @@ use crate::FLOAT; #[cfg(not(feature = "no_float"))] use num_traits::float::Float; -use crate::stdlib::{format, string::String}; - #[inline(always)] pub fn make_err(msg: impl Into) -> Box { EvalAltResult::ErrorArithmetic(msg.into(), NO_POS).into() diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 6d077d7b..8f493063 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -1,21 +1,17 @@ #![cfg(not(feature = "no_index"))] #![allow(non_snake_case)] -use crate::def_package; -use crate::dynamic::Dynamic; -use crate::engine::{Array, OP_EQUALS, TYPICAL_ARRAY_SIZE}; -use crate::fn_native::{FnPtr, NativeCallContext}; +use crate::engine::{OP_EQUALS, TYPICAL_ARRAY_SIZE}; use crate::plugin::*; -use crate::result::EvalAltResult; -use crate::token::NO_POS; -use crate::utils::ImmutableString; -use crate::INT; +use crate::stdlib::{any::TypeId, boxed::Box, cmp::max, cmp::Ordering, string::ToString}; +use crate::{ + def_package, Array, Dynamic, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, INT, + NO_POS, +}; #[cfg(not(feature = "no_object"))] use crate::Map; -use crate::stdlib::{any::TypeId, boxed::Box, cmp::max, cmp::Ordering, string::ToString}; - pub type Unit = (); macro_rules! gen_array_functions { diff --git a/src/packages/eval.rs b/src/packages/eval.rs index 68012dc5..525b8f3a 100644 --- a/src/packages/eval.rs +++ b/src/packages/eval.rs @@ -1,8 +1,5 @@ -use crate::def_package; -use crate::dynamic::Dynamic; use crate::plugin::*; -use crate::result::EvalAltResult; -use crate::utils::ImmutableString; +use crate::{def_package, Dynamic, EvalAltResult, ImmutableString}; def_package!(crate:EvalPackage:"Disable 'eval'.", lib, { combine_with_exported_module!(lib, "eval", eval_override); diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 64494290..630c58d9 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -1,6 +1,5 @@ -use crate::def_package; -use crate::fn_native::FnPtr; use crate::plugin::*; +use crate::{def_package, FnPtr}; def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index a9ff0f10..c336707f 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,12 +1,9 @@ -use crate::def_package; use crate::dynamic::Variant; -use crate::result::EvalAltResult; -use crate::INT; - use crate::stdlib::{ boxed::Box, ops::{Add, Range}, }; +use crate::{def_package, EvalAltResult, INT}; fn get_range(from: T, to: T) -> Result, Box> { Ok(from..to) diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index a46fb67d..22cdbe09 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -1,11 +1,8 @@ #![cfg(not(feature = "no_object"))] -use crate::def_package; -use crate::dynamic::Dynamic; -use crate::engine::{Map, OP_EQUALS}; +use crate::engine::OP_EQUALS; use crate::plugin::*; -use crate::utils::ImmutableString; -use crate::INT; +use crate::{def_package, Dynamic, ImmutableString, Map, INT}; #[cfg(not(feature = "no_index"))] use crate::Array; diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 1dd00419..c186782a 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,9 +1,7 @@ #![allow(non_snake_case)] -use crate::def_package; use crate::plugin::*; -use crate::token::NO_POS; -use crate::INT; +use crate::{def_package, INT, NO_POS}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; diff --git a/src/packages/mod.rs b/src/packages/mod.rs index df067fe1..4b0f8ecc 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -1,10 +1,8 @@ //! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages. -use crate::fn_native::{CallableFunction, IteratorFn, Shared}; -use crate::module::Module; -use crate::StaticVec; - +use crate::fn_native::{CallableFunction, IteratorFn}; use crate::stdlib::any::TypeId; +use crate::{Module, Shared, StaticVec}; pub(crate) mod arithmetic; mod array_basic; diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 99369195..2db28c7a 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -1,11 +1,13 @@ #![allow(non_snake_case)] -use crate::def_package; use crate::engine::{FN_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT}; -use crate::fn_native::FnPtr; use crate::plugin::*; -use crate::utils::ImmutableString; -use crate::INT; +use crate::stdlib::{ + fmt::{Debug, Display}, + format, + string::ToString, +}; +use crate::{def_package, FnPtr, ImmutableString, INT}; #[cfg(not(feature = "no_index"))] use crate::Array; @@ -13,12 +15,6 @@ use crate::Array; #[cfg(not(feature = "no_object"))] use crate::Map; -use crate::stdlib::{ - fmt::{Debug, Display}, - format, - string::ToString, -}; - type Unit = (); macro_rules! gen_functions { diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 0e336f3e..661e00e8 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -1,16 +1,10 @@ #![allow(non_snake_case)] -use crate::def_package; -use crate::dynamic::Dynamic; -use crate::fn_native::FnPtr; use crate::plugin::*; -use crate::utils::ImmutableString; -use crate::StaticVec; -use crate::INT; - use crate::stdlib::{ any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec, }; +use crate::{def_package, Dynamic, FnPtr, ImmutableString, StaticVec, INT}; macro_rules! gen_concat_functions { ($root:ident => $($arg_type:ident),+ ) => { diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index e419905b..bcd50f55 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -1,18 +1,13 @@ #![cfg(not(feature = "no_std"))] use super::{arithmetic::make_err as make_arithmetic_err, math_basic::MAX_INT}; - -use crate::def_package; -use crate::dynamic::Dynamic; use crate::plugin::*; -use crate::result::EvalAltResult; -use crate::INT; +use crate::stdlib::boxed::Box; +use crate::{def_package, Dynamic, EvalAltResult, INT}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; -use crate::stdlib::boxed::Box; - #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::time::{Duration, Instant}; diff --git a/src/parse_error.rs b/src/parse_error.rs index f34a8f36..cd90d2d7 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -1,14 +1,12 @@ //! Module containing error definitions for the parsing process. -use crate::result::EvalAltResult; -use crate::token::{Position, NO_POS}; - use crate::stdlib::{ boxed::Box, error::Error, fmt, string::{String, ToString}, }; +use crate::{EvalAltResult, Position, NO_POS}; /// _[INTERNALS]_ Error encountered when tokenizing the script text. /// Exported under the `internals` feature only. diff --git a/src/parser.rs b/src/parser.rs index 59e4d4f8..795993de 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,24 +1,14 @@ //! Main module defining the lexer and parser. use crate::ast::{ - BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt, AST, + BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt, }; -use crate::dynamic::{Dynamic, Union}; -use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; +use crate::dynamic::Union; +use crate::engine::{KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; use crate::module::NamespaceRef; -use crate::optimize::{optimize_into_ast, OptimizationLevel}; -use crate::parse_error::{LexError, ParseError, ParseErrorType}; -use crate::scope::{EntryType as ScopeEntryType, Scope}; -use crate::syntax::CustomSyntax; -use crate::token::{ - is_keyword_function, is_valid_identifier, Position, Token, TokenStream, NO_POS, -}; -use crate::utils::{get_hasher, ImmutableString, StraightHasherBuilder}; -use crate::{calc_script_fn_hash, StaticVec}; - -#[cfg(not(feature = "no_float"))] -use crate::FLOAT; - +use crate::optimize::optimize_into_ast; +use crate::optimize::OptimizationLevel; +use crate::scope::EntryType as ScopeEntryType; use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -31,6 +21,16 @@ use crate::stdlib::{ vec, vec::Vec, }; +use crate::syntax::CustomSyntax; +use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream}; +use crate::utils::{get_hasher, StraightHasherBuilder}; +use crate::{ + calc_script_fn_hash, Dynamic, Engine, FnAccess, ImmutableString, LexError, ParseError, + ParseErrorType, Position, Scope, StaticVec, AST, NO_POS, +}; + +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; type PERR = ParseErrorType; @@ -148,10 +148,10 @@ impl<'e> ParseState<'e> { } /// Get an interned string, creating one if it is not yet interned. - pub fn get_interned_string(&mut self, text: S) -> ImmutableString - where - S: AsRef + Into, - { + pub fn get_interned_string( + &mut self, + text: impl AsRef + Into, + ) -> ImmutableString { #[allow(clippy::map_entry)] if !self.strings.contains_key(text.as_ref()) { let value: ImmutableString = text.into(); @@ -1418,7 +1418,9 @@ fn make_dot_expr( } // lhs.Fn() or lhs.eval() (_, Expr::FnCall(x, pos)) - if x.args.len() == 0 && [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL].contains(&x.name.as_ref()) => + if x.args.len() == 0 + && [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL] + .contains(&x.name.as_ref()) => { return Err(PERR::BadInput(LexError::ImproperSymbol(format!( "'{}' should not be called in method style. Try {}(...);", @@ -2369,9 +2371,9 @@ fn parse_stmt( Token::Fn | Token::Private => { let access = if matches!(token, Token::Private) { eat_token(input, Token::Private); - crate::FnAccess::Private + FnAccess::Private } else { - crate::FnAccess::Public + FnAccess::Public }; match input.next().unwrap() { @@ -2552,7 +2554,7 @@ fn parse_fn( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, - access: crate::FnAccess, + access: FnAccess, mut settings: ParseSettings, ) -> Result { #[cfg(not(feature = "unchecked"))] @@ -2664,11 +2666,13 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: P args.push(Expr::Variable(Box::new((None, None, 0, x.clone().into())))); }); - let hash = calc_script_fn_hash(empty(), crate::engine::KEYWORD_FN_PTR_CURRY, num_externals + 1); + let curry_func = crate::engine::KEYWORD_FN_PTR_CURRY; + + let hash = calc_script_fn_hash(empty(), curry_func, num_externals + 1); let expr = Expr::FnCall( Box::new(FnCallExpr { - name: crate::engine::KEYWORD_FN_PTR_CURRY.into(), + name: curry_func.into(), hash, args, ..Default::default() @@ -2785,7 +2789,7 @@ fn parse_anon_fn( // Define the function let script = ScriptFnDef { name: fn_name.clone(), - access: crate::FnAccess::Public, + access: FnAccess::Public, params, #[cfg(not(feature = "no_closure"))] externals: Default::default(), diff --git a/src/plugin.rs b/src/plugin.rs index 7bce9b5c..66783bbd 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,15 +1,11 @@ //! Module defining macros for developing _plugins_. -pub use crate::ast::FnAccess; -pub use crate::dynamic::Dynamic; -pub use crate::engine::Engine; -pub use crate::fn_native::{CallableFunction, FnCallArgs, NativeCallContext}; -pub use crate::fn_register::{RegisterFn, RegisterResultFn}; -pub use crate::module::Module; -pub use crate::result::EvalAltResult; -pub use crate::utils::ImmutableString; - +pub use crate::fn_native::{CallableFunction, FnCallArgs}; pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec}; +pub use crate::{ + Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module, + NativeCallContext, RegisterFn, RegisterResultFn, +}; #[cfg(not(features = "no_module"))] pub use rhai_codegen::*; diff --git a/src/result.rs b/src/result.rs index 7404fa2f..96450c6d 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,17 +1,12 @@ //! Module containing error definitions for the evaluation process. -use crate::dynamic::Dynamic; -use crate::parse_error::ParseErrorType; -use crate::token::{Position, NO_POS}; -use crate::utils::ImmutableString; -use crate::INT; - use crate::stdlib::{ boxed::Box, error::Error, fmt, string::{String, ToString}, }; +use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT, NO_POS}; /// Evaluation result. /// diff --git a/src/scope.rs b/src/scope.rs index 3152a27a..07b8cd11 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,9 +1,8 @@ //! Module that defines the `Scope` type representing a function call-stack scope. -use crate::dynamic::{Dynamic, Variant}; -use crate::StaticVec; - +use crate::dynamic::Variant; use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec}; +use crate::{Dynamic, StaticVec}; /// Type of an entry in the Scope. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] diff --git a/src/serde_impl/de.rs b/src/serde_impl/de.rs index 8c17824a..a259377a 100644 --- a/src/serde_impl/de.rs +++ b/src/serde_impl/de.rs @@ -1,12 +1,9 @@ //! Implement deserialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). use super::str::ImmutableStringDeserializer; -use crate::dynamic::{Dynamic, Union}; -use crate::parse_error::{LexError, ParseErrorType}; -use crate::result::EvalAltResult; -use crate::token::NO_POS; -use crate::utils::ImmutableString; - +use crate::dynamic::Union; +use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString}; +use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, ParseErrorType, NO_POS}; use serde::de::{ DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor, }; @@ -18,8 +15,6 @@ use crate::Array; #[cfg(not(feature = "no_object"))] use crate::Map; -use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString}; - /// Deserializer for `Dynamic` which is kept as a reference. /// /// The reference is necessary because the deserialized type may hold references diff --git a/src/serde_impl/ser.rs b/src/serde_impl/ser.rs index 04cc03fd..97ef4bfb 100644 --- a/src/serde_impl/ser.rs +++ b/src/serde_impl/ser.rs @@ -1,22 +1,18 @@ //! Implement serialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). -use crate::dynamic::Dynamic; -use crate::result::EvalAltResult; -use crate::token::NO_POS; - -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - +use crate::stdlib::{boxed::Box, fmt, string::ToString}; +use crate::{Dynamic, EvalAltResult, NO_POS}; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, Serializer, }; use serde::Serialize; -use crate::stdlib::{boxed::Box, fmt, string::ToString}; +#[cfg(not(feature = "no_index"))] +use crate::Array; + +#[cfg(not(feature = "no_object"))] +use crate::Map; /// Serializer for `Dynamic` which is kept as a reference. pub struct DynamicSerializer { diff --git a/src/serde_impl/str.rs b/src/serde_impl/str.rs index c499ad49..4c98966b 100644 --- a/src/serde_impl/str.rs +++ b/src/serde_impl/str.rs @@ -1,12 +1,8 @@ //! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde). -use crate::result::EvalAltResult; -use crate::token::NO_POS; -use crate::utils::ImmutableString; - -use serde::de::{Deserializer, Visitor}; - use crate::stdlib::{any::type_name, boxed::Box}; +use crate::{EvalAltResult, ImmutableString, NO_POS}; +use serde::de::{Deserializer, Visitor}; /// Deserializer for `ImmutableString`. pub struct ImmutableStringDeserializer<'a> { diff --git a/src/syntax.rs b/src/syntax.rs index a7214eec..77527fab 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,20 +1,18 @@ //! Module implementing custom syntax for `Engine`. use crate::ast::Expr; -use crate::dynamic::Dynamic; -use crate::engine::{Engine, EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; -use crate::fn_native::{SendSync, Shared}; -use crate::parse_error::{LexError, ParseError}; -use crate::result::EvalAltResult; -use crate::token::{is_valid_identifier, Position, Token, NO_POS}; -use crate::utils::ImmutableString; -use crate::StaticVec; - +use crate::engine::{EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; +use crate::fn_native::SendSync; use crate::stdlib::{ boxed::Box, format, string::{String, ToString}, }; +use crate::token::{is_valid_identifier, Token}; +use crate::{ + Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseError, Position, Shared, + StaticVec, NO_POS, +}; /// A general expression evaluation trait object. #[cfg(not(feature = "sync"))] diff --git a/src/token.rs b/src/token.rs index ba12ff22..cdf48dd4 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1,17 +1,9 @@ //! Main module defining the lexer and parser. use crate::engine::{ - Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, + KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; - -use crate::parse_error::LexError; -use crate::StaticVec; -use crate::INT; - -#[cfg(not(feature = "no_float"))] -use crate::FLOAT; - use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -20,6 +12,10 @@ use crate::stdlib::{ str::{Chars, FromStr}, string::{String, ToString}, }; +use crate::{Engine, LexError, StaticVec, INT}; + +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; type LERR = LexError; diff --git a/src/unsafe.rs b/src/unsafe.rs index ed859735..eca454fa 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -1,7 +1,6 @@ //! A helper module containing unsafe utility functions. use crate::dynamic::Variant; - use crate::stdlib::{ any::{Any, TypeId}, boxed::Box, diff --git a/src/utils.rs b/src/utils.rs index 672fb4ec..ab9c661d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,6 @@ //! Module containing various utility types and functions. -use crate::fn_native::{shared_make_mut, shared_take, Shared}; - +use crate::fn_native::{shared_make_mut, shared_take}; use crate::stdlib::{ any::TypeId, borrow::Borrow, @@ -14,12 +13,7 @@ use crate::stdlib::{ str::FromStr, string::{String, ToString}, }; - -#[cfg(not(feature = "no_std"))] -use crate::stdlib::collections::hash_map::DefaultHasher; - -#[cfg(feature = "no_std")] -use ahash::AHasher; +use crate::Shared; /// A hasher that only takes one single `u64` and returns it as a hash key. /// @@ -95,9 +89,9 @@ pub fn calc_script_fn_hash<'a>( /// Create an instance of the default hasher. pub fn get_hasher() -> impl Hasher { #[cfg(feature = "no_std")] - let s: AHasher = Default::default(); + let s: ahash::AHasher = Default::default(); #[cfg(not(feature = "no_std"))] - let s = DefaultHasher::new(); + let s = crate::stdlib::collections::hash_map::DefaultHasher::new(); s } @@ -111,15 +105,17 @@ pub fn get_hasher() -> impl Hasher { /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. fn calc_fn_hash<'a>( - modules: impl Iterator, + mut modules: impl Iterator, fn_name: &str, num: Option, params: impl Iterator, ) -> u64 { let s = &mut get_hasher(); + // Hash a boolean indicating whether the hash is namespace-qualified. + modules.next().is_some().hash(s); // We always skip the first module - modules.skip(1).for_each(|m| m.hash(s)); + modules.for_each(|m| m.hash(s)); s.write(fn_name.as_bytes()); if let Some(num) = num { s.write_usize(num); diff --git a/tests/closures.rs b/tests/closures.rs index 1d80f46e..fd5f6c56 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -267,7 +267,7 @@ fn test_closures_external() -> Result<(), Box> { let fn_ptr = engine.eval_ast::(&ast)?; // Get rid of the script, retaining only functions - ast.retain_functions(|_, _, _| true); + ast.retain_functions(|_, _, _, _| true); // Closure 'f' captures: the engine, the AST, and the curried function pointer let f = move |x: INT| fn_ptr.call_dynamic((&engine, &[ast.as_ref()]).into(), None, [x.into()]); diff --git a/tests/modules.rs b/tests/modules.rs index bd4f7730..de8e4de3 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -1,7 +1,7 @@ #![cfg(not(feature = "no_module"))] use rhai::{ - module_resolvers::StaticModuleResolver, Dynamic, Engine, EvalAltResult, ImmutableString, - Module, ParseError, ParseErrorType, Scope, INT, + module_resolvers::StaticModuleResolver, Dynamic, Engine, EvalAltResult, FnNamespace, + ImmutableString, Module, ParseError, ParseErrorType, Scope, INT, }; #[test] @@ -23,7 +23,8 @@ fn test_module_sub_module() -> Result<(), Box> { sub_module2.set_var("answer", 41 as INT); let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1)); - sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1)); + let hash_super_inc = sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1)); + sub_module2.set_fn_namespace(hash_super_inc, FnNamespace::Global); sub_module.set_sub_module("universe", sub_module2); module.set_sub_module("life", sub_module); @@ -56,16 +57,10 @@ fn test_module_sub_module() -> Result<(), Box> { assert!(engine .eval::("inc(question::life::universe::answer)") .is_err()); - - #[cfg(not(feature = "no_object"))] assert_eq!( engine.eval::("super_inc(question::life::universe::answer)")?, 42 ); - #[cfg(feature = "no_object")] - assert!(engine - .eval::("super_inc(question::life::universe::answer)") - .is_err()); Ok(()) } diff --git a/tests/plugins.rs b/tests/plugins.rs index 84da570c..e3e8d1a1 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -1,6 +1,5 @@ #![cfg(not(any(feature = "no_index", feature = "no_module")))] -use rhai::module_resolvers::StaticModuleResolver; use rhai::plugin::*; use rhai::{Engine, EvalAltResult, INT}; @@ -96,14 +95,9 @@ fn test_plugins_package() -> Result<(), Box> { "6 kitties" ); - let mut resolver = StaticModuleResolver::new(); - resolver.insert("test", exported_module!(test::special_array_package)); + engine.register_module("test", exported_module!(test::special_array_package)); - engine.set_module_resolver(Some(resolver)); - assert_eq!( - engine.eval::(r#"import "test" as test; test::MYSTIC_NUMBER"#)?, - 42 - ); + assert_eq!(engine.eval::("test::MYSTIC_NUMBER")?, 42); Ok(()) }