diff --git a/Cargo.toml b/Cargo.toml index 95e54ab9..6ee58d61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "rhai" -version = "0.19.9" +version = "0.19.10" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" diff --git a/RELEASES.md b/RELEASES.md index bd6357b2..d377aa7c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,21 @@ Rhai Release Notes ================== +Version 0.19.10 +=============== + +Breaking changes +---------------- + +* The error variant `EvalAltResult::ErrorInFunctionCall` has a new parameter holding the _source_ of the function. + +Enhancements +------------ + +* Source information is provided when there is an error within a call to a function defined in another module. +* Source information is provided to the `NativeCallContext` for native Rust functions. + + Version 0.19.9 ============== diff --git a/doc/src/context.json b/doc/src/context.json index 52bebcc6..d19c1ac0 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -1,5 +1,5 @@ { - "version": "0.19.9", + "version": "0.19.10", "repoHome": "https://github.com/jonathandturner/rhai/blob/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master", "rootUrl": "", diff --git a/doc/src/engine/custom-syntax.md b/doc/src/engine/custom-syntax.md index 97747072..5c27142a 100644 --- a/doc/src/engine/custom-syntax.md +++ b/doc/src/engine/custom-syntax.md @@ -116,18 +116,20 @@ The function signature of an implementation is: where: -| Parameter | Type | Description | -| -------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------- | -| `context` | `&mut EvalContext` | mutable reference to the current evaluation _context_ | -| • `scope()` | `&Scope` | reference to the current [`Scope`] | -| • `scope_mut()` | `&mut Scope` | mutable reference to the current [`Scope`]; variables can be added to/removed from it | -| • `engine()` | `&Engine` | reference to the current [`Engine`] | -| • `source()` | `Option<&str>` | reference to the current source, if any | -| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements | -| • `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | -| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any | -| • `call_level()` | `usize` | the current nesting level of function calls | -| `inputs` | `&[Expression]` | a list of input expression trees | +| Parameter | Type | Description | +| -------------------------- | :-------------------------------------: | ---------------------------------------------------------------------------------------------------------------------- | +| `context` | `&mut EvalContext` | mutable reference to the current evaluation _context_ | +| • `scope()` | `&Scope` | reference to the current [`Scope`] | +| • `scope_mut()` | `&mut &mut Scope` | mutable reference to the current [`Scope`]; variables can be added to/removed from it | +| • `engine()` | `&Engine` | reference to the current [`Engine`] | +| • `source()` | `Option<&str>` | reference to the current source, if any | +| • `iter_imports()` | `impl Iterator` | iterator of the current stack of [modules] imported via `import` statements | +| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature | +| • `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| • `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature | +| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any | +| • `call_level()` | `usize` | the current nesting level of function calls | +| `inputs` | `&[Expression]` | a list of input expression trees | ### Return Value diff --git a/doc/src/engine/var.md b/doc/src/engine/var.md index 5fe79881..c0fb5579 100644 --- a/doc/src/engine/var.md +++ b/doc/src/engine/var.md @@ -67,18 +67,20 @@ The function signature passed to `Engine::on_var` takes the following form: where: -| Parameter | Type | Description | -| -------------------------- | :-----------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | `&str` | variable name | -| `index` | `usize` | an offset from the bottom of the current [`Scope`] that the variable is supposed to reside.
Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`.
If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. | -| `context` | `&EvalContext` | reference to the current evaluation _context_ | -| • `scope()` | `&Scope` | reference to the current [`Scope`] | -| • `engine()` | `&Engine` | reference to the current [`Engine`] | -| • `source()` | `Option<&str>` | reference to the current source, if any | -| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements | -| • `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | -| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any | -| • `call_level()` | `usize` | the current nesting level of function calls | +| Parameter | Type | Description | +| -------------------------- | :-------------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | `&str` | variable name | +| `index` | `usize` | an offset from the bottom of the current [`Scope`] that the variable is supposed to reside.
Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`.
If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. | +| `context` | `&EvalContext` | reference to the current evaluation _context_ | +| • `scope()` | `&Scope` | reference to the current [`Scope`] | +| • `engine()` | `&Engine` | reference to the current [`Engine`] | +| • `source()` | `Option<&str>` | reference to the current source, if any | +| • `iter_imports()` | `impl Iterator` | iterator of the current stack of [modules] imported via `import` statements | +| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature | +| • `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| • `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature | +| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any | +| • `call_level()` | `usize` | the current nesting level of function calls | ### Return Value diff --git a/doc/src/language/fn-ptr.md b/doc/src/language/fn-ptr.md index 16c37e08..4717504a 100644 --- a/doc/src/language/fn-ptr.md +++ b/doc/src/language/fn-ptr.md @@ -225,12 +225,14 @@ engine.register_raw_fn("super_call", `FnPtr::call_dynamic` takes a parameter of type `NativeCallContext` which holds the _native call context_ of the particular call to a registered Rust function. It is a type that exposes the following: -| Field | Type | Description | -| ------------------- | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | -| `source()` | `Option<&str>` | reference to the current source, if any | -| `imports()` | `Option<&Imports>` | reference to the current stack of [modules] imported via `import` statements (if any) | -| `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| Field | Type | Description | +| ------------------- | :-------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | +| `source()` | `Option<&str>` | reference to the current source, if any | +| `iter_imports()` | `impl Iterator` | iterator of the current stack of [modules] imported via `import` statements | +| `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature | +| `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature | This type is normally provided by the [`Engine`] (e.g. when using [`Engine::register_fn_raw`](../rust/register-raw.md)). diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md index 980c4799..d7f9917d 100644 --- a/doc/src/language/functions.md +++ b/doc/src/language/functions.md @@ -149,14 +149,18 @@ change(); // <- error: `this` is unbound `is_def_fn` ----------- -Use `is_def_fn` to detect if a Rhai function is defined (and therefore callable), based on its name -and the number of parameters. +Use `is_def_fn` to detect if a Rhai function is defined (and therefore callable), +based on its name and the number of parameters. ```rust fn foo(x) { x + 1 } is_def_fn("foo", 1) == true; +is_def_fn("foo", 0) == false; + +is_def_fn("foo", 2) == false; + is_def_fn("bar", 1) == false; ``` diff --git a/doc/src/plugins/function.md b/doc/src/plugins/function.md index fad53a03..8f7b3f46 100644 --- a/doc/src/plugins/function.md +++ b/doc/src/plugins/function.md @@ -86,12 +86,14 @@ specially by the plugins system. `NativeCallContext` is a type that encapsulates the current _native call context_ and exposes the following: -| Field | Type | Description | -| ------------------- | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | -| `source()` | `Option<&str>` | reference to the current source, if any | -| `imports()` | `Option<&Imports>` | reference to the current stack of [modules] imported via `import` statements (if any) | -| `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| Field | Type | Description | +| ------------------- | :-------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | +| `source()` | `Option<&str>` | reference to the current source, if any | +| `iter_imports()` | `impl Iterator` | iterator of the current stack of [modules] imported via `import` statements | +| `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature | +| `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature | This first parameter, if exists, will be stripped before all other processing. It is _virtual_. Most importantly, it does _not_ count as a parameter to the function and there is no need to provide diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index d8ebbd41..c147809f 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -399,12 +399,14 @@ specially by the plugins system. `NativeCallContext` is a type that encapsulates the current _native call context_ and exposes the following: -| Field | Type | Description | -| ------------------- | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | -| `source()` | `Option<&str>` | reference to the current source, if any | -| `imports()` | `Option<&Imports>` | reference to the current stack of [modules] imported via `import` statements (if any) | -| `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| Field | Type | Description | +| ------------------- | :-------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | +| `source()` | `Option<&str>` | reference to the current source, if any | +| `iter_imports()` | `impl Iterator` | iterator of the current stack of [modules] imported via `import` statements | +| `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature | +| `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature | This first parameter, if exists, will be stripped before all other processing. It is _virtual_. Most importantly, it does _not_ count as a parameter to the function and there is no need to provide diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index c2b094e7..7b7566f7 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -65,15 +65,17 @@ The function signature passed to `Engine::register_raw_fn` takes the following f where: -| Parameter | Type | Description | -| -------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `T` | `impl Clone` | return type of the function | -| `context` | `NativeCallContext` | the current _native call context_ | -| • `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | -| • `source()` | `Option<&str>` | reference to the current source, if any | -| • `imports()` | `Option<&Imports>` | reference to the current stack of [modules] imported via `import` statements (if any) | -| • `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | -| `args` | `&mut [&mut Dynamic]` | a slice containing `&mut` references to [`Dynamic`] values.
The slice is guaranteed to contain enough arguments _of the correct types_. | +| Parameter | Type | Description | +| -------------------------- | :-------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `T` | `impl Clone` | return type of the function | +| `context` | `NativeCallContext` | the current _native call context_ | +| • `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. | +| • `source()` | `Option<&str>` | reference to the current source, if any | +| • `iter_imports()` | `impl Iterator` | iterator of the current stack of [modules] imported via `import` statements | +| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature | +| • `iter_namespaces()` | `impl Iterator` | iterator of the namespaces (as [modules]) containing all script-defined functions | +| • `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature | +| `args` | `&mut [&mut Dynamic]` | a slice containing `&mut` references to [`Dynamic`] values.
The slice is guaranteed to contain enough arguments _of the correct types_. | ### Return value diff --git a/doc/src/start/bin.md b/doc/src/start/bin.md index 9e180a37..a053315d 100644 --- a/doc/src/start/bin.md +++ b/doc/src/start/bin.md @@ -10,8 +10,43 @@ A number of Rhai-driven utility programs can be found in the `src/bin` directory | [`rhai-repl`]({{repoTree}}/examples/rhai-repl.rs) | a simple REPL, interactively evaluate statements from stdin | | [`rhai-run`]({{repoTree}}/examples/rhai-run.rs) | runs each filename passed to it as a Rhai script | -`rhai-repl` is particularly useful – it allows one to interactively try out Rhai's -language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop). + +`rhai-repl` – The Rhai REPL Tool +------------------------------------- + +`rhai-repl` is a particularly useful utility program – it allows one to interactively +try out Rhai's language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop). + +Filenames passed to it as command line arguments are run and loaded before the REPL starts. + +### Example + +The following command first runs three scripts – `init1.rhai`, `init2.rhai` and +`init3.rhai` – loading the functions defined in each script into the _global_ +namespace. + +Then it enters an REPL, which can call the above functions freely. + +```bash +rhai-repl init1.rhai init2.rhai init3.rhai +``` + + +`rhai-run` – The Rhai Runner +--------------------------------- + +Use `rhai-run` to run Rhai scripts. + +Filenames passed to it as command line arguments are run in sequence. + +### Example + +The following command runs the scripts `script1.rhai`, `script2.rhai` and `script3.rhai` +in order. + +```bash +rhai-run script1.rhai script2.rhai script3.rhai +``` Running a Utility Program diff --git a/no_std/no_std_test/Cargo.toml b/no_std/no_std_test/Cargo.toml index cd151bad..1db56f57 100644 --- a/no_std/no_std_test/Cargo.toml +++ b/no_std/no_std_test/Cargo.toml @@ -12,7 +12,7 @@ homepage = "https://github.com/jonathandturner/rhai/tree/no_std/no_std_test" repository = "https://github.com/jonathandturner/rhai" [dependencies] -rhai = { path = "../../", features = [ "no_std", "unchecked", "only_i32", "f32_float", "no_module" ], default_features = false } +rhai = { path = "../../", features = [ "no_std" ], default_features = false } wee_alloc = { version = "0.4.5", default_features = false } [profile.dev] diff --git a/src/ast.rs b/src/ast.rs index e6694324..fafc4394 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -62,7 +62,7 @@ impl FnAccess { /// _(INTERNALS)_ A type containing information on a scripted function. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Clone)] @@ -224,7 +224,11 @@ impl AST { /// Set the source. #[inline(always)] pub fn set_source>(&mut self, source: Option) { - self.source = source.map(|s| s.into()) + self.source = source.map(|s| s.into()); + + if let Some(module) = Shared::get_mut(&mut self.functions) { + module.set_id(self.source.clone()); + } } /// Get the statements. #[cfg(not(feature = "internals"))] @@ -271,6 +275,8 @@ impl AST { /// No statements are cloned. /// /// This operation is cheap because functions are shared. + /// + /// Not available under [`no_function`]. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clone_functions_only(&self) -> Self { @@ -280,6 +286,8 @@ impl AST { /// No statements are cloned. /// /// This operation is cheap because functions are shared. + /// + /// Not available under [`no_function`]. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clone_functions_only_filtered( @@ -564,6 +572,8 @@ impl AST { } /// Filter out the functions, retaining only some based on a filter predicate. /// + /// Not available under [`no_function`]. + /// /// # Example /// /// ``` @@ -597,6 +607,8 @@ impl AST { self } /// Iterate through all function definitions. + /// + /// Not available under [`no_function`]. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn iter_functions<'a>(&'a self) -> impl Iterator + 'a { @@ -605,6 +617,8 @@ impl AST { .map(|(_, _, _, _, fn_def)| fn_def.into()) } /// Clear all function definitions in the [`AST`]. + /// + /// Not available under [`no_function`]. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clear_functions(&mut self) { @@ -650,7 +664,7 @@ impl AsRef for AST { /// _(INTERNALS)_ An identifier containing an [immutable string][ImmutableString] name and a [position][Position]. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Clone, Eq, PartialEq, Hash)] @@ -671,7 +685,7 @@ impl fmt::Debug for Ident { /// _(INTERNALS)_ A type encapsulating the mode of a `return`/`throw` statement. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] @@ -685,7 +699,7 @@ pub enum ReturnType { /// _(INTERNALS)_ A statement. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Clone)] @@ -882,7 +896,7 @@ impl Stmt { /// _(INTERNALS)_ A custom syntax definition. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Clone)] @@ -909,7 +923,7 @@ impl fmt::Debug for CustomExpr { /// _(INTERNALS)_ A binary expression. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Clone)] @@ -923,7 +937,7 @@ pub struct BinaryExpr { /// _(INTERNALS)_ A function call. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Clone, Default)] @@ -948,7 +962,7 @@ pub struct FnCallExpr { /// _(INTERNALS)_ An expression sub-tree. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Clone)] @@ -1028,7 +1042,7 @@ impl Expr { AccessMode::ReadOnly, )), Self::BoolConstant(x, _) => (*x).into(), - Self::Unit(_) => ().into(), + Self::Unit(_) => Dynamic::UNIT, #[cfg(not(feature = "no_index"))] Self::Array(x, _) if self.is_constant() => { @@ -1245,6 +1259,7 @@ mod tests { assert_eq!(size_of::>(), 16); assert_eq!(size_of::(), 32); assert_eq!(size_of::>(), 32); + assert_eq!(size_of::(), 32); assert_eq!(size_of::(), 48); assert_eq!(size_of::(), 56); assert_eq!(size_of::(), 16); diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 54565405..d5d5c406 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -92,8 +92,10 @@ fn main() { let module = match engine .compile(&contents) .map_err(|err| err.into()) - .and_then(|ast| Module::eval_ast_as_new(Default::default(), &ast, &engine)) - { + .and_then(|mut ast| { + ast.set_source(Some(&filename)); + Module::eval_ast_as_new(Default::default(), &ast, &engine) + }) { Err(err) => { eprintln!("{:=<1$}", "", filename.len()); eprintln!("{}", filename); diff --git a/src/dynamic.rs b/src/dynamic.rs index 08e473ae..47507ff6 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -9,7 +9,7 @@ use crate::stdlib::{ hash::{Hash, Hasher}, mem, ops::{Deref, DerefMut}, - string::{String, ToString}, + string::String, }; use crate::{FnPtr, ImmutableString, INT}; @@ -132,7 +132,7 @@ pub enum AccessMode { } impl AccessMode { - /// Is the access type [`ReadOnly`]? + /// Is the access type `ReadOnly`? #[inline(always)] pub fn is_read_only(self) -> bool { match self { @@ -183,7 +183,7 @@ enum DynamicReadLockInner<'d, T: Variant + Clone> { /// A simple reference to a non-shared value. Reference(&'d T), - /// A read guard to a shared `RefCell`. + /// A read guard to a shared [`RefCell`][std::cell::RefCell]. #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] Guard(crate::stdlib::cell::Ref<'d, Dynamic>), @@ -220,7 +220,7 @@ enum DynamicWriteLockInner<'d, T: Variant + Clone> { /// A simple mutable reference to a non-shared value. Reference(&'d mut T), - /// A write guard to a shared `RefCell`. + /// A write guard to a shared [`RefCell`][std::cell::RefCell]. #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] Guard(crate::stdlib::cell::RefMut<'d, Dynamic>), @@ -258,7 +258,7 @@ impl<'d, T: Variant + Clone> DerefMut for DynamicWriteLock<'d, T> { impl Dynamic { /// Does this [`Dynamic`] hold a variant data type - /// instead of one of the support system primitive types? + /// instead of one of the supported system primitive types? #[inline(always)] pub fn is_variant(&self) -> bool { match self.0 { @@ -266,8 +266,7 @@ impl Dynamic { _ => false, } } - /// Does this [`Dynamic`] hold a shared data type - /// instead of one of the supported system primitive types? + /// Is the value held by this [`Dynamic`] shared? #[inline(always)] pub fn is_shared(&self) -> bool { match self.0 { @@ -279,7 +278,7 @@ impl Dynamic { /// Is the value held by this [`Dynamic`] a particular type? /// /// If the [`Dynamic`] is a shared variant checking is performed on - /// top of it's internal value. + /// top of its internal value. #[inline(always)] pub fn is(&self) -> bool { let mut target_type_id = TypeId::of::(); @@ -508,7 +507,7 @@ impl fmt::Debug for Dynamic { impl Clone for Dynamic { /// Clone the [`Dynamic`] value. /// - /// ## WARNING + /// # WARNING /// /// The cloned copy is marked read-write even if the original is read-only. fn clone(&self) -> Self { @@ -618,7 +617,7 @@ impl Dynamic { /// /// Constant [`Dynamic`] values are read-only. If a [`&mut Dynamic`][Dynamic] to such a constant /// is passed to a Rust function, the function can use this information to return an error of - /// [`EvalAltResult::ErrorAssignmentToConstant`][crate::EvalAltResult::ErrorAssignmentToConstant] + /// [`ErrorAssignmentToConstant`][crate::EvalAltResult::ErrorAssignmentToConstant] /// if its value is going to be modified. This safe-guards constant values from being modified /// from within Rust functions. pub fn is_read_only(&self) -> bool { @@ -707,7 +706,7 @@ impl Dynamic { if TypeId::of::() == TypeId::of::<&str>() { return ::downcast_ref::<&str>(&value) .unwrap() - .to_string() + .deref() .into(); } if TypeId::of::() == TypeId::of::<()>() { diff --git a/src/engine.rs b/src/engine.rs index 972f135a..05d67062 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -45,13 +45,13 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical /// _(INTERNALS)_ A stack of imported [modules][Module]. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. // // # Implementation Notes // -// We cannot use &str or Cow here because `eval` may load a [module][Module] and +// We cannot use Cow here because `eval` may load a [module][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)] @@ -96,7 +96,7 @@ impl Imports { /// Get an iterator to this stack of imported [modules][Module] in reverse order. #[allow(dead_code)] #[inline(always)] - pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + pub fn iter(&self) -> impl Iterator { self.0 .iter() .rev() @@ -105,9 +105,7 @@ impl Imports { /// Get an iterator to this stack of imported [modules][Module] in reverse order. #[allow(dead_code)] #[inline(always)] - pub(crate) fn iter_raw<'a>( - &'a self, - ) -> impl Iterator)> + 'a { + pub(crate) fn iter_raw(&self) -> impl Iterator)> { self.0.iter().rev().map(|(n, m)| (n, m)) } /// Get a consuming iterator to this stack of imported [modules][Module] in reverse order. @@ -128,11 +126,14 @@ impl Imports { } /// Get specified function via its hash key. #[inline(always)] - pub fn get_fn(&self, hash: NonZeroU64) -> Option<&CallableFunction> { + pub fn get_fn( + &self, + hash: NonZeroU64, + ) -> Option<(&CallableFunction, Option<&ImmutableString>)> { self.0 .iter() .rev() - .find_map(|(_, m)| m.get_qualified_fn(hash)) + .find_map(|(_, m)| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of imported [modules][Module]? #[allow(dead_code)] @@ -216,55 +217,62 @@ pub const FN_ANONYMOUS: &str = "anon$"; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] pub const OP_EQUALS: &str = "=="; -/// A type specifying the method of chaining. +/// Method of chaining. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum ChainType { - None, + /// Not a chaining type. + NonChaining, + /// Indexing. Index, + /// Dotting. Dot, } +/// Value of a chaining argument. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -#[derive(Debug, Clone)] -pub enum IndexChainValue { - None, +#[derive(Debug, Clone, Hash)] +pub enum ChainArgument { + /// Dot-property access. + Property, + /// Arguments to a dot-function call. FnCallArgs(StaticVec), - Value(Dynamic), + /// Index value. + IndexValue(Dynamic), } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -impl IndexChainValue { +impl ChainArgument { /// Return the `Dynamic` value. /// /// # Panics /// - /// Panics if not `IndexChainValue::Value`. + /// Panics if not `ChainArgument::IndexValue`. #[inline(always)] #[cfg(not(feature = "no_index"))] - pub fn as_value(self) -> Dynamic { + pub fn as_index_value(self) -> Dynamic { match self { - Self::None | Self::FnCallArgs(_) => panic!("expecting IndexChainValue::Value"), - Self::Value(value) => value, + Self::Property | Self::FnCallArgs(_) => panic!("expecting ChainArgument::IndexValue"), + Self::IndexValue(value) => value, } } /// Return the `StaticVec` value. /// /// # Panics /// - /// Panics if not `IndexChainValue::FnCallArgs`. + /// Panics if not `ChainArgument::FnCallArgs`. #[inline(always)] #[cfg(not(feature = "no_object"))] pub fn as_fn_call_args(self) -> StaticVec { match self { - Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"), + Self::Property | Self::IndexValue(_) => panic!("expecting ChainArgument::FnCallArgs"), Self::FnCallArgs(value) => value, } } } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -impl From> for IndexChainValue { +impl From> for ChainArgument { #[inline(always)] fn from(value: StaticVec) -> Self { Self::FnCallArgs(value) @@ -272,10 +280,10 @@ impl From> for IndexChainValue { } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -impl From for IndexChainValue { +impl From for ChainArgument { #[inline(always)] fn from(value: Dynamic) -> Self { - Self::Value(value) + Self::IndexValue(value) } } @@ -489,7 +497,7 @@ impl> From for Target<'_> { /// _(INTERNALS)_ A type that holds all the current states of the [`Engine`]. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Clone, Default)] @@ -508,7 +516,11 @@ pub struct State { /// Number of modules loaded. pub modules: usize, /// Cached lookup values for function hashes. - pub functions_cache: HashMap, StraightHasherBuilder>, + pub functions_cache: HashMap< + NonZeroU64, + Option<(CallableFunction, Option)>, + StraightHasherBuilder, + >, } impl State { @@ -522,7 +534,7 @@ impl State { /// _(INTERNALS)_ A type containing all the limits imposed by the [`Engine`]. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[cfg(not(feature = "unchecked"))] @@ -571,17 +583,17 @@ pub struct EvalContext<'e, 'x, 'px: 'x, 'a, 's, 'm, 'pm: 'm, 't, 'pt: 't> { impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> { /// The current [`Engine`]. #[inline(always)] - pub fn engine(&self) -> &'e Engine { + pub fn engine(&self) -> &Engine { self.engine } /// The current source. #[inline(always)] - pub fn source<'z: 's>(&'z self) -> Option<&'s str> { + pub fn source(&self) -> Option<&str> { self.state.source.as_ref().map(|s| s.as_str()) } /// The current [`Scope`]. #[inline(always)] - pub fn scope(&self) -> &Scope<'px> { + pub fn scope(&self) -> &Scope { self.scope } /// Mutable reference to the current [`Scope`]. @@ -589,19 +601,32 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, pub fn scope_mut(&mut self) -> &mut &'x mut Scope<'px> { &mut self.scope } + /// Get an iterator over the current set of modules imported via `import` statements. + #[cfg(not(feature = "no_module"))] + #[inline(always)] + pub fn iter_imports(&self) -> impl Iterator { + self.mods.iter() + } /// _(INTERNALS)_ The current set of modules imported via `import` statements. /// Available under the `internals` feature only. #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[inline(always)] - pub fn imports(&'a self) -> &'a Imports { + pub fn imports(&self) -> &Imports { self.mods } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline(always)] - pub fn iter_namespaces(&self) -> impl Iterator + 'm { + pub fn iter_namespaces(&self) -> impl Iterator { self.lib.iter().cloned() } + /// _(INTERNALS)_ The current set of namespaces containing definitions of all script-defined functions. + /// Available under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline(always)] + pub fn namespaces(&self) -> &[&Module] { + self.lib + } /// The current bound `this` pointer, if any. #[inline(always)] pub fn this_ptr(&self) -> Option<&Dynamic> { @@ -835,7 +860,8 @@ impl Engine { } /// Create a new [`Engine`] with minimal built-in functions. - /// Use the [`register_global_module`][Engine::register_global_module] method to load additional packages of functions. + /// + /// Use [`register_global_module`][Engine::register_global_module] to add packages of functions. #[inline] pub fn new_raw() -> Self { Self { @@ -1006,13 +1032,13 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, rhs: &Expr, - idx_values: &mut StaticVec, + idx_values: &mut StaticVec, chain_type: ChainType, level: usize, new_val: Option<(Dynamic, Position)>, ) -> Result<(Dynamic, bool), Box> { - if chain_type == ChainType::None { - unreachable!("should not be ChainType::None"); + if chain_type == ChainType::NonChaining { + unreachable!("should not be ChainType::NonChaining"); } let is_ref = target.is_ref(); @@ -1020,7 +1046,7 @@ impl Engine { let next_chain = match rhs { Expr::Index(_, _) => ChainType::Index, Expr::Dot(_, _) => ChainType::Dot, - _ => ChainType::None, + _ => ChainType::NonChaining, }; // Pop the last index value @@ -1037,7 +1063,7 @@ impl Engine { // xxx[idx].expr... | xxx[idx][expr]... Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => { let idx_pos = x.lhs.position(); - let idx_val = idx_val.as_value(); + let idx_val = idx_val.as_index_value(); let obj_ptr = &mut self.get_indexed_mut( mods, state, lib, target_val, idx_val, idx_pos, false, is_ref, true, level, @@ -1051,7 +1077,7 @@ impl Engine { } // xxx[rhs] = new_val _ if new_val.is_some() => { - let idx_val = idx_val.as_value(); + let idx_val = idx_val.as_index_value(); let mut idx_val2 = idx_val.clone(); // `call_setter` is introduced to bypass double mutable borrowing of target @@ -1099,7 +1125,7 @@ impl Engine { } // xxx[rhs] _ => { - let idx_val = idx_val.as_value(); + let idx_val = idx_val.as_index_value(); self.get_indexed_mut( mods, state, lib, target_val, idx_val, pos, false, is_ref, true, level, ) @@ -1167,7 +1193,6 @@ impl Engine { None, None, level, ) .map(|(v, _)| (v, true)) - .map_err(|err| err.fill_position(*pos)) } // xxx.id Expr::Property(x) => { @@ -1178,7 +1203,6 @@ impl Engine { None, None, level, ) .map(|(v, _)| (v, false)) - .map_err(|err| err.fill_position(*pos)) } // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::() => { @@ -1229,12 +1253,10 @@ impl Engine { let (getter, setter, Ident { pos, .. }) = p.as_ref(); let arg_values = &mut [target_val, &mut Default::default()]; let args = &mut arg_values[..1]; - let (mut val, updated) = self - .exec_fn_call( - mods, state, lib, getter, None, args, is_ref, true, false, - *pos, None, None, level, - ) - .map_err(|err| err.fill_position(*pos))?; + let (mut val, updated) = self.exec_fn_call( + mods, state, lib, getter, None, args, is_ref, true, false, + *pos, None, None, level, + )?; let val = &mut val; @@ -1267,7 +1289,7 @@ impl Engine { EvalAltResult::ErrorDotExpr(_, _) => { Ok((Dynamic::UNIT, false)) } - _ => Err(err.fill_position(*x_pos)), + _ => Err(err), }, )?; } @@ -1394,7 +1416,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, parent_chain_type: ChainType, - idx_values: &mut StaticVec, + idx_values: &mut StaticVec, size: usize, level: usize, ) -> Result<(), Box> { @@ -1417,7 +1439,7 @@ impl Engine { } Expr::Property(_) if parent_chain_type == ChainType::Dot => { - idx_values.push(IndexChainValue::None) + idx_values.push(ChainArgument::Property) } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), @@ -1427,7 +1449,7 @@ impl Engine { // Evaluate in left-to-right order let lhs_val = match lhs { Expr::Property(_) if parent_chain_type == ChainType::Dot => { - IndexChainValue::None + ChainArgument::Property } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::FnCall(x, _) @@ -2027,15 +2049,16 @@ impl Engine { match self .global_namespace .get_fn(hash_fn, false) + .map(|f| (f, None)) .or_else(|| { self.global_modules .iter() - .find_map(|m| m.get_fn(hash_fn, false)) + .find_map(|m| m.get_fn(hash_fn, false).map(|f| (f, m.id_raw()))) }) .or_else(|| mods.get_fn(hash_fn)) { // op= function registered as method - Some(func) if func.is_method() => { + Some((func, source)) if func.is_method() => { let mut lock_guard; let lhs_ptr_inner; @@ -2049,14 +2072,16 @@ impl Engine { let args = &mut [lhs_ptr_inner, &mut rhs_val]; // Overriding exact implementation + let source = if source.is_none() { + state.source.as_ref() + } else { + source + }; if func.is_plugin_fn() { func.get_plugin_fn() - .call((self, &state.source, &*mods, lib).into(), args)?; + .call((self, source, &*mods, lib).into(), args)?; } else { - func.get_native_fn()( - (self, &state.source, &*mods, lib).into(), - args, - )?; + func.get_native_fn()((self, source, &*mods, lib).into(), args)?; } } // Built-in op-assignment function @@ -2301,7 +2326,7 @@ impl Engine { let result = self .eval_stmt(scope, mods, state, lib, this_ptr, try_body, level) - .map(|_| ().into()); + .map(|_| Dynamic::UNIT); match result { Ok(_) => result, @@ -2363,7 +2388,7 @@ impl Engine { // Empty throw Stmt::Return((ReturnType::Exception, pos), None, _) => { - EvalAltResult::ErrorRuntime(().into(), *pos).into() + EvalAltResult::ErrorRuntime(Dynamic::UNIT, *pos).into() } // Let/const statement @@ -2378,7 +2403,7 @@ impl Engine { self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten() } else { - ().into() + Dynamic::UNIT }; let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() { ( @@ -2596,6 +2621,7 @@ impl Engine { } /// Check if the number of operations stay within limit. + #[inline] pub(crate) fn inc_operations( &self, state: &mut State, diff --git a/src/engine_api.rs b/src/engine_api.rs index ff07e9e8..5f5b4b21 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -35,7 +35,7 @@ fn calc_hash_for_scripts<'a>(scripts: impl IntoIterator) -> impl Engine { /// Register a function of the [`Engine`]. /// - /// ## WARNING - Low Level API + /// # WARNING - Low Level API /// /// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s indicating the actual types of the parameters. /// @@ -88,13 +88,12 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. - /// engine.register_type::(); - /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Use `register_fn` to register methods on the type. - /// engine.register_fn("update", TestStruct::update); + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Use `register_fn` to register methods on the type. + /// .register_fn("update", TestStruct::update); /// /// assert_eq!( /// engine.eval::("let x = new_ts(); x.update(41); x")?, @@ -128,22 +127,19 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. - /// engine.register_type::(); - /// - /// engine.register_fn("new_ts", TestStruct::new); + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new); /// /// assert_eq!( /// engine.eval::("let x = new_ts(); type_of(x)")?, /// "rust_out::TestStruct" /// ); /// - /// // Register the custom type with a name. + /// // Re-register the custom type with a name. /// engine.register_type_with_name::("Hello"); /// - /// // Register methods on the type. - /// engine.register_fn("new_ts", TestStruct::new); - /// /// assert_eq!( /// engine.eval::("let x = new_ts(); type_of(x)")?, /// "Hello" @@ -192,13 +188,12 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. - /// engine.register_type::(); - /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register a getter on a property (notice it doesn't have to be the same name). - /// engine.register_get("xyz", TestStruct::get_field); + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register a getter on a property (notice it doesn't have to be the same name). + /// .register_get("xyz", TestStruct::get_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz")?, 1); /// # Ok(()) @@ -239,13 +234,12 @@ impl Engine { /// # fn main() -> Result<(), Box> { /// let mut engine = Engine::new(); /// - /// // Register the custom type. - /// engine.register_type::(); - /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register a getter on a property (notice it doesn't have to be the same name). - /// engine.register_get_result("xyz", TestStruct::get_field); + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register a getter on a property (notice it doesn't have to be the same name). + /// .register_get_result("xyz", TestStruct::get_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz")?, 1); /// # Ok(()) @@ -281,13 +275,12 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. - /// engine.register_type::(); - /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register a setter on a property (notice it doesn't have to be the same name) - /// engine.register_set("xyz", TestStruct::set_field); + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register a setter on a property (notice it doesn't have to be the same name) + /// .register_set("xyz", TestStruct::set_field); /// /// // Notice that, with a getter, there is no way to get the property value /// assert_eq!( @@ -330,13 +323,12 @@ impl Engine { /// # fn main() -> Result<(), Box> { /// let mut engine = Engine::new(); /// - /// // Register the custom type. - /// engine.register_type::(); - /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register a setter on a property (notice it doesn't have to be the same name) - /// engine.register_set_result("xyz", TestStruct::set_field); + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register a setter on a property (notice it doesn't have to be the same name) + /// .register_set_result("xyz", TestStruct::set_field); /// /// // Notice that, with a getter, there is no way to get the property value /// assert_eq!( @@ -383,14 +375,13 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. - /// engine.register_type::(); - /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register a getter and a setter on a property - /// // (notice it doesn't have to be the same name) - /// engine.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field); + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register both a getter and a setter on a property + /// // (notice it doesn't have to be the same name) + /// .register_get_set("xyz", TestStruct::get_field, TestStruct::set_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz = 42; a.xyz")?, 42); /// # Ok(()) @@ -434,14 +425,14 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. + /// // Register API for the custom type. /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register an indexer. - /// engine.register_indexer_get(TestStruct::get_field); + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_get(TestStruct::get_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a[2]")?, 3); /// # Ok(()) @@ -500,14 +491,14 @@ impl Engine { /// # fn main() -> Result<(), Box> { /// let mut engine = Engine::new(); /// - /// // Register the custom type. + /// // Register API for the custom type. /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register an indexer. - /// engine.register_indexer_get_result(TestStruct::get_field); + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_get_result(TestStruct::get_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a[2]")?, 3); /// # Ok(()) @@ -561,14 +552,14 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. + /// // Register API for the custom type. /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register an indexer. - /// engine.register_indexer_set(TestStruct::set_field); + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_set(TestStruct::set_field); /// /// assert_eq!( /// engine.eval::("let a = new_ts(); a[2] = 42; a")?.fields[2], @@ -628,14 +619,14 @@ impl Engine { /// # fn main() -> Result<(), Box> { /// let mut engine = Engine::new(); /// - /// // Register the custom type. + /// // Register API for the custom type. /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register an indexer. - /// engine.register_indexer_set_result(TestStruct::set_field); + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_set_result(TestStruct::set_field); /// /// assert_eq!( /// engine.eval::("let a = new_ts(); a[2] = 42; a")?.fields[2], @@ -700,14 +691,14 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register the custom type. + /// // Register API for the custom type. /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// - /// engine.register_fn("new_ts", TestStruct::new); - /// - /// // Register an indexer. - /// engine.register_indexer_get_set(TestStruct::get_field, TestStruct::set_field); + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a[2] = 42; a[2]")?, 42); /// # Ok(()) @@ -740,9 +731,9 @@ impl Engine { } /// Register a shared [`Module`] into the global namespace of [`Engine`]. /// - /// ## Deprecated + /// # Deprecated /// - /// Use `register_global_module` instead. + /// Use [`register_global_module`][Engine::register_global_module] instead. #[inline(always)] #[deprecated = "use `register_global_module` instead"] pub fn load_package(&mut self, module: impl Into>) -> &mut Self { @@ -768,13 +759,12 @@ impl Engine { /// /// let module: Shared = module.into(); /// - /// // Register the module as a fixed sub-module - /// engine.register_static_module("foo::bar::baz", module.clone()); - /// - /// // Multiple registrations to the same partial path is also OK! - /// engine.register_static_module("foo::bar::hello", module.clone()); - /// - /// engine.register_static_module("CalcService", module); + /// engine + /// // Register the module as a fixed sub-module + /// .register_static_module("foo::bar::baz", module.clone()) + /// // Multiple registrations to the same partial path is also OK! + /// .register_static_module("foo::bar::hello", module.clone()) + /// .register_static_module("CalcService", module); /// /// assert_eq!(engine.eval::("foo::bar::baz::calc(41)")?, 42); /// assert_eq!(engine.eval::("foo::bar::hello::calc(41)")?, 42); @@ -830,9 +820,9 @@ impl Engine { /// Register a shared [`Module`] as a static module namespace with the [`Engine`]. /// - /// ## Deprecated + /// # Deprecated /// - /// Use `register_static_module` instead. + /// Use [`register_static_module`][Engine::register_static_module] instead. #[cfg(not(feature = "no_module"))] #[inline(always)] #[deprecated = "use `register_static_module` instead"] @@ -1001,6 +991,8 @@ impl Engine { } /// Compile a script file into an [`AST`], which can be used later for evaluation. /// + /// Not available under `no_std` or `WASM`. + /// /// # Example /// /// ```no_run @@ -1030,6 +1022,8 @@ impl Engine { } /// Compile a script file into an [`AST`] using own scope, which can be used later for evaluation. /// + /// Not available under `no_std` or `WASM`. + /// /// The scope is useful for passing constants into the script for optimization /// when using [`OptimizationLevel::Full`]. /// @@ -1069,12 +1063,13 @@ impl Engine { ) -> Result> { Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) } - /// Parse a JSON string into a map. + /// Parse a JSON string into an [object map][`Map`]. + /// This is a light-weight alternative to using, say, [`serde_json`][https://crates.io/crates/serde_json] to deserialize the JSON. /// - /// The JSON string must be an object hash. It cannot be a simple JavaScript primitive. + /// The JSON string must be an object hash. It cannot be a simple scalar value. /// /// Set `has_null` to `true` in order to map `null` values to `()`. - /// Setting it to `false` will cause a _variable not found_ error during parsing. + /// Setting it to `false` will cause an [`ErrorVariableNotFound`][EvalAltResult::ErrorVariableNotFound] error during parsing. /// /// # JSON With Sub-Objects /// @@ -1083,7 +1078,7 @@ impl Engine { /// Parsing a JSON string with sub-objects will cause a syntax error. /// /// If it is certain that the character `{` never appears in any text string within the JSON object, - /// then globally replace `{` with `#{` before calling this method. + /// which is a valid assumption for many use cases, then globally replace `{` with `#{` before calling this method. /// /// # Example /// @@ -1235,6 +1230,8 @@ impl Engine { } /// Evaluate a script file. /// + /// Not available under `no_std` or `WASM`. + /// /// # Example /// /// ```no_run @@ -1259,6 +1256,8 @@ impl Engine { } /// Evaluate a script file with own scope. /// + /// Not available under `no_std` or `WASM`. + /// /// # Example /// /// ```no_run @@ -1483,6 +1482,8 @@ impl Engine { } /// Evaluate a file, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. + /// + /// Not available under `no_std` or `WASM`. #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] #[inline(always)] @@ -1494,6 +1495,8 @@ impl Engine { } /// Evaluate a file with own scope, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. + /// + /// Not available under `no_std` or `WASM`. #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] #[inline(always)] @@ -1611,7 +1614,7 @@ impl Engine { /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments /// and optionally a value for binding to the `this` pointer. /// - /// ## WARNING + /// # WARNING /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. @@ -1673,7 +1676,7 @@ impl Engine { } /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments. /// - /// ## WARNING + /// # WARNING /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. @@ -1776,12 +1779,12 @@ impl Engine { } /// Provide a callback that will be invoked before each variable access. /// - /// ## Return Value of Callback + /// # Return Value of Callback /// /// Return `Ok(None)` to continue with normal variable access. /// Return `Ok(Some(Dynamic))` as the variable's value. /// - /// ## Errors in Callback + /// # Errors in Callback /// /// Return `Err(...)` if there is an error. /// diff --git a/src/engine_settings.rs b/src/engine_settings.rs index cb60a6d0..7d99c1fa 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -10,7 +10,7 @@ use crate::stdlib::boxed::Box; impl Engine { /// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation. /// - /// Not available under the `no_optimize` feature. + /// Not available under `no_optimize`. #[cfg(not(feature = "no_optimize"))] #[inline(always)] pub fn set_optimization_level( @@ -23,7 +23,7 @@ impl Engine { /// The current optimization level. /// It controls whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation. /// - /// Not available under the `no_optimize` feature. + /// Not available under `no_optimize`. #[cfg(not(feature = "no_optimize"))] #[inline(always)] pub fn optimization_level(&self) -> crate::OptimizationLevel { @@ -37,6 +37,8 @@ impl Engine { } /// Set the maximum levels of function calls allowed for a script in order to avoid /// infinite recursion and stack overflows. + /// + /// Not available under `unchecked` or `no_function`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_function"))] #[inline(always)] @@ -45,6 +47,8 @@ impl Engine { self } /// The maximum levels of function calls allowed for a script. + /// + /// Not available under `unchecked` or `no_function`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_function"))] #[inline(always)] @@ -53,6 +57,8 @@ impl Engine { } /// Set the maximum number of operations allowed for a script to run to avoid /// consuming too much resources (0 for unlimited). + /// + /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn set_max_operations(&mut self, operations: u64) -> &mut Self { @@ -64,12 +70,16 @@ impl Engine { self } /// The maximum number of operations allowed for a script to run (0 for unlimited). + /// + /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_operations(&self) -> u64 { self.limits.max_operations } /// Set the maximum number of imported [modules][crate::Module] allowed for a script. + /// + /// Not available under `unchecked` or `no_module`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_module"))] #[inline(always)] @@ -78,6 +88,8 @@ impl Engine { self } /// The maximum number of imported [modules][crate::Module] allowed for a script. + /// + /// Not available under `unchecked` or `no_module`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_module"))] #[inline(always)] @@ -85,6 +97,8 @@ impl Engine { self.limits.max_modules } /// Set the depth limits for expressions (0 for unlimited). + /// + /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn set_max_expr_depths( @@ -108,12 +122,16 @@ impl Engine { self } /// The depth limit for expressions (0 for unlimited). + /// + /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_expr_depth(&self) -> usize { self.limits.max_expr_depth } /// The depth limit for expressions in functions (0 for unlimited). + /// + /// Not available under `unchecked` or `no_function`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_function"))] #[inline(always)] @@ -121,6 +139,8 @@ impl Engine { self.limits.max_function_expr_depth } /// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited). + /// + /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self { @@ -128,12 +148,16 @@ impl Engine { self } /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). + /// + /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_string_size(&self) -> usize { self.limits.max_string_size } /// Set the maximum length of [arrays][crate::Array] (0 for unlimited). + /// + /// Not available under `unchecked` or `no_index`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_index"))] #[inline(always)] @@ -142,13 +166,17 @@ impl Engine { self } /// The maximum length of [arrays][crate::Array] (0 for unlimited). + /// + /// Not available under `unchecked` or `no_index`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn max_array_size(&self) -> usize { self.limits.max_array_size } - /// Set the maximum length of [object maps][crate::Map] (0 for unlimited). + /// Set the maximum size of [object maps][crate::Map] (0 for unlimited). + /// + /// Not available under `unchecked` or `no_object`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_object"))] #[inline(always)] @@ -156,7 +184,9 @@ impl Engine { self.limits.max_map_size = if max_size == usize::MAX { 0 } else { max_size }; self } - /// The maximum length of [object maps][crate::Map] (0 for unlimited). + /// The maximum size of [object maps][crate::Map] (0 for unlimited). + /// + /// Not available under `unchecked` or `no_object`. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_object"))] #[inline(always)] @@ -165,7 +195,7 @@ impl Engine { } /// Set the module resolution service used by the [`Engine`]. /// - /// Not available under the `no_module` feature. + /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] #[inline(always)] pub fn set_module_resolver( diff --git a/src/fn_call.rs b/src/fn_call.rs index 38bdeff6..78879f50 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -155,7 +155,7 @@ pub fn ensure_no_data_race( impl Engine { /// Call a native Rust function registered with the [`Engine`]. /// - /// ## WARNING + /// # WARNING /// /// Function call arguments be _consumed_ when the function requires them to be passed by value. /// All function arguments not in the first position are always passed by value and thus consumed. @@ -175,32 +175,31 @@ impl Engine { ) -> Result<(Dynamic, bool), Box> { self.inc_operations(state, pos)?; - let func = state.functions_cache.get(&hash_fn).cloned(); - - let func = if let Some(ref f) = func { - f.as_ref() - } else { + // Check if function access already in the cache + let func = &*state.functions_cache.entry(hash_fn).or_insert_with(|| { // Search for the native function // First search registered functions (can override packages) // Then search packages // Finally search modules //lib.get_fn(hash_fn, pub_only) - let f = self - .global_namespace + self.global_namespace .get_fn(hash_fn, pub_only) + .cloned() + .map(|f| (f, None)) .or_else(|| { - self.global_modules - .iter() - .find_map(|m| m.get_fn(hash_fn, false)) + self.global_modules.iter().find_map(|m| { + m.get_fn(hash_fn, false) + .map(|f| (f.clone(), m.id_raw().cloned())) + }) }) - .or_else(|| mods.get_fn(hash_fn)); + .or_else(|| { + mods.get_fn(hash_fn) + .map(|(f, source)| (f.clone(), source.cloned())) + }) + }); - state.functions_cache.insert(hash_fn, f.cloned()); - f - }; - - if let Some(func) = func { + if let Some((func, source)) = func { assert!(func.is_native()); // Calling pure function but the first argument is a reference? @@ -208,11 +207,16 @@ impl Engine { backup.change_first_arg_to_copy(is_ref && func.is_pure(), args); // Run external function + let source = if source.is_none() { + state.source.as_ref() + } else { + source.as_ref() + }; let result = if func.is_plugin_fn() { func.get_plugin_fn() - .call((self, &state.source, mods, lib).into(), args) + .call((self, source, mods, lib).into(), args) } else { - func.get_native_fn()((self, &state.source, mods, lib).into(), args) + func.get_native_fn()((self, source, mods, lib).into(), args) }; // Restore the original reference @@ -338,7 +342,7 @@ impl Engine { /// Call a script-defined function. /// - /// ## WARNING + /// # WARNING /// /// Function call arguments may be _consumed_ when the function requires them to be passed by value. /// All function arguments not in the first position are always passed by value and thus consumed. @@ -413,9 +417,26 @@ impl Engine { .or_else(|err| match *err { // Convert return statement to return value EvalAltResult::Return(x, _) => Ok(x), - EvalAltResult::ErrorInFunctionCall(name, err, _) => { + EvalAltResult::ErrorInFunctionCall(name, src, err, _) => { EvalAltResult::ErrorInFunctionCall( - format!("{} > {}", fn_def.name, name), + format!( + "{}{} < {}", + name, + if src.is_empty() { + "".to_string() + } else { + format!(" @ '{}'", src) + }, + fn_def.name + ), + fn_def + .lib + .as_ref() + .map(|m| m.id()) + .flatten() + .or_else(|| state.source.as_ref().map(|s| s.as_str())) + .unwrap_or("") + .to_string(), err, pos, ) @@ -424,7 +445,20 @@ impl Engine { // System errors are passed straight-through err if err.is_system_exception() => Err(Box::new(err)), // Other errors are wrapped in `ErrorInFunctionCall` - _ => EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, pos).into(), + _ => EvalAltResult::ErrorInFunctionCall( + fn_def.name.to_string(), + fn_def + .lib + .as_ref() + .map(|m| m.id()) + .flatten() + .or_else(|| state.source.as_ref().map(|s| s.as_str())) + .unwrap_or("") + .to_string(), + err, + pos, + ) + .into(), }); // Remove all local variables @@ -478,7 +512,7 @@ impl Engine { /// Perform an actual function call, native Rust or scripted, taking care of special functions. /// - /// ## WARNING + /// # WARNING /// /// Function call arguments may be _consumed_ when the function requires them to be passed by value. /// All function arguments not in the first position are always passed by value and thus consumed. @@ -549,100 +583,85 @@ impl Engine { .iter() .find_map(|&m| { m.get_fn(hash_script, pub_only) - .map(|f| (f, m.id_raw().clone())) + .map(|f| (f, m.id_raw().cloned())) }) //.or_else(|| self.global_namespace.get_fn(hash_script, pub_only)) .or_else(|| { - self.global_modules - .iter() - .find_map(|m| m.get_fn(hash_script, false)) - .map(|f| (f, None)) + self.global_modules.iter().find_map(|m| { + m.get_fn(hash_script, false) + .map(|f| (f, m.id_raw().cloned())) + }) }) //.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.id_raw().clone())))) .unwrap(); - if func.is_script() { - let func = func.get_fn_def(); + assert!(func.is_script()); - let scope: &mut Scope = &mut Default::default(); + let func = func.get_fn_def(); - // Move captured variables into scope - #[cfg(not(feature = "no_closure"))] - if let Some(captured) = _capture_scope { - if !func.externals.is_empty() { - captured - .into_iter() - .filter(|(name, _, _)| func.externals.iter().any(|ex| ex == name)) - .for_each(|(name, value, _)| { - // Consume the scope values. - scope.push_dynamic(name, value); - }); - } + let scope: &mut Scope = &mut Default::default(); + + // Move captured variables into scope + #[cfg(not(feature = "no_closure"))] + if let Some(captured) = _capture_scope { + if !func.externals.is_empty() { + captured + .into_iter() + .filter(|(name, _, _)| func.externals.iter().any(|ex| ex == name)) + .for_each(|(name, value, _)| { + // Consume the scope values. + scope.push_dynamic(name, value); + }); } + } - let result = if _is_method { - // Method call of script function - map first argument to `this` - let (first, rest) = args.split_first_mut().unwrap(); + let result = if _is_method { + // Method call of script function - map first argument to `this` + let (first, rest) = args.split_first_mut().unwrap(); - mem::swap(&mut state.source, &mut source); + mem::swap(&mut state.source, &mut source); - let level = _level + 1; + let level = _level + 1; - let result = self.call_script_fn( - scope, - mods, - state, - lib, - &mut Some(*first), - func, - rest, - pos, - level, - ); - - // Restore the original source - state.source = source; - - result? - } else { - // Normal call of script function - // The first argument is a reference? - let mut backup: ArgBackup = Default::default(); - backup.change_first_arg_to_copy(is_ref, args); - - mem::swap(&mut state.source, &mut source); - - let level = _level + 1; - - let result = self.call_script_fn( - scope, mods, state, lib, &mut None, func, args, pos, level, - ); - - // Restore the original source - state.source = source; - - // Restore the original reference - backup.restore_first_arg(args); - - result? - }; - - Ok((result, false)) - } else { - // If it is a native function, redirect it - self.call_native_fn( + let result = self.call_script_fn( + scope, mods, state, lib, - fn_name, - hash_script, - args, - is_ref, - pub_only, + &mut Some(*first), + func, + rest, pos, - def_val, - ) - } + level, + ); + + // Restore the original source + state.source = source; + + result? + } else { + // Normal call of script function + // The first argument is a reference? + let mut backup: ArgBackup = Default::default(); + backup.change_first_arg_to_copy(is_ref, args); + + mem::swap(&mut state.source, &mut source); + + let level = _level + 1; + + let result = self + .call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level); + + // Restore the original source + state.source = source; + + // Restore the original reference + backup.restore_first_arg(args); + + result? + }; + + Ok((result, false)) } // Normal native function call @@ -675,7 +694,7 @@ impl Engine { ) -> Result> { statements .into_iter() - .try_fold(().into(), |_, stmt| { + .try_fold(Dynamic::UNIT, |_, stmt| { self.eval_stmt(scope, mods, state, lib, &mut None, stmt, level) }) .or_else(|err| match *err { @@ -1204,7 +1223,7 @@ impl Engine { let new_scope = &mut Default::default(); let fn_def = f.get_fn_def().clone(); - let mut source = module.id_raw().clone(); + let mut source = module.id_raw().cloned(); mem::swap(&mut state.source, &mut source); let level = level + 1; diff --git a/src/fn_func.rs b/src/fn_func.rs index 1791c0ba..e4b2d4ed 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -12,6 +12,7 @@ pub trait Func { type Output; /// Create a Rust closure from an [`AST`]. + /// /// The [`Engine`] and [`AST`] are consumed and basically embedded into the closure. /// /// # Example @@ -43,6 +44,7 @@ pub trait Func { fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output; /// Create a Rust closure from a script. + /// /// The [`Engine`] is consumed and basically embedded into the closure. /// /// # Example diff --git a/src/fn_native.rs b/src/fn_native.rs index 6ca51e43..6144ac5e 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -10,11 +10,12 @@ use crate::stdlib::{ iter::empty, mem, string::String, + vec::Vec, }; use crate::token::is_valid_identifier; use crate::{ calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, - Position, StaticVec, + Position, }; #[cfg(not(feature = "sync"))] @@ -62,14 +63,14 @@ pub struct NativeCallContext<'e, 's, 'a, 'm, 'pm: 'm> { } impl<'e, 's, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> - From<(&'e Engine, &'s Option, &'a Imports, &'m M)> + From<(&'e Engine, Option<&'s ImmutableString>, &'a Imports, &'m M)> for NativeCallContext<'e, 's, 'a, 'm, 'pm> { #[inline(always)] - fn from(value: (&'e Engine, &'s Option, &'a Imports, &'m M)) -> Self { + fn from(value: (&'e Engine, Option<&'s ImmutableString>, &'a Imports, &'m M)) -> Self { Self { engine: value.0, - source: value.1.as_ref().map(|s| s.as_str()), + source: value.1.map(|s| s.as_str()), mods: Some(value.2), lib: value.3.as_ref(), } @@ -121,14 +122,20 @@ impl<'e, 's, 'a, 'm, 'pm> NativeCallContext<'e, 's, 'a, 'm, 'pm> { } /// The current [`Engine`]. #[inline(always)] - pub fn engine(&self) -> &'e Engine { + pub fn engine(&self) -> &Engine { self.engine } /// The current source. #[inline(always)] - pub fn source<'z: 's>(&'z self) -> Option<&'s str> { + pub fn source(&self) -> Option<&str> { self.source } + /// Get an iterator over the current set of modules imported via `import` statements. + #[cfg(not(feature = "no_module"))] + #[inline(always)] + pub fn iter_imports(&self) -> impl Iterator { + self.mods.iter().flat_map(|&m| m.iter()) + } /// _(INTERNALS)_ The current set of modules imported via `import` statements. /// Available under the `internals` feature only. #[cfg(feature = "internals")] @@ -137,14 +144,21 @@ impl<'e, 's, 'a, 'm, 'pm> NativeCallContext<'e, 's, 'a, 'm, 'pm> { pub fn imports(&self) -> Option<&Imports> { self.mods } - /// Get an iterator over the namespaces containing definition of all script-defined functions. + /// Get an iterator over the namespaces containing definitions of all script-defined functions. #[inline(always)] - pub fn iter_namespaces(&self) -> impl Iterator + 'm { + pub fn iter_namespaces(&self) -> impl Iterator { self.lib.iter().cloned() } + /// _(INTERNALS)_ The current set of namespaces containing definitions of all script-defined functions. + /// Available under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline(always)] + pub fn namespaces(&self) -> &[&Module] { + self.lib + } /// Call a function inside the call context. /// - /// ## WARNING + /// # WARNING /// /// All arguments may be _consumed_, meaning that they may be replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. @@ -224,7 +238,7 @@ pub type FnCallArgs<'a> = [&'a mut Dynamic]; /// A general function pointer, which may carry additional (i.e. curried) argument values /// to be passed onto a function during a call. #[derive(Debug, Clone)] -pub struct FnPtr(ImmutableString, StaticVec); +pub struct FnPtr(ImmutableString, Vec); impl FnPtr { /// Create a new function pointer. @@ -234,10 +248,7 @@ impl FnPtr { } /// Create a new function pointer without checking its parameters. #[inline(always)] - pub(crate) fn new_unchecked( - name: impl Into, - curry: StaticVec, - ) -> Self { + pub(crate) fn new_unchecked(name: impl Into, curry: Vec) -> Self { Self(name.into(), curry) } /// Get the name of the function. @@ -252,7 +263,7 @@ impl FnPtr { } /// Get the underlying data of the function pointer. #[inline(always)] - pub(crate) fn take_data(self) -> (ImmutableString, StaticVec) { + pub(crate) fn take_data(self) -> (ImmutableString, Vec) { (self.0, self.1) } /// Get the curried arguments. @@ -287,7 +298,7 @@ impl FnPtr { /// /// If this function is a script-defined function, it must not be marked private. /// - /// ## WARNING + /// # WARNING /// /// All the arguments are _consumed_, meaning that they're replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. @@ -307,9 +318,9 @@ impl FnPtr { .iter() .cloned() .chain(arg_values.iter_mut().map(mem::take)) - .collect::>(); + .collect::>(); - let mut args = args_data.iter_mut().collect::>(); + let mut args = args_data.iter_mut().collect::>(); let is_method = this_ptr.is_some(); diff --git a/src/lib.rs b/src/lib.rs index 9f7a8773..a618879d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,7 +104,7 @@ pub type INT = i32; /// /// If the `f32_float` feature is enabled, this will be [`i32`] instead. /// -/// Not available under the `no_float` feature. +/// Not available under `no_float`. #[cfg(not(feature = "no_float"))] #[cfg(not(feature = "f32_float"))] pub type FLOAT = f64; @@ -114,7 +114,7 @@ pub type FLOAT = f64; /// /// If the `f32_float` feature is not used, this will be `f64` instead. /// -/// Not available under the `no_float` feature. +/// Not available under `no_float`. #[cfg(not(feature = "no_float"))] #[cfg(feature = "f32_float")] pub type FLOAT = f32; @@ -148,13 +148,13 @@ pub use fn_func::Func; /// Variable-sized array of [`Dynamic`] values. /// -/// Not available under the `no_index` feature. +/// Not available under `no_index`. #[cfg(not(feature = "no_index"))] pub type Array = stdlib::vec::Vec; /// Hash map of [`Dynamic`] values with [`ImmutableString`] keys. /// -/// Not available under the `no_object` feature. +/// Not available under `no_object`. #[cfg(not(feature = "no_object"))] pub type Map = stdlib::collections::HashMap; diff --git a/src/module/mod.rs b/src/module/mod.rs index c90775a2..e7cda5d4 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -125,17 +125,15 @@ impl FuncInfo { /// A module which may contain variables, sub-modules, external Rust functions, /// and/or script-defined functions. -/// -/// Not available under the `no_module` feature. #[derive(Clone)] pub struct Module { /// ID identifying the module. id: Option, /// Sub-modules. modules: HashMap>, - /// Module variables. + /// [`Module`] variables. variables: HashMap, - /// Flattened collection of all module variables, including those in sub-modules. + /// Flattened collection of all [`Module`] variables, including those in sub-modules. all_variables: HashMap, /// External Rust functions. functions: HashMap, @@ -146,7 +144,7 @@ pub struct Module { type_iterators: HashMap, /// Flattened collection of iterator functions, including those in sub-modules. all_type_iterators: HashMap, - /// Is the module indexed? + /// Is the [`Module`] indexed? indexed: bool, } @@ -203,7 +201,7 @@ impl AsRef for Module { } impl Module { - /// Create a new module. + /// Create a new [`Module`]. /// /// # Example /// @@ -219,7 +217,7 @@ impl Module { Default::default() } - /// Create a new module with a specified capacity for native Rust functions. + /// Create a new [`Module`] with a specified capacity for native Rust functions. /// /// # Example /// @@ -238,7 +236,7 @@ impl Module { } } - /// Get the ID of the module, if any. + /// Get the ID of the [`Module`], if any. /// /// # Example /// @@ -249,16 +247,28 @@ impl Module { /// module.set_id(Some("hello")); /// assert_eq!(module.id(), Some("hello")); /// ``` + #[inline(always)] pub fn id(&self) -> Option<&str> { - self.id.as_ref().map(|s| s.as_str()) + self.id_raw().map(|s| s.as_str()) } - /// Get the ID of the module, if any. - pub(crate) fn id_raw(&self) -> &Option { - &self.id + /// Get the ID of the [`Module`] as an [`ImmutableString`], if any. + /// + /// # Example + /// + /// ``` + /// use rhai::Module; + /// + /// let mut module = Module::new(); + /// module.set_id(Some("hello")); + /// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello")); + /// ``` + #[inline(always)] + pub fn id_raw(&self) -> Option<&ImmutableString> { + self.id.as_ref() } - /// Set the ID of the module. + /// Set the ID of the [`Module`]. /// /// # Example /// @@ -269,11 +279,12 @@ impl Module { /// module.set_id(Some("hello")); /// assert_eq!(module.id(), Some("hello")); /// ``` + #[inline(always)] pub fn set_id>(&mut self, id: Option) { self.id = id.map(|s| s.into()); } - /// Is the module empty? + /// Is the [`Module`] empty? /// /// # Example /// @@ -294,7 +305,7 @@ impl Module { && self.all_type_iterators.is_empty() } - /// Is the module indexed? + /// Is the [`Module`] indexed? /// /// # Example /// @@ -310,11 +321,13 @@ impl Module { /// assert!(module.is_indexed()); /// # } /// ``` + #[inline(always)] pub fn is_indexed(&self) -> bool { self.indexed } - /// Generate signatures for all the functions in the module. + /// Generate signatures for all the functions in the [`Module`]. + #[inline(always)] pub fn gen_fn_signatures<'a>(&'a self) -> impl Iterator + 'a { self.functions .values() @@ -322,7 +335,7 @@ impl Module { .map(FuncInfo::gen_signature) } - /// Does a variable exist in the module? + /// Does a variable exist in the [`Module`]? /// /// # Example /// @@ -338,7 +351,7 @@ impl Module { self.variables.contains_key(name) } - /// Get the value of a module variable. + /// Get the value of a [`Module`] variable. /// /// # Example /// @@ -354,7 +367,7 @@ impl Module { self.get_var(name).and_then(Dynamic::try_cast::) } - /// Get a module variable as a [`Dynamic`]. + /// Get a [`Module`] variable as a [`Dynamic`]. /// /// # Example /// @@ -370,7 +383,7 @@ impl Module { self.variables.get(name).cloned() } - /// Set a variable into the module. + /// Set a variable into the [`Module`]. /// /// If there is an existing variable of the same name, it is replaced. /// @@ -395,9 +408,9 @@ impl Module { } /// Get a reference to a namespace-qualified variable. - /// Name and Position in `EvalAltResult` are None and must be set afterwards. + /// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards. /// - /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated by the function [`calc_native_fn_hash`][crate::calc_native_fn_hash]. #[inline(always)] pub(crate) fn get_qualified_var( &self, @@ -408,7 +421,7 @@ impl Module { }) } - /// Set a script-defined function into the module. + /// Set a script-defined function into the [`Module`]. /// /// If there is an existing function of the same name and number of arguments, it is replaced. #[cfg(not(feature = "no_function"))] @@ -437,7 +450,7 @@ impl Module { hash_script } - /// Get a script-defined function in the module based on name and number of parameters. + /// Get a script-defined function in the [`Module`] based on name and number of parameters. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn get_script_fn( @@ -465,10 +478,10 @@ impl Module { /// Get a mutable reference to the underlying [`HashMap`] of sub-modules. /// - /// ## Warning + /// # WARNING /// /// By taking a mutable reference, it is assumed that some sub-modules will be modified. - /// Thus the module is automatically set to be non-indexed. + /// Thus the [`Module`] is automatically set to be non-indexed. #[cfg(not(feature = "no_module"))] #[inline(always)] pub(crate) fn sub_modules_mut(&mut self) -> &mut HashMap> { @@ -482,7 +495,7 @@ impl Module { &mut self.modules } - /// Does a sub-module exist in the module? + /// Does a sub-module exist in the [`Module`]? /// /// # Example /// @@ -499,7 +512,7 @@ impl Module { self.modules.contains_key(name) } - /// Get a sub-module. + /// Get a sub-module in the [`Module`]. /// /// # Example /// @@ -516,7 +529,7 @@ impl Module { self.modules.get(name).map(|m| m.as_ref()) } - /// Set a sub-module into the module. + /// Set a sub-module into the [`Module`]. /// /// If there is an existing sub-module of the same name, it is replaced. /// @@ -541,9 +554,9 @@ impl Module { self } - /// Does the particular Rust function exist in the module? + /// Does the particular Rust function exist in the [`Module`]? /// - /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated by the function [`calc_native_fn_hash`][crate::calc_native_fn_hash]. /// It is also returned by the `set_fn_XXX` calls. /// /// # Example @@ -569,13 +582,19 @@ impl Module { /// Update the metadata (parameter names/types and return type) of a registered function. /// - /// The [`NonZeroU64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or - /// the function [`crate::calc_script_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated either by the function + /// [`calc_native_fn_hash`][crate::calc_native_fn_hash] or the function + /// [`calc_script_fn_hash`][crate::calc_script_fn_hash]. + /// + /// ## Parameter Names and Types /// /// Each parameter name/type pair should be a single string of the format: `var_name: type`. /// - /// The last entry in the list should be the return type of the function. + /// ## Return Type + /// + /// The _last entry_ in the list should be the _return type_ of the function. /// In other words, the number of entries should be one larger than the number of parameters. + #[inline(always)] pub fn update_fn_metadata<'a>( &mut self, hash_fn: NonZeroU64, @@ -589,8 +608,10 @@ impl Module { /// Update the namespace of a registered function. /// - /// The [`NonZeroU64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or - /// the function [`crate::calc_script_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated either by the function + /// [`calc_native_fn_hash`][crate::calc_native_fn_hash] or the function + /// [`calc_script_fn_hash`][crate::calc_script_fn_hash]. + #[inline(always)] pub fn update_fn_namespace( &mut self, hash_fn: NonZeroU64, @@ -603,13 +624,14 @@ impl Module { self } - /// Set a Rust function into the module, returning a hash key. + /// Set a Rust function into the [`Module`], returning a hash key. /// /// If there is an existing Rust function of the same hash, it is replaced. /// - /// ## WARNING - Low Level API + /// # WARNING - Low Level API /// /// This function is very low level. + #[inline] pub fn set_fn( &mut self, name: impl Into, @@ -660,7 +682,7 @@ impl Module { /// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine], /// the current set of functions, plus a list of mutable [`Dynamic`] references - /// into the module, returning a hash key. + /// into the [`Module`], returning a hash key. /// /// Use this to register a built-in function which must reference settings on the scripting /// [`Engine`][crate::Engine] (e.g. to prevent growing an array beyond the allowed maximum size), @@ -668,13 +690,13 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// - /// ## WARNING - Low Level API + /// # WARNING - Low Level API /// /// This function is very low level. /// /// A list of [`TypeId`]'s is taken as the argument types. /// - /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`], + /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic], /// which is guaranteed to contain enough arguments of the correct types. /// /// The function is assumed to be a _method_, meaning that the first argument should not be consumed. @@ -722,7 +744,7 @@ impl Module { /// /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_raw_fn( &mut self, name: impl Into, @@ -746,7 +768,7 @@ impl Module { ) } - /// Set a Rust function taking no parameters into the module, returning a hash key. + /// 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. /// @@ -763,7 +785,7 @@ impl Module { /// let hash = module.set_fn_0("calc", || Ok(42_i64)); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_0( &mut self, name: impl Into, @@ -781,7 +803,7 @@ impl Module { ) } - /// Set a Rust function taking one parameter into the module, returning a hash key. + /// Set a Rust function taking one parameter into the [`Module`], returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -798,7 +820,7 @@ impl Module { /// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1)); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_1( &mut self, name: impl Into, @@ -818,7 +840,7 @@ impl Module { ) } - /// Set a Rust function taking one mutable parameter into the module, returning a hash key. + /// Set a Rust function taking one mutable parameter into the [`Module`], returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -837,7 +859,7 @@ impl Module { /// ); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_1_mut( &mut self, name: impl Into, @@ -877,7 +899,7 @@ impl Module { /// assert!(module.contains_fn(hash, true)); /// ``` #[cfg(not(feature = "no_object"))] - #[inline] + #[inline(always)] pub fn set_getter_fn( &mut self, name: impl Into, @@ -890,7 +912,7 @@ impl Module { ) } - /// Set a Rust function taking two parameters into the module, returning a hash key. + /// Set a Rust function taking two parameters into the [`Module`], returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -909,7 +931,7 @@ impl Module { /// }); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_2( &mut self, name: impl Into, @@ -932,7 +954,7 @@ impl Module { ) } - /// Set a Rust function taking two parameters (the first one mutable) into the module, + /// Set a Rust function taking two parameters (the first one mutable) into the [`Module`], /// returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. @@ -955,7 +977,7 @@ impl Module { /// ); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_2_mut( &mut self, name: impl Into, @@ -979,7 +1001,7 @@ impl Module { ) } - /// Set a Rust setter function taking two parameters (the first one mutable) into the module, + /// Set a Rust setter function taking two parameters (the first one mutable) into the [`Module`], /// returning a hash key. /// This function is automatically exposed to the global namespace. /// @@ -1002,7 +1024,7 @@ impl Module { /// assert!(module.contains_fn(hash, true)); /// ``` #[cfg(not(feature = "no_object"))] - #[inline] + #[inline(always)] pub fn set_setter_fn( &mut self, name: impl Into, @@ -1015,7 +1037,7 @@ impl Module { ) } - /// Set a Rust index getter taking two parameters (the first one mutable) into the module, + /// Set a Rust index getter taking two parameters (the first one mutable) into the [`Module`], /// returning a hash key. /// This function is automatically exposed to the global namespace. /// @@ -1042,7 +1064,7 @@ impl Module { /// assert!(module.contains_fn(hash, true)); /// ``` #[cfg(not(feature = "no_index"))] - #[inline] + #[inline(always)] pub fn set_indexer_get_fn( &mut self, func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, @@ -1064,7 +1086,7 @@ impl Module { self.set_fn_2_mut(crate::engine::FN_IDX_GET, FnNamespace::Global, func) } - /// Set a Rust function taking three parameters into the module, returning a hash key. + /// Set a Rust function taking three parameters into the [`Module`], returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -1083,7 +1105,7 @@ impl Module { /// }); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_3< A: Variant + Clone, B: Variant + Clone, @@ -1112,7 +1134,7 @@ impl Module { ) } - /// Set a Rust function taking three parameters (the first one mutable) into the module, + /// Set a Rust function taking three parameters (the first one mutable) into the [`Module`], /// returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. @@ -1135,7 +1157,7 @@ impl Module { /// ); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_3_mut< A: Variant + Clone, B: Variant + Clone, @@ -1165,7 +1187,7 @@ impl Module { ) } - /// Set a Rust index setter taking three parameters (the first one mutable) into the module, + /// Set a Rust index setter taking three parameters (the first one mutable) into the [`Module`], /// returning a hash key. /// This function is automatically exposed to the global namespace. /// @@ -1193,7 +1215,7 @@ impl Module { /// assert!(module.contains_fn(hash, true)); /// ``` #[cfg(not(feature = "no_index"))] - #[inline] + #[inline(always)] pub fn set_indexer_set_fn( &mut self, func: impl Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, @@ -1264,7 +1286,7 @@ impl Module { /// assert!(module.contains_fn(hash_set, true)); /// ``` #[cfg(not(feature = "no_index"))] - #[inline] + #[inline(always)] pub fn set_indexer_get_set_fn( &mut self, getter: impl Fn(&mut A, B) -> Result> + SendSync + 'static, @@ -1276,7 +1298,7 @@ impl Module { ) } - /// Set a Rust function taking four parameters into the module, returning a hash key. + /// Set a Rust function taking four parameters into the [`Module`], returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -1295,7 +1317,7 @@ impl Module { /// }); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_4< A: Variant + Clone, B: Variant + Clone, @@ -1331,7 +1353,7 @@ impl Module { ) } - /// Set a Rust function taking four parameters (the first one mutable) into the module, + /// Set a Rust function taking four parameters (the first one mutable) into the [`Module`], /// returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. @@ -1354,7 +1376,7 @@ impl Module { /// ); /// assert!(module.contains_fn(hash, true)); /// ``` - #[inline] + #[inline(always)] pub fn set_fn_4_mut< A: Variant + Clone, B: Variant + Clone, @@ -1393,7 +1415,7 @@ impl Module { /// Get a Rust function. /// - /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated by the function [`calc_native_fn_hash`][crate::calc_native_fn_hash]. /// It is also returned by the `set_fn_XXX` calls. #[inline(always)] pub(crate) fn get_fn( @@ -1410,9 +1432,10 @@ impl Module { }) } - /// Does the particular namespace-qualified function exist in the module? + /// Does the particular namespace-qualified function exist in the [`Module`]? /// - /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match + /// The [`NonZeroU64`] hash is calculated by the function + /// [`calc_native_fn_hash`][crate::calc_native_fn_hash] and must match /// the hash calculated by [`build_index`][Module::build_index]. #[inline(always)] pub fn contains_qualified_fn(&self, hash_fn: NonZeroU64) -> bool { @@ -1420,9 +1443,9 @@ impl Module { } /// Get a namespace-qualified function. - /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// - /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match + /// The [`NonZeroU64`] hash is calculated by the function + /// [`calc_native_fn_hash`][crate::calc_native_fn_hash] and must match /// the hash calculated by [`build_index`][Module::build_index]. #[inline(always)] pub(crate) fn get_qualified_fn( @@ -1432,8 +1455,8 @@ impl Module { self.all_functions.get(&hash_qualified_fn) } - /// Combine another module into this module. - /// The other module is consumed to merge into this module. + /// Combine another [`Module`] into this [`Module`]. + /// The other [`Module`] is _consumed_ to merge into this [`Module`]. #[inline] pub fn combine(&mut self, other: Self) -> &mut Self { self.modules.extend(other.modules.into_iter()); @@ -1447,9 +1470,9 @@ impl Module { self } - /// Combine another module into this module. - /// The other module is consumed to merge into this module. - /// Sub-modules are flattened onto the root module, with higher level overriding lower level. + /// Combine another [`Module`] into this [`Module`]. + /// The other [`Module`] is _consumed_ to merge into this [`Module`]. + /// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level. #[inline] pub fn combine_flatten(&mut self, other: Self) -> &mut Self { other.modules.into_iter().for_each(|(_, m)| { @@ -1465,8 +1488,8 @@ impl Module { self } - /// Poly-fill this module with another module. - /// Only items not existing in this module are added. + /// Polyfill this [`Module`] with another [`Module`]. + /// Only items not existing in this [`Module`] are added. #[inline] pub fn fill_with(&mut self, other: &Self) -> &mut Self { other.modules.iter().for_each(|(k, v)| { @@ -1492,13 +1515,13 @@ impl Module { self } - /// Merge another module into this module. + /// Merge another [`Module`] into this [`Module`]. #[inline(always)] pub fn merge(&mut self, other: &Self) -> &mut Self { self.merge_filtered(other, &mut |_, _, _, _, _| true) } - /// Merge another module into this module 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, @@ -1584,7 +1607,7 @@ impl Module { self } - /// Get the number of variables, functions and type iterators in the module. + /// Get the number of variables, functions and type iterators in the [`Module`]. #[inline(always)] pub fn count(&self) -> (usize, usize, usize) { ( @@ -1594,19 +1617,19 @@ impl Module { ) } - /// Get an iterator to the sub-modules in the module. + /// Get an iterator to the sub-modules in the [`Module`]. #[inline(always)] pub fn iter_sub_modules(&self) -> impl Iterator)> { self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) } - /// Get an iterator to the variables in the module. + /// Get an iterator to the variables in the [`Module`]. #[inline(always)] pub fn iter_var(&self) -> impl Iterator { self.variables.iter().map(|(k, v)| (k.as_str(), v)) } - /// Get an iterator to the functions in the module. + /// Get an iterator to the functions in the [`Module`]. #[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_function"))] #[inline(always)] @@ -1614,7 +1637,7 @@ impl Module { self.functions.values() } - /// Get an iterator over all script-defined functions in the module. + /// Get an iterator over all script-defined functions in the [`Module`]. /// /// Function metadata includes: /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). @@ -1647,7 +1670,7 @@ impl Module { ) } - /// Get an iterator over all script-defined functions in the module. + /// Get an iterator over all script-defined functions in the [`Module`]. /// /// Function metadata includes: /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). @@ -1671,7 +1694,7 @@ impl Module { ) } - /// Get an iterator over all script-defined functions in the module. + /// Get an iterator over all script-defined functions in the [`Module`]. /// /// Function metadata includes: /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). @@ -1689,11 +1712,11 @@ impl Module { self.iter_script_fn() } - /// Create a new module by evaluating an [`AST`][crate::AST]. + /// Create a new [`Module`] by evaluating an [`AST`][crate::AST]. /// /// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions /// to cross-call each other. Functions in the global namespace, plus all functions - /// defined in the module, are _merged_ into a _unified_ namespace before each call. + /// defined in the [`Module`], are _merged_ into a _unified_ namespace before each call. /// Therefore, all functions will be found. /// /// # Example @@ -1766,10 +1789,10 @@ impl Module { Ok(module) } - /// Scan through all the sub-modules in the module and build a hash index of all + /// Scan through all the sub-modules in the [`Module`] and build a hash index of all /// variables and functions as one flattened namespace. /// - /// If the module is already indexed, this method has no effect. + /// If the [`Module`] is already indexed, this method has no effect. pub fn build_index(&mut self) -> &mut Self { // Collect a particular module. fn index_module<'a>( @@ -1887,14 +1910,14 @@ impl Module { self.type_iterators.contains_key(&id) } - /// Set a type iterator into the module. + /// Set a type iterator into the [`Module`]. pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self { self.type_iterators.insert(typ, func); self.indexed = false; self } - /// Set a type iterator into the module. + /// Set a type iterator into the [`Module`]. pub fn set_iterable(&mut self) -> &mut Self where T: Variant + Clone + IntoIterator, @@ -1905,7 +1928,7 @@ impl Module { }) } - /// Set an iterator type into the module as a type iterator. + /// Set an iterator type into the [`Module`] as a type iterator. pub fn set_iterator(&mut self) -> &mut Self where T: Variant + Clone + Iterator, @@ -1935,7 +1958,7 @@ impl Module { /// A [`StaticVec`] is used because most namespace-qualified access contains only one level, /// and it is wasteful to always allocate a [`Vec`] with one element. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Clone, Eq, PartialEq, Default, Hash)] @@ -2019,10 +2042,9 @@ impl NamespaceRef { } } -/// Re-export module resolver trait. #[cfg(not(feature = "no_module"))] pub use resolvers::ModuleResolver; -/// Re-export module resolvers. +/// Module containing all built-in [module resolvers][ModuleResolver]. #[cfg(not(feature = "no_module"))] pub mod resolvers; diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 30039234..6a6009c3 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,7 +1,7 @@ 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, +/// [Module] resolution service that holds a collection of [module][Module] resolves, /// to be searched in sequential order. /// /// # Example @@ -42,13 +42,13 @@ impl ModuleResolversCollection { pub fn new() -> Self { Default::default() } - /// Append a module resolver to the end. + /// Append a [module resolver][ModuleResolver] to the end. #[inline(always)] pub fn push(&mut self, resolver: impl ModuleResolver + 'static) -> &mut Self { self.0.push(Box::new(resolver)); self } - /// Insert a module resolver to an offset index. + /// Insert a [module resolver][ModuleResolver] to an offset index. /// /// # Panics /// @@ -58,12 +58,12 @@ impl ModuleResolversCollection { self.0.insert(index, Box::new(resolver)); self } - /// Remove the last module resolver from the end, if any. + /// Remove the last [module resolver][ModuleResolver] from the end, if any. #[inline(always)] pub fn pop(&mut self) -> Option> { self.0.pop() } - /// Remove a module resolver at an offset index. + /// Remove a [module resolver][ModuleResolver] at an offset index. /// /// # Panics /// @@ -72,17 +72,17 @@ impl ModuleResolversCollection { pub fn remove(&mut self, index: usize) -> Box { self.0.remove(index) } - /// Get an iterator of all the module resolvers. + /// Get an iterator of all the [module resolvers][ModuleResolver]. #[inline(always)] pub fn iter(&self) -> impl Iterator { self.0.iter().map(|v| v.as_ref()) } - /// Get a mutable iterator of all the modules. + /// Get a mutable iterator of all the [module resolvers][ModuleResolver]. #[inline(always)] pub fn into_iter(self) -> impl Iterator> { self.0.into_iter() } - /// Remove all module resolvers. + /// Remove all [module resolvers][ModuleResolver]. #[inline(always)] pub fn clear(&mut self) -> &mut Self { self.0.clear(); @@ -93,7 +93,7 @@ impl ModuleResolversCollection { pub fn is_empty(&self) -> bool { self.0.is_empty() } - /// Get the number of module resolvers in this [`ModuleResolversCollection`]. + /// Get the number of [module resolvers][ModuleResolver] in this [`ModuleResolversCollection`]. #[inline(always)] pub fn len(&self) -> usize { self.0.len() diff --git a/src/module/resolvers/dummy.rs b/src/module/resolvers/dummy.rs index 8ed3b3f0..d238b448 100644 --- a/src/module/resolvers/dummy.rs +++ b/src/module/resolvers/dummy.rs @@ -1,7 +1,7 @@ use crate::stdlib::boxed::Box; use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; -/// Empty/disabled module resolution service that acts as a dummy. +/// Empty/disabled [module][Module] resolution service that acts as a dummy. /// /// # Example /// diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index c35c347d..af4cc98e 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -7,21 +7,15 @@ use crate::stdlib::{ }; use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; -/// Module resolution service that loads module script files from the file system. +/// [Module] resolution service that loads [module][Module] script files from the file system. /// /// Script files are cached so they are are not reloaded and recompiled in subsequent requests. /// -/// The [`new_with_path`][FileModuleResolver::new_with_path] and -/// [`new_with_path_and_extension`][FileModuleResolver::new_with_path_and_extension] constructor functions -/// allow specification of a base directory with module path used as a relative path offset -/// to the base directory. The script file is then forced to be in a specified extension -/// (default `.rhai`). -/// /// # Function Namespace /// -/// When a function within a script file module is loaded, all functions in the _global_ namespace +/// When a function within a script file module is called, all functions in the _global_ namespace /// plus all those defined within the same module are _merged_ into a _unified_ namespace before -/// the call. Therefore, functions in a module script can cross-call each other. +/// the call. Therefore, functions in a module script can always cross-call each other. /// /// # Example /// @@ -58,6 +52,8 @@ impl Default for FileModuleResolver { impl FileModuleResolver { /// Create a new [`FileModuleResolver`] with a specific base path. /// + /// The default extension is `.rhai`. + /// /// # Example /// /// ``` @@ -78,8 +74,6 @@ impl FileModuleResolver { /// Create a new [`FileModuleResolver`] with a specific base path and file extension. /// - /// The default extension is `.rhai`. - /// /// # Example /// /// ``` @@ -107,6 +101,8 @@ impl FileModuleResolver { /// Create a new [`FileModuleResolver`] with the current directory as base path. /// + /// The default extension is `.rhai`. + /// /// # Example /// /// ``` @@ -159,7 +155,9 @@ impl FileModuleResolver { self.cache.write().unwrap().clear(); } - /// Empty the internal cache. + /// Remove the specified path from internal cache. + /// + /// The next time this path is resolved, the script file will be loaded once again. #[inline(always)] pub fn clear_cache_for_path(&mut self, path: impl AsRef) -> Option> { #[cfg(not(feature = "sync"))] @@ -190,52 +188,43 @@ impl ModuleResolver for FileModuleResolver { file_path.push(path); file_path.set_extension(&self.extension); // Force extension - let scope = Default::default(); - // See if it is cached - let mut module: Option> = None; - - let mut module_ref = { + { #[cfg(not(feature = "sync"))] let c = self.cache.borrow(); #[cfg(feature = "sync")] let c = self.cache.read().unwrap(); if let Some(module) = c.get(&file_path) { - Some(module.clone()) - } else { - None + return Ok(module.clone()); } - }; - - if module_ref.is_none() { - // Load the script file and compile it - let ast = engine - .compile_file(file_path.clone()) - .map_err(|err| match *err { - EvalAltResult::ErrorSystem(_, err) if err.is::() => { - Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)) - } - _ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)), - })?; - - let mut m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| { - Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)) - })?; - - m.set_id(Some(path)); - module = Some(m.into()); - module_ref = module.clone(); - }; - - if let Some(module) = module { - // Put it into the cache - #[cfg(not(feature = "sync"))] - self.cache.borrow_mut().insert(file_path, module); - #[cfg(feature = "sync")] - self.cache.write().unwrap().insert(file_path, module); } - Ok(module_ref.unwrap()) + // Load the script file and compile it + let scope = Default::default(); + + let mut ast = engine + .compile_file(file_path.clone()) + .map_err(|err| match *err { + EvalAltResult::ErrorSystem(_, err) if err.is::() => { + Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)) + } + _ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)), + })?; + + ast.set_source(Some(path)); + + // Make a module from the AST + let m: Shared = Module::eval_ast_as_new(scope, &ast, engine) + .map_err(|err| Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)))? + .into(); + + // Put it into the cache + #[cfg(not(feature = "sync"))] + self.cache.borrow_mut().insert(file_path, m.clone()); + #[cfg(feature = "sync")] + self.cache.write().unwrap().insert(file_path, m.clone()); + + Ok(m) } } diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 311d92d0..f4705108 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -1,7 +1,7 @@ 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. +/// [Module] resolution service that serves [modules][Module] added into it. /// /// # Example /// @@ -42,13 +42,13 @@ impl StaticModuleResolver { pub fn new() -> Self { Default::default() } - /// Add a module keyed by its path. + /// Add a [module][Module] keyed by its path. #[inline(always)] pub fn insert(&mut self, path: impl Into, mut module: Module) { module.build_index(); self.0.insert(path.into(), module.into()); } - /// Remove a module given its path. + /// Remove a [module][Module] given its path. #[inline(always)] pub fn remove(&mut self, path: &str) -> Option> { self.0.remove(path) @@ -58,12 +58,12 @@ impl StaticModuleResolver { pub fn contains_path(&self, path: &str) -> bool { self.0.contains_key(path) } - /// Get an iterator of all the modules. + /// Get an iterator of all the [modules][Module]. #[inline(always)] pub fn iter(&self) -> impl Iterator)> { self.0.iter().map(|(k, v)| (k.as_str(), v)) } - /// Get a mutable iterator of all the modules. + /// Get a mutable iterator of all the [modules][Module]. #[inline(always)] pub fn iter_mut(&mut self) -> impl Iterator)> { self.0.iter_mut().map(|(k, v)| (k.as_str(), v)) @@ -73,17 +73,17 @@ impl StaticModuleResolver { pub fn into_iter(self) -> impl Iterator)> { self.0.into_iter() } - /// Get an iterator of all the module paths. + /// Get an iterator of all the [module][Module] paths. #[inline(always)] pub fn paths(&self) -> impl Iterator { self.0.keys().map(String::as_str) } - /// Get an iterator of all the modules. + /// Get an iterator of all the [modules][Module]. #[inline(always)] pub fn values(&self) -> impl Iterator> { self.0.values().map(|m| m) } - /// Remove all modules. + /// Remove all [modules][Module]. #[inline(always)] pub fn clear(&mut self) { self.0.clear(); @@ -93,7 +93,7 @@ impl StaticModuleResolver { pub fn is_empty(&self) -> bool { self.0.is_empty() } - /// Get the number of modules in this [`StaticModuleResolver`]. + /// Get the number of [modules][Module] in this [`StaticModuleResolver`]. #[inline(always)] pub fn len(&self) -> usize { self.0.len() diff --git a/src/optimize.rs b/src/optimize.rs index 5ba290e0..729d43e8 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -19,8 +19,6 @@ use crate::utils::get_hasher; use crate::{calc_native_fn_hash, Dynamic, Engine, Module, Position, Scope, StaticVec, AST}; /// Level of optimization performed. -/// -/// Not available under the `no_optimize` feature. #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] pub enum OptimizationLevel { /// No optimization performed. diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 4ab427db..6247e881 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -214,6 +214,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "map".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -245,6 +246,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "filter".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -278,6 +280,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "index_of".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -311,6 +314,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "some".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -344,6 +348,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "all".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -379,6 +384,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -397,6 +403,7 @@ mod array_functions { let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -416,6 +423,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -446,6 +454,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce_rev".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -464,6 +473,7 @@ mod array_functions { let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce_rev".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -483,6 +493,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce_rev".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -553,6 +564,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "drain".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) @@ -612,6 +624,7 @@ mod array_functions { .map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "retain".to_string(), + ctx.source().unwrap_or("").to_string(), err, Position::NONE, )) diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 9b7112bb..a2f41ada 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -41,9 +41,9 @@ pub trait Package { /// Retrieve the generic package library from this package. /// - /// ## Deprecated + /// # Deprecated /// - /// Use `as_shared_module` instead. + /// Use [`as_shared_module`][Package::as_shared_module] instead. #[deprecated = "use `as_shared_module` instead"] fn get(&self) -> Shared { self.as_shared_module() diff --git a/src/parse_error.rs b/src/parse_error.rs index 376631fe..0a03726c 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -11,7 +11,7 @@ use crate::{EvalAltResult, Position}; /// _(INTERNALS)_ Error encountered when tokenizing the script text. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Eq, PartialEq, Clone, Hash)] diff --git a/src/parser.rs b/src/parser.rs index ca546ac9..7f43e9c9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -173,11 +173,9 @@ impl<'e> ParseState<'e> { ) -> ImmutableString { #[allow(clippy::map_entry)] if !self.strings.contains_key(text.as_ref()) { - let value: ImmutableString = text.into(); - let result = value.clone(); - let key = value.to_string(); - self.strings.insert(key, value); - result + let value = text.into(); + self.strings.insert(value.clone().into(), value.clone()); + value } else { self.strings.get(text.as_ref()).unwrap().clone() } diff --git a/src/result.rs b/src/result.rs index afc45dec..0face85e 100644 --- a/src/result.rs +++ b/src/result.rs @@ -34,8 +34,8 @@ pub enum EvalAltResult { /// Call to an unknown function. Wrapped value is the function signature. ErrorFunctionNotFound(String, Position), /// An error has occurred inside a called function. - /// Wrapped values are the function name and the interior error. - ErrorInFunctionCall(String, Box, Position), + /// Wrapped values are the function name, function source, and the interior error. + ErrorInFunctionCall(String, String, Box, Position), /// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name. ErrorModuleNotFound(String, Position), /// An error has occurred while loading a [module][crate::Module]. @@ -98,7 +98,7 @@ impl EvalAltResult { #[allow(deprecated)] Self::ErrorSystem(_, s) => s.description(), Self::ErrorParsing(p, _) => p.desc(), - Self::ErrorInFunctionCall(_, _, _) => "Error in called function", + Self::ErrorInFunctionCall(_,_, _, _) => "Error in called function", Self::ErrorInModule(_, _, _) => "Error in module", Self::ErrorFunctionNotFound(_, _) => "Function not found", Self::ErrorUnboundThis(_) => "'this' is not bound", @@ -152,12 +152,19 @@ impl fmt::Display for EvalAltResult { Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?, #[cfg(not(feature = "no_function"))] - Self::ErrorInFunctionCall(s, err, _) if crate::engine::is_anonymous_fn(s) => { - write!(f, "Error in call to closure: {}", err)? + Self::ErrorInFunctionCall(s, src, err, _) if crate::engine::is_anonymous_fn(s) => { + write!(f, "{}, in call to closure", err)?; + if !src.is_empty() { + write!(f, " @ '{}'", src)?; + } } - Self::ErrorInFunctionCall(s, err, _) => { - write!(f, "Error in call to function '{}': {}", s, err)? + Self::ErrorInFunctionCall(s, src, err, _) => { + write!(f, "{}, in call to function {}", err, s)?; + if !src.is_empty() { + write!(f, " @ '{}'", src)?; + } } + Self::ErrorInModule(s, err, _) if s.is_empty() => { write!(f, "Error in module: {}", err)? } @@ -165,8 +172,9 @@ impl fmt::Display for EvalAltResult { Self::ErrorFunctionNotFound(s, _) | Self::ErrorVariableNotFound(s, _) - | Self::ErrorDataRace(s, _) - | Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?, + | Self::ErrorDataRace(s, _) => write!(f, "{}: {}", desc, s)?, + + Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?, Self::ErrorDotExpr(s, _) if !s.is_empty() => write!(f, "{}", s)?, @@ -187,9 +195,7 @@ impl fmt::Display for EvalAltResult { Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?, Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?, - Self::ErrorAssignmentToConstant(s, _) => { - write!(f, "Cannot assign to constant '{}'", s)? - } + Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot assign to constant {}", s)?, Self::ErrorMismatchOutputType(s, r, _) => { write!(f, "Output type is incorrect: {} (expecting {})", r, s)? } @@ -276,7 +282,7 @@ impl EvalAltResult { Self::ErrorParsing(_, _) => false, Self::ErrorFunctionNotFound(_, _) - | Self::ErrorInFunctionCall(_, _, _) + | Self::ErrorInFunctionCall(_, _, _, _) | Self::ErrorInModule(_, _, _) | Self::ErrorUnboundThis(_) | Self::ErrorMismatchDataType(_, _, _) @@ -334,7 +340,7 @@ impl EvalAltResult { Self::ErrorParsing(_, pos) | Self::ErrorFunctionNotFound(_, pos) - | Self::ErrorInFunctionCall(_, _, pos) + | Self::ErrorInFunctionCall(_, _, _, pos) | Self::ErrorInModule(_, _, pos) | Self::ErrorUnboundThis(pos) | Self::ErrorMismatchDataType(_, _, pos) @@ -367,7 +373,7 @@ impl EvalAltResult { Self::ErrorParsing(_, pos) | Self::ErrorFunctionNotFound(_, pos) - | Self::ErrorInFunctionCall(_, _, pos) + | Self::ErrorInFunctionCall(_, _, _, pos) | Self::ErrorInModule(_, _, pos) | Self::ErrorUnboundThis(pos) | Self::ErrorMismatchDataType(_, _, pos) diff --git a/src/scope.rs b/src/scope.rs index 4386bae7..d60aab40 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -445,7 +445,7 @@ impl<'a> Scope<'a> { /// let mut my_scope = Scope::new(); /// /// my_scope.push("x", 42_i64); - /// my_scope.push_constant("foo", "hello".to_string()); + /// my_scope.push_constant("foo", "hello"); /// /// let mut iter = my_scope.iter(); /// diff --git a/src/syntax.rs b/src/syntax.rs index 8ce578fa..65b6513c 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -64,7 +64,7 @@ impl Expression<'_> { impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> { /// Evaluate an [expression tree][Expression]. /// - /// ## WARNING - Low Level API + /// # WARNING - Low Level API /// /// This function is very low level. It evaluates an expression from an [`AST`][crate::AST]. #[inline(always)] @@ -210,7 +210,7 @@ impl Engine { } /// Register a custom syntax with the [`Engine`]. /// - /// ## WARNING - Low Level API + /// # WARNING - Low Level API /// /// This function is very low level. /// diff --git a/src/token.rs b/src/token.rs index c4f8d74f..7ae8ba8a 100644 --- a/src/token.rs +++ b/src/token.rs @@ -149,7 +149,7 @@ impl fmt::Debug for Position { /// _(INTERNALS)_ A Rhai language token. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, PartialEq, Clone)] @@ -741,7 +741,7 @@ impl From for String { /// _(INTERNALS)_ State of the tokenizer. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[derive(Debug, Clone, Eq, PartialEq, Default)] @@ -763,7 +763,7 @@ pub struct TokenizeState { /// _(INTERNALS)_ Trait that encapsulates a peekable character input stream. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This trait is volatile and may change. pub trait InputStream { @@ -777,7 +777,7 @@ pub trait InputStream { /// _(INTERNALS)_ Parse a string literal wrapped by `enclosing_char`. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. pub fn parse_string_literal( @@ -968,7 +968,7 @@ fn scan_block_comment( /// _(INTERNALS)_ Get the next token from the `stream`. /// Exported under the `internals` feature only. /// -/// ## WARNING +/// # WARNING /// /// This type is volatile and may change. #[inline] diff --git a/src/utils.rs b/src/utils.rs index 9043bb4a..c43a5aaf 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -212,6 +212,12 @@ impl From<&str> for ImmutableString { Self(value.to_string().into()) } } +impl From<&String> for ImmutableString { + #[inline(always)] + fn from(value: &String) -> Self { + Self(value.to_string().into()) + } +} impl From for ImmutableString { #[inline(always)] fn from(value: String) -> Self { diff --git a/tests/fn_ptr.rs b/tests/fn_ptr.rs index 6a37af62..9f175bd2 100644 --- a/tests/fn_ptr.rs +++ b/tests/fn_ptr.rs @@ -73,7 +73,7 @@ fn test_fn_ptr() -> Result<(), Box> { "# ) .expect_err("should error"), - EvalAltResult::ErrorInFunctionCall(fn_name, err, _) + EvalAltResult::ErrorInFunctionCall(fn_name, _, err, _) if fn_name == "foo" && matches!(*err, EvalAltResult::ErrorUnboundThis(_)) )); diff --git a/tests/modules.rs b/tests/modules.rs index 26c4af8c..eee696fb 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -227,7 +227,7 @@ fn test_module_resolver() -> Result<(), Box> { "# ) .expect_err("should error"), - EvalAltResult::ErrorInFunctionCall(fn_name, _, _) if fn_name == "foo" + EvalAltResult::ErrorInFunctionCall(fn_name, _, _, _) if fn_name == "foo" )); engine.set_max_modules(1000);