diff --git a/RELEASES.md b/RELEASES.md index 7f19875a..1f0a1fed 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -7,20 +7,35 @@ Version 0.19.6 This version adds the `switch` statement. It also allows exposing selected module functions (usually methods) to the global namespace. +This is very convenient when encapsulating the API of a custom Rust type into a module while having methods +and iterators registered on the custom type work normally. + +Bug fixes +--------- + +* Custom syntax that introduces a shadowing variable now works properly. + +Breaking changes +---------------- + +* `Module::set_fn`, `Module::set_raw_fn` and `Module::set_fn_XXX_mut` all take an additional parameter of `FnNamespace`. +* `unless` is now a reserved keyword. New features ------------ * `switch` statement. +* `do ... while` and `do ... until` statement. * `Engine::register_module` to register a module as a sub-module in the global namespace. -* `Module::get_fn_namespace` and `Module::set_fn_namespace` can expose a module function to the global namespace. This is convenient when registering an API for a custom type. * `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace. -* `#[rhai_fn(gobal)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type. +* `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type. +* `Module::set_getter_fn`, `Module::set_setter_fn`, `Module::set_indexer_get_fn`, `Module::set_indexer_set_fn` all expose the function to the global namespace by default. This is convenient when registering an API for a custom type. +* `#[rhai_fn(global)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type. Enhancements ------------ -* New constant `Dynamic::UNIT`. +* New constants under `Dynamic` including `UNIT`, `TRUE`, `FALSE`, `ZERO`, `ONE` etc. Version 0.19.5 diff --git a/benches/eval_module.rs b/benches/eval_module.rs index de74ab24..ac6f56d4 100644 --- a/benches/eval_module.rs +++ b/benches/eval_module.rs @@ -26,8 +26,7 @@ fn bench_eval_module(bench: &mut Bencher) { .compile( r#" fn foo(x) { x - 1 } - import "testing" as t; - t::bar(41) + testing::bar(41) "#, ) .unwrap(); diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 64df0cd3..f1d18fcd 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -215,14 +215,18 @@ impl ExportedParams for ExportedFnParams { return Err(syn::Error::new(s.span(), "extraneous value")) } ("global", None) => { - if namespace.is_some() { - return Err(syn::Error::new(key.span(), "conflicting namespace")); + if let Some(ns) = namespace { + if ns != FnNamespaceAccess::Global { + return Err(syn::Error::new(key.span(), "conflicting namespace")); + } } namespace = Some(FnNamespaceAccess::Global); } ("internal", None) => { - if namespace.is_some() { - return Err(syn::Error::new(key.span(), "conflicting namespace")); + if let Some(ns) = namespace { + if ns != FnNamespaceAccess::Internal { + return Err(syn::Error::new(key.span(), "conflicting namespace")); + } } namespace = Some(FnNamespaceAccess::Internal); } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 3981e3b9..76d7648a 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -129,17 +129,18 @@ pub(crate) fn generate_body( } for fn_literal in reg_names { - set_fn_stmts.push( + let ns_str = syn::Ident::new( match namespace { - FnNamespaceAccess::Global => syn::parse2::(quote! { - m.set_fn(#fn_literal, FnNamespace::Global, FnAccess::Public, &[#(#fn_input_types),*], - #fn_token_name().into()); - }), - FnNamespaceAccess::Internal => syn::parse2::(quote! { - m.set_fn(#fn_literal, FnNamespace::Internal, FnAccess::Public, &[#(#fn_input_types),*], - #fn_token_name().into()); - }), - } + FnNamespaceAccess::Global => "Global", + FnNamespaceAccess::Internal => "Internal", + }, + fn_literal.span(), + ); + set_fn_stmts.push( + syn::parse2::(quote! { + m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, &[#(#fn_input_types),*], + #fn_token_name().into()); + }) .unwrap(), ); } diff --git a/codegen/ui_tests/rhai_fn_global_multiple.rs b/codegen/ui_tests/rhai_fn_global_multiple.rs index 45e2e325..6b670112 100644 --- a/codegen/ui_tests/rhai_fn_global_multiple.rs +++ b/codegen/ui_tests/rhai_fn_global_multiple.rs @@ -9,7 +9,7 @@ pub struct Point { #[export_module] pub mod test_module { pub use super::Point; - #[rhai_fn(global, global)] + #[rhai_fn(global, internal)] pub fn test_fn(input: Point) -> bool { input.x > input.y } diff --git a/codegen/ui_tests/rhai_fn_global_multiple.stderr b/codegen/ui_tests/rhai_fn_global_multiple.stderr index f66ebbb7..93da770d 100644 --- a/codegen/ui_tests/rhai_fn_global_multiple.stderr +++ b/codegen/ui_tests/rhai_fn_global_multiple.stderr @@ -1,8 +1,8 @@ error: conflicting namespace --> $DIR/rhai_fn_global_multiple.rs:12:23 | -12 | #[rhai_fn(global, global)] - | ^^^^^^ +12 | #[rhai_fn(global, internal)] + | ^^^^^^^^ error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_global_multiple.rs:23:8 diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 8bdce36f..73332147 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -79,6 +79,7 @@ The Rhai Scripting Language 9. [If Statement](language/if.md) 10. [Switch Expression](language/switch.md) 11. [While Loop](language/while.md) + 11. [Do Loop](language/do.md) 12. [Loop Statement](language/loop.md) 13. [For Loop](language/for.md) 14. [Return Values](language/return.md) diff --git a/doc/src/appendix/keywords.md b/doc/src/appendix/keywords.md index ad8d3e26..0e542dcf 100644 --- a/doc/src/appendix/keywords.md +++ b/doc/src/appendix/keywords.md @@ -14,7 +14,9 @@ Keywords List | `if` | if statement | | no | | | `else` | else block of if statement | | no | | | `switch` | matching | | no | | -| `while` | while loop | | no | | +| `do` | looping | | no | | +| `while` | 1) while loop
2) condition for do loop | | no | | +| `until` | do loop | | no | | | `loop` | infinite loop | | no | | | `for` | for loop | | no | | | `in` | 1) containment test
2) part of for loop | | no | | @@ -48,11 +50,11 @@ Reserved Keywords | `var` | variable declaration | | `static` | variable declaration | | `shared` | share value | -| `do` | looping | | `each` | looping | | `then` | control flow | | `goto` | control flow | | `exit` | control flow | +| `unless` | control flow | | `match` | matching | | `case` | matching | | `public` | function/field access | diff --git a/doc/src/engine/custom-syntax.md b/doc/src/engine/custom-syntax.md index c9b77c90..85d385aa 100644 --- a/doc/src/engine/custom-syntax.md +++ b/doc/src/engine/custom-syntax.md @@ -296,7 +296,7 @@ engine.register_custom_syntax_raw( "cleanup" => Ok(None), cmd => Err(ParseError(Box::new(ParseErrorType::BadInput( LexError::ImproperSymbol(format!("Improper command: {}", cmd)) - )), NO_POS)), + )), Position::NONE)), }, // perform command arg ... 3 => match (stream[1].as_str(), stream[2].as_str()) { @@ -311,7 +311,7 @@ engine.register_custom_syntax_raw( LexError::ImproperSymbol( format!("Invalid argument for command {}: {}", cmd, arg) ) - )), NO_POS)), + )), Position::NONE)), }, _ => unreachable!(), }, @@ -336,8 +336,8 @@ where: The return value is `Result, ParseError>` where: -| Value | Description | -| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Ok(None)` | parsing complete and there are no more symbols to match | -| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` | -| `Err(ParseError)` | error that is reflected back to the [`Engine`].
Normally this is `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), NO_POS)` to indicate that there is a syntax error, but it can be any `ParseError`. | +| Value | Description | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Ok(None)` | parsing complete and there are no more symbols to match | +| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` | +| `Err(ParseError)` | error that is reflected back to the [`Engine`].
Normally this is `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate that there is a syntax error, but it can be any `ParseError`. | diff --git a/doc/src/engine/var.md b/doc/src/engine/var.md index 9abfe0f0..f21a5754 100644 --- a/doc/src/engine/var.md +++ b/doc/src/engine/var.md @@ -21,11 +21,11 @@ engine.on_var(|name, index, context| { "MYSTIC_NUMBER" => Ok(Some((42 as INT).into())), // Override a variable - make it not found even if it exists! "DO_NOT_USE" => Err(Box::new( - EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS) + EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE) )), // Silently maps 'chameleon' into 'innocent'. "chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new( - EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS) + EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE) )), // Return Ok(None) to continue with the normal variable resolution process. _ => Ok(None) @@ -83,8 +83,8 @@ where: The return value is `Result, Box>` where: -| Value | Description | -| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] | -| `Ok(Some(Dynamic))` | value of the variable, treated as a constant | -| `Err(Box)` | error that is reflected back to the [`Engine`].
Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, NO_POS)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. | +| Value | Description | +| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] | +| `Ok(Some(Dynamic))` | value of the variable, treated as a constant | +| `Err(Box)` | error that is reflected back to the [`Engine`].
Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, Position::NONE)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. | diff --git a/doc/src/language/do.md b/doc/src/language/do.md new file mode 100644 index 00000000..c1129944 --- /dev/null +++ b/doc/src/language/do.md @@ -0,0 +1,28 @@ +`do` Loop +========= + +{{#include ../links.md}} + +`do` loops have two opposite variants: `do` ... `while` and `do` ... `until`. + +Like the `while` loop, `continue` can be used to skip to the next iteration, by-passing all following statements; +`break` can be used to break out of the loop unconditionally. + +```rust +let x = 10; + +do { + x -= 1; + if x < 6 { continue; } // skip to the next iteration + print(x); + if x == 5 { break; } // break out of do loop +} while x > 0; + + +do { + x -= 1; + if x < 6 { continue; } // skip to the next iteration + print(x); + if x == 5 { break; } // break out of do loop +} until x == 0; +``` diff --git a/doc/src/plugins/function.md b/doc/src/plugins/function.md index 6c47575f..03f8361d 100644 --- a/doc/src/plugins/function.md +++ b/doc/src/plugins/function.md @@ -118,7 +118,7 @@ pub fn greet(context: NativeCallContext, callback: FnPtr) The native call context is also useful in another scenario: protecting a function from malicious scripts. ```rust -use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS}; +use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position}; use rhai::plugin::*; // a "prelude" import for macros // This function builds an array of arbitrary size, but is protected @@ -138,7 +138,7 @@ pub fn grow(context: NativeCallContext, size: i64) "Size to grow".to_string(), context.engine().max_array_size(), size as usize, - NO_POS, + Position::NONE, ).into(); } diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index ee55ab96..cbeb304d 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -428,7 +428,7 @@ mod my_module { The native call context is also useful in another scenario: protecting a function from malicious scripts. ```rust -use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS}; +use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position}; use rhai::plugin::*; // a "prelude" import for macros #[export_module] @@ -449,7 +449,7 @@ mod my_module { "Size to grow".to_string(), context.engine().max_array_size(), size as usize, - NO_POS, + Position::NONE, ).into(); } diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 7f58b103..937f85e9 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -60,7 +60,7 @@ engine.register_module("calc", module); engine.eval::("calc::inc(41)")? == 42; // refer to the 'Calc' module ``` -`Module::set_fn_namespace` can expose functions (usually _methods_) in the module +`Module::set_fn_XXX_mut` can expose functions (usually _methods_) in the module to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] can work as expected. Type iterators, because of their special nature, are always exposed to the _global_ namespace. @@ -69,13 +69,12 @@ Type iterators, because of their special nature, are always exposed to the _glob use rhai::{Engine, Module, FnNamespace}; let mut module = Module::new(); // new module -let hash = module.set_fn_1_mut("inc", // add new method + +// Expose method 'inc' to the global namespace (default is 'Internal') +module.set_fn_1_mut("inc", FnNamespace::Global, |x: &mut i64| Ok(x+1) ); -// Expose 'inc' to the global namespace (default is 'Internal') -module.set_fn_namespace(hash, FnNamespace::Global); - // Load the module into the Engine as a sub-module named 'calc' let mut engine = Engine::new(); engine.register_module("calc", module); diff --git a/src/ast.rs b/src/ast.rs index 575ccf53..5e71db1e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,6 +1,7 @@ //! Module defining the AST (abstract syntax tree). use crate::dynamic::Union; +use crate::fn_native::shared_make_mut; use crate::module::NamespaceRef; use crate::stdlib::{ borrow::Cow, @@ -18,7 +19,7 @@ use crate::syntax::FnCustomSyntaxEval; use crate::token::Token; use crate::utils::StraightHasherBuilder; use crate::{ - Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT, NO_POS, + Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT, }; #[cfg(not(feature = "no_float"))] @@ -58,7 +59,7 @@ impl FnAccess { } } -/// _[INTERNALS]_ A type containing information on a scripted function. +/// _(INTERNALS)_ A type containing information on a scripted function. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -108,13 +109,13 @@ impl fmt::Display for ScriptFnDef { /// /// # Thread Safety /// -/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. +/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. #[derive(Debug, Clone)] pub struct AST( /// Global statements. Vec, /// Script-defined functions. - Module, + Shared, ); impl Default for AST { @@ -124,10 +125,10 @@ impl Default for AST { } impl AST { - /// Create a new `AST`. + /// Create a new [`AST`]. #[inline(always)] - pub fn new(statements: impl IntoIterator, lib: Module) -> Self { - Self(statements.into_iter().collect(), lib) + pub fn new(statements: impl IntoIterator, lib: impl Into>) -> Self { + Self(statements.into_iter().collect(), lib.into()) } /// Get the statements. #[cfg(not(feature = "internals"))] @@ -135,7 +136,7 @@ impl AST { pub(crate) fn statements(&self) -> &[Stmt] { &self.0 } - /// _[INTERNALS]_ Get the statements. + /// _(INTERNALS)_ Get the statements. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[deprecated(note = "this method is volatile and may change")] @@ -149,13 +150,18 @@ impl AST { pub(crate) fn statements_mut(&mut self) -> &mut Vec { &mut self.0 } - /// Get the internal `Module` containing all script-defined functions. + /// Get the internal shared [`Module`] containing all script-defined functions. + #[inline(always)] + pub(crate) fn shared_lib(&self) -> Shared { + self.1.clone() + } + /// Get the internal [`Module`] containing all script-defined functions. #[cfg(not(feature = "internals"))] #[inline(always)] pub(crate) fn lib(&self) -> &Module { &self.1 } - /// _[INTERNALS]_ Get the internal `Module` containing all script-defined functions. + /// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[deprecated(note = "this method is volatile and may change")] @@ -163,7 +169,7 @@ impl AST { pub fn lib(&self) -> &Module { &self.1 } - /// Clone the `AST`'s functions into a new `AST`. + /// Clone the [`AST`]'s functions into a new [`AST`]. /// No statements are cloned. /// /// This operation is cheap because functions are shared. @@ -172,7 +178,7 @@ impl AST { pub fn clone_functions_only(&self) -> Self { self.clone_functions_only_filtered(|_, _, _, _, _| true) } - /// Clone the `AST`'s functions into a new `AST` based on a filter predicate. + /// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate. /// No statements are cloned. /// /// This operation is cheap because functions are shared. @@ -184,24 +190,24 @@ impl AST { ) -> Self { let mut functions: Module = Default::default(); functions.merge_filtered(&self.1, &mut filter); - Self(Default::default(), functions) + Self(Default::default(), functions.into()) } - /// Clone the `AST`'s script statements into a new `AST`. + /// Clone the [`AST`]'s script statements into a new [`AST`]. /// No functions are cloned. #[inline(always)] pub fn clone_statements_only(&self) -> Self { Self(self.0.clone(), Default::default()) } - /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version + /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version /// is returned. /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. /// - /// All script-defined functions in the second `AST` overwrite similarly-named functions - /// in the first `AST` with the same number of parameters. + /// All script-defined functions in the second [`AST`] overwrite similarly-named functions + /// in the first [`AST`] with the same number of parameters. /// /// # Example /// @@ -245,15 +251,15 @@ impl AST { pub fn merge(&self, other: &Self) -> Self { self.merge_filtered(other, |_, _, _, _, _| true) } - /// Combine one `AST` with another. The second `AST` is consumed. + /// Combine one [`AST`] with another. The second [`AST`] is consumed. /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. /// - /// All script-defined functions in the second `AST` overwrite similarly-named functions - /// in the first `AST` with the same number of parameters. + /// All script-defined functions in the second [`AST`] overwrite similarly-named functions + /// in the first [`AST`] with the same number of parameters. /// /// # Example /// @@ -297,16 +303,16 @@ impl AST { pub fn combine(&mut self, other: Self) -> &mut Self { self.combine_filtered(other, |_, _, _, _, _| true) } - /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version + /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version /// is returned. /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. /// - /// All script-defined functions in the second `AST` are first selected based on a filter - /// predicate, then overwrite similarly-named functions in the first `AST` with the + /// All script-defined functions in the second [`AST`] are first selected based on a filter + /// predicate, then overwrite similarly-named functions in the first [`AST`] with the /// same number of parameters. /// /// # Example @@ -368,20 +374,20 @@ impl AST { (true, true) => vec![], }; - let mut functions = functions.clone(); + let mut functions = functions.as_ref().clone(); functions.merge_filtered(&other.1, &mut filter); Self::new(ast, functions) } - /// Combine one `AST` with another. The second `AST` is consumed. + /// Combine one [`AST`] with another. The second [`AST`] is consumed. /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. + /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. + /// Of course, if the first [`AST`] uses a `return` statement at the end, then + /// the second [`AST`] will essentially be dead code. /// - /// All script-defined functions in the second `AST` are first selected based on a filter - /// predicate, then overwrite similarly-named functions in the first `AST` with the + /// All script-defined functions in the second [`AST`] are first selected based on a filter + /// predicate, then overwrite similarly-named functions in the first [`AST`] with the /// same number of parameters. /// /// # Example @@ -430,9 +436,10 @@ impl AST { other: Self, mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> &mut Self { - let Self(ref mut statements, ref mut functions) = self; - statements.extend(other.0.into_iter()); - functions.merge_filtered(&other.1, &mut filter); + self.0.extend(other.0.into_iter()); + if !other.1.is_empty() { + shared_make_mut(&mut self.1).merge_filtered(&other.1, &mut filter); + } self } /// Filter out the functions, retaining only some based on a filter predicate. @@ -463,8 +470,11 @@ impl AST { pub fn retain_functions( &mut self, filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool, - ) { - self.1.retain_script_functions(filter); + ) -> &mut Self { + if !self.1.is_empty() { + shared_make_mut(&mut self.1).retain_script_functions(filter); + } + self } /// Iterate through all functions #[cfg(not(feature = "no_function"))] @@ -474,13 +484,13 @@ impl AST { ) -> impl Iterator)> + 'a { self.1.iter_script_fn() } - /// Clear all function definitions in the `AST`. + /// Clear all function definitions in the [`AST`]. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clear_functions(&mut self) { self.1 = Default::default(); } - /// Clear all statements in the `AST`, leaving only function definitions. + /// Clear all statements in the [`AST`], leaving only function definitions. #[inline(always)] pub fn clear_statements(&mut self) { self.0 = vec![]; @@ -557,7 +567,7 @@ impl IdentX { } } -/// _[INTERNALS]_ A type encapsulating the mode of a `return`/`throw` statement. +/// _(INTERNALS)_ A type encapsulating the mode of a `return`/`throw` statement. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -571,7 +581,7 @@ pub enum ReturnType { Exception, } -/// _[INTERNALS]_ A statement. +/// _(INTERNALS)_ A statement. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -581,42 +591,42 @@ pub enum ReturnType { pub enum Stmt { /// No-op. Noop(Position), - /// if expr { stmt } else { stmt } + /// `if` expr `{` stmt `}` `else` `{` stmt `}` If(Expr, Box<(Stmt, Option)>, Position), - /// switch expr { literal or _ => stmt, ... } + /// `switch` expr `{` literal or _ `=>` stmt `,` ... `}` Switch( Expr, Box<(HashMap, Option)>, Position, ), - /// while expr { stmt } + /// `while` expr `{` stmt `}` While(Expr, Box, Position), - /// loop { stmt } - Loop(Box, Position), - /// for id in expr { stmt } + /// `do` `{` stmt `}` `while`|`until` expr + Do(Box, Expr, bool, Position), + /// `for` id `in` expr `{` stmt `}` For(Expr, Box<(String, Stmt)>, Position), - /// [export] let id = expr + /// \[`export`\] `let` id `=` expr Let(Box, Option, bool, Position), - /// [export] const id = expr + /// \[`export`\] `const` id `=` expr Const(Box, Option, bool, Position), - /// expr op= expr + /// expr op`=` expr Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position), - /// { stmt; ... } + /// `{` stmt`;` ... `}` Block(Vec, Position), - /// try { stmt; ... } catch ( var ) { stmt; ... } + /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` TryCatch(Box<(Stmt, Option, Stmt)>, Position, Position), - /// expr + /// [expression][Expr] Expr(Expr), - /// continue + /// `continue` Continue(Position), - /// break + /// `break` Break(Position), - /// return/throw + /// `return`/`throw` Return((ReturnType, Position), Option, Position), - /// import expr as var + /// `import` expr `as` var #[cfg(not(feature = "no_module"))] Import(Expr, Option>, Position), - /// export var as var, ... + /// `export` var `as` var `,` ... #[cfg(not(feature = "no_module"))] Export(Vec<(IdentX, Option)>, Position), /// Convert a variable to shared. @@ -627,19 +637,19 @@ pub enum Stmt { impl Default for Stmt { #[inline(always)] fn default() -> Self { - Self::Noop(NO_POS) + Self::Noop(Position::NONE) } } impl Stmt { - /// Is this statement `Noop`? + /// Is this statement [`Noop`][Stmt::Noop]? pub fn is_noop(&self) -> bool { match self { Self::Noop(_) => true, _ => false, } } - /// Get the `Position` of this statement. + /// Get the [`Position`] of this statement. pub fn position(&self) -> Position { match self { Self::Noop(pos) @@ -650,7 +660,7 @@ impl Stmt { | Self::If(_, _, pos) | Self::Switch(_, _, pos) | Self::While(_, _, pos) - | Self::Loop(_, pos) + | Self::Do(_, _, _, pos) | Self::For(_, _, pos) | Self::Return((_, pos), _, _) | Self::Let(_, _, _, pos) @@ -668,7 +678,7 @@ impl Stmt { Self::Share(x) => x.pos, } } - /// Override the `Position` of this statement. + /// Override the [`Position`] of this statement. pub fn set_position(&mut self, new_pos: Position) -> &mut Self { match self { Self::Noop(pos) @@ -679,7 +689,7 @@ impl Stmt { | Self::If(_, _, pos) | Self::Switch(_, _, pos) | Self::While(_, _, pos) - | Self::Loop(_, pos) + | Self::Do(_, _, _, pos) | Self::For(_, _, pos) | Self::Return((_, pos), _, _) | Self::Let(_, _, _, pos) @@ -707,7 +717,6 @@ impl Stmt { Self::If(_, _, _) | Self::Switch(_, _, _) | Self::While(_, _, _) - | Self::Loop(_, _) | Self::For(_, _, _) | Self::Block(_, _) | Self::TryCatch(_, _, _) => true, @@ -719,6 +728,7 @@ impl Stmt { | Self::Const(_, _, _, _) | Self::Assignment(_, _) | Self::Expr(_) + | Self::Do(_, _, _, _) | Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, @@ -727,7 +737,7 @@ impl Stmt { Self::Import(_, _, _) | Self::Export(_, _) => false, #[cfg(not(feature = "no_closure"))] - Self::Share(_) => false, + Self::Share(_) => unreachable!(), } } /// Is this statement _pure_? @@ -745,8 +755,9 @@ impl Stmt { && x.0.values().all(Stmt::is_pure) && x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) } - Self::While(condition, block, _) => condition.is_pure() && block.is_pure(), - Self::Loop(block, _) => block.is_pure(), + Self::While(condition, block, _) | Self::Do(block, condition, _, _) => { + condition.is_pure() && block.is_pure() + } Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(), Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), @@ -764,7 +775,7 @@ impl Stmt { } } -/// _[INTERNALS]_ A custom syntax definition. +/// _(INTERNALS)_ A custom syntax definition. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -786,19 +797,19 @@ impl fmt::Debug for CustomExpr { } impl CustomExpr { - /// Get the keywords for this `CustomExpr`. + /// Get the keywords for this custom syntax. #[inline(always)] pub fn keywords(&self) -> &[Expr] { &self.keywords } - /// Get the implementation function for this `CustomExpr`. + /// Get the implementation function for this custom syntax. #[inline(always)] pub fn func(&self) -> &FnCustomSyntaxEval { self.func.as_ref() } } -/// _[INTERNALS]_ A binary expression. +/// _(INTERNALS)_ A binary expression. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -812,7 +823,7 @@ pub struct BinaryExpr { pub rhs: Expr, } -/// _[INTERNALS]_ A function call. +/// _(INTERNALS)_ A function call. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -822,25 +833,24 @@ pub struct BinaryExpr { pub struct FnCallExpr { /// Pre-calculated hash for a script-defined function of the same name and number of parameters. pub hash: u64, - /// Call native functions only? Set to `true` to skip searching for script-defined function overrides + /// Call native functions only? Set to [`true`] to skip searching for script-defined function overrides /// when it is certain that the function must be native (e.g. an operator). pub native_only: bool, /// Does this function call capture the parent scope? pub capture: bool, /// Default value when the function is not found, mostly used to provide a default for comparison functions. - /// Type is `bool` in order for `FnCallInfo` to be `Hash` - pub def_value: Option, + pub def_value: Option, /// Namespace of the function, if any. Boxed because it occurs rarely. pub namespace: Option>, /// Function name. - /// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls - /// and the function names are predictable, so no need to allocate a new `String`. + /// Use [`Cow<'static, str>`][Cow] because a lot of operators (e.g. `==`, `>=`) are implemented as + /// function calls and the function names are predictable, so no need to allocate a new [`String`]. pub name: Cow<'static, str>, /// List of function call arguments. pub args: StaticVec, } -/// _[INTERNALS]_ An expression sub-tree. +/// _(INTERNALS)_ An expression sub-tree. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -849,9 +859,11 @@ pub struct FnCallExpr { #[derive(Debug, Clone)] pub enum Expr { /// Dynamic constant. - /// Used to hold either an Array or Map literal for quick cloning. + /// Used to hold either an [`Array`] or [`Map`] literal for quick cloning. /// All other primitive data types should use the appropriate variants for better speed. DynamicConstant(Box, Position), + /// Boolean constant. + BoolConstant(bool, Position), /// Integer constant. IntegerConstant(INT, Position), /// Floating-point constant. @@ -859,39 +871,35 @@ pub enum Expr { FloatConstant(FLOAT, Position), /// Character constant. CharConstant(char, Position), - /// String constant. + /// [String][ImmutableString] constant. StringConstant(ImmutableString, Position), - /// FnPtr constant. + /// [`FnPtr`] constant. FnPointer(ImmutableString, Position), /// [ expr, ... ] Array(Box>, Position), /// #{ name:expr, ... } Map(Box>, Position), - /// true - True(Position), - /// false - False(Position), /// () Unit(Position), /// Variable access - (optional index, optional modules, hash, variable name) Variable(Box<(Option, Option>, u64, IdentX)>), /// Property access - (getter, setter), prop Property(Box<((ImmutableString, ImmutableString), IdentX)>), - /// { stmt } + /// { [statement][Stmt] } Stmt(Box>, Position), - /// Wrapped expression - should not be optimized away. + /// Wrapped [expression][`Expr`] - should not be optimized away. Expr(Box), - /// func(expr, ... ) + /// func `(` expr `,` ... `)` FnCall(Box, Position), - /// lhs.rhs + /// lhs `.` rhs Dot(Box, Position), - /// expr[expr] + /// expr `[` expr `]` Index(Box, Position), - /// lhs in rhs + /// lhs `in` rhs In(Box, Position), - /// lhs && rhs + /// lhs `&&` rhs And(Box, Position), - /// lhs || rhs + /// lhs `||` rhs Or(Box, Position), /// Custom syntax Custom(Box, Position), @@ -900,14 +908,14 @@ pub enum Expr { impl Default for Expr { #[inline(always)] fn default() -> Self { - Self::Unit(NO_POS) + Self::Unit(Position::NONE) } } impl Expr { - /// Get the `Dynamic` value of a constant expression. + /// Get the [`Dynamic`] value of a constant expression. /// - /// Returns `None` if the expression is not constant. + /// Returns [`None`] if the expression is not constant. pub fn get_constant_value(&self) -> Option { Some(match self { Self::Expr(x) => return x.get_constant_value(), @@ -922,8 +930,7 @@ impl Expr { x.clone(), Default::default(), )))), - Self::True(_) => true.into(), - Self::False(_) => false.into(), + Self::BoolConstant(x, _) => (*x).into(), Self::Unit(_) => ().into(), #[cfg(not(feature = "no_index"))] @@ -959,7 +966,7 @@ impl Expr { _ => None, } } - /// Get the `Position` of the expression. + /// Get the [`Position`] of the expression. pub fn position(&self) -> Position { match self { Self::Expr(x) => x.position(), @@ -968,6 +975,7 @@ impl Expr { Self::FloatConstant(_, pos) => *pos, Self::DynamicConstant(_, pos) => *pos, + Self::BoolConstant(_, pos) => *pos, Self::IntegerConstant(_, pos) => *pos, Self::CharConstant(_, pos) => *pos, Self::StringConstant(_, pos) => *pos, @@ -981,14 +989,14 @@ impl Expr { Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(), - Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos, + Self::Unit(pos) => *pos, Self::Dot(x, _) | Self::Index(x, _) => x.lhs.position(), Self::Custom(_, pos) => *pos, } } - /// Override the `Position` of the expression. + /// Override the [`Position`] of the expression. pub fn set_position(&mut self, new_pos: Position) -> &mut Self { match self { Self::Expr(x) => { @@ -999,6 +1007,7 @@ impl Expr { Self::FloatConstant(_, pos) => *pos = new_pos, Self::DynamicConstant(_, pos) => *pos = new_pos, + Self::BoolConstant(_, pos) => *pos = new_pos, Self::IntegerConstant(_, pos) => *pos = new_pos, Self::CharConstant(_, pos) => *pos = new_pos, Self::StringConstant(_, pos) => *pos = new_pos, @@ -1010,7 +1019,7 @@ impl Expr { Self::Stmt(_, pos) => *pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos, Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos, - Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos, + Self::Unit(pos) => *pos = new_pos, Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos, Self::Custom(_, pos) => *pos = new_pos, } @@ -1056,12 +1065,11 @@ impl Expr { Self::FloatConstant(_, _) => true, Self::DynamicConstant(_, _) + | Self::BoolConstant(_, _) | Self::IntegerConstant(_, _) | Self::CharConstant(_, _) | Self::StringConstant(_, _) | Self::FnPointer(_, _) - | Self::True(_) - | Self::False(_) | Self::Unit(_) => true, // An array literal is constant if all items are constant @@ -1089,14 +1097,13 @@ impl Expr { Self::FloatConstant(_, _) => false, Self::DynamicConstant(_, _) + | Self::BoolConstant(_, _) | Self::IntegerConstant(_, _) | Self::CharConstant(_, _) | Self::FnPointer(_, _) | Self::In(_, _) | Self::And(_, _) | Self::Or(_, _) - | Self::True(_) - | Self::False(_) | Self::Unit(_) => false, Self::StringConstant(_, _) diff --git a/src/dynamic.rs b/src/dynamic.rs index 2a6a0220..412acafb 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1,4 +1,4 @@ -//! Helper module which defines the `Any` trait to to allow dynamic value handling. +//! Helper module which defines the [`Any`] trait to to allow dynamic value handling. use crate::fn_native::SendSync; use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; @@ -35,8 +35,6 @@ mod private { use crate::stdlib::any::Any; /// A sealed trait that prevents other crates from implementing [`Variant`]. - /// - /// [`Variant`]: super::Variant pub trait Sealed {} impl Sealed for T {} @@ -44,51 +42,48 @@ mod private { /// Trait to represent any type. /// -/// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type. -/// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`. +/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type. +/// Turn on the [`Sync`] feature to restrict it to only types that implement [`Send`] `+` [`Sync`]. #[cfg(not(feature = "sync"))] pub trait Variant: Any + private::Sealed { - /// Convert this `Variant` trait object to `&dyn Any`. + /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. fn as_any(&self) -> &dyn Any; - /// Convert this `Variant` trait object to `&mut dyn Any`. + /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. fn as_mut_any(&mut self) -> &mut dyn Any; - /// Convert this `Variant` trait object to an `Any` trait object. + /// Convert this [`Variant`] trait object to an [`Any`] trait object. fn as_box_any(self: Box) -> Box; /// Get the name of this type. fn type_name(&self) -> &'static str; - /// Convert into `Dynamic`. + /// Convert into [`Dynamic`]. fn into_dynamic(self) -> Dynamic; - /// Clone into `Dynamic`. + /// Clone into [`Dynamic`]. fn clone_into_dynamic(&self) -> Dynamic; } /// Trait to represent any type. -/// -/// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`), -/// `bool`, `String`, `char`, `Vec` (into `Array`) and `HashMap` (into `Map`). #[cfg(feature = "sync")] pub trait Variant: Any + Send + Sync + private::Sealed { - /// Convert this `Variant` trait object to `&dyn Any`. + /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. fn as_any(&self) -> &dyn Any; - /// Convert this `Variant` trait object to `&mut dyn Any`. + /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. fn as_mut_any(&mut self) -> &mut dyn Any; - /// Convert this `Variant` trait object to an `Any` trait object. + /// Convert this [`Variant`] trait object to an [`Any`] trait object. fn as_box_any(self: Box) -> Box; /// Get the name of this type. fn type_name(&self) -> &'static str; - /// Convert into `Dynamic`. + /// Convert into [`Dynamic`]. fn into_dynamic(self) -> Dynamic; - /// Clone into `Dynamic`. + /// Clone into [`Dynamic`]. fn clone_into_dynamic(&self) -> Dynamic; } @@ -114,7 +109,7 @@ impl Variant for T { } impl dyn Variant { - /// Is this `Variant` a specific type? + /// Is this [`Variant`] a specific type? #[inline(always)] pub fn is(&self) -> bool { TypeId::of::() == self.type_id() @@ -124,7 +119,7 @@ impl dyn Variant { /// Dynamic type containing any value. pub struct Dynamic(pub(crate) Union); -/// Internal `Dynamic` representation. +/// Internal [`Dynamic`] representation. /// /// Most variants are boxed to reduce the size. pub enum Union { @@ -149,14 +144,14 @@ pub enum Union { Shared(crate::Shared>), } -/// Underlying `Variant` read guard for `Dynamic`. +/// Underlying [`Variant`] read guard for [`Dynamic`]. /// /// This data structure provides transparent interoperability between -/// normal `Dynamic` and shared Dynamic values. +/// normal [`Dynamic`] and shared [`Dynamic`] values. #[derive(Debug)] pub struct DynamicReadLock<'d, T: Variant + Clone>(DynamicReadLockInner<'d, T>); -/// Different types of read guards for `DynamicReadLock`. +/// Different types of read guards for [`DynamicReadLock`]. #[derive(Debug)] enum DynamicReadLockInner<'d, T: Variant + Clone> { /// A simple reference to a non-shared value. @@ -166,7 +161,7 @@ enum DynamicReadLockInner<'d, T: Variant + Clone> { #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] Guard(crate::stdlib::cell::Ref<'d, Dynamic>), - /// A read guard to a shared `RwLock`. + /// A read guard to a shared [`RwLock`][std::sync::RwLock]. #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] Guard(crate::stdlib::sync::RwLockReadGuard<'d, Dynamic>), @@ -186,14 +181,14 @@ impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> { } } -/// Underlying `Variant` write guard for `Dynamic`. +/// Underlying [`Variant`] write guard for [`Dynamic`]. /// /// This data structure provides transparent interoperability between -/// normal `Dynamic` and shared Dynamic values. +/// normal [`Dynamic`] and shared [`Dynamic`] values. #[derive(Debug)] pub struct DynamicWriteLock<'d, T: Variant + Clone>(DynamicWriteLockInner<'d, T>); -/// Different types of write guards for `DynamicReadLock`. +/// Different types of write guards for [`DynamicReadLock`]. #[derive(Debug)] enum DynamicWriteLockInner<'d, T: Variant + Clone> { /// A simple mutable reference to a non-shared value. @@ -203,7 +198,7 @@ enum DynamicWriteLockInner<'d, T: Variant + Clone> { #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] Guard(crate::stdlib::cell::RefMut<'d, Dynamic>), - /// A write guard to a shared `RwLock`. + /// A write guard to a shared [`RwLock`][std::sync::RwLock]. #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] Guard(crate::stdlib::sync::RwLockWriteGuard<'d, Dynamic>), @@ -236,7 +231,7 @@ impl<'d, T: Variant + Clone> DerefMut for DynamicWriteLock<'d, T> { } impl Dynamic { - /// Does this `Dynamic` hold a variant data type + /// Does this [`Dynamic`] hold a variant data type /// instead of one of the support system primitive types? #[inline(always)] pub fn is_variant(&self) -> bool { @@ -245,7 +240,7 @@ impl Dynamic { _ => false, } } - /// Does this `Dynamic` hold a shared data type + /// Does this [`Dynamic`] hold a shared data type /// instead of one of the supported system primitive types? #[inline(always)] pub fn is_shared(&self) -> bool { @@ -255,9 +250,9 @@ impl Dynamic { _ => false, } } - /// Is the value held by this `Dynamic` a particular type? + /// Is the value held by this [`Dynamic`] a particular type? /// - /// If the `Dynamic` is a Shared variant checking is performed on + /// If the [`Dynamic`] is a shared variant checking is performed on /// top of it's internal value. #[inline(always)] pub fn is(&self) -> bool { @@ -269,11 +264,11 @@ impl Dynamic { self.type_id() == target_type_id } - /// Get the TypeId of the value held by this `Dynamic`. + /// Get the [`TypeId`] of the value held by this [`Dynamic`]. /// /// # Panics or Deadlocks When Value is Shared /// - /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). + /// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. pub fn type_id(&self) -> TypeId { match &self.0 { @@ -302,11 +297,11 @@ impl Dynamic { Union::Shared(cell) => (*cell.read().unwrap()).type_id(), } } - /// Get the name of the type of the value held by this `Dynamic`. + /// Get the name of the type of the value held by this [`Dynamic`]. /// /// # Panics or Deadlocks When Value is Shared /// - /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). + /// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. pub fn type_name(&self) -> &'static str { match &self.0 { @@ -518,29 +513,29 @@ impl Default for Dynamic { } impl Dynamic { - /// A `Dynamic` containing a `()`. + /// A [`Dynamic`] containing a `()`. pub const UNIT: Dynamic = Self(Union::Unit(())); - /// A `Dynamic` containing a `true`. + /// A [`Dynamic`] containing a `true`. pub const TRUE: Dynamic = Self(Union::Bool(true)); - /// A `Dynamic` containing a `false`. + /// A [`Dynamic`] containing a [`false`]. pub const FALSE: Dynamic = Self(Union::Bool(false)); - /// A `Dynamic` containing the integer zero. + /// A [`Dynamic`] containing the integer zero. pub const ZERO: Dynamic = Self(Union::Int(0)); - /// A `Dynamic` containing the integer one. + /// A [`Dynamic`] containing the integer one. pub const ONE: Dynamic = Self(Union::Int(1)); - /// A `Dynamic` containing the integer negative one. + /// A [`Dynamic`] containing the integer negative one. pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1)); - /// A `Dynamic` containing the floating-point zero. + /// A [`Dynamic`] containing the floating-point zero. #[cfg(not(feature = "no_float"))] pub const FLOAT_ZERO: Dynamic = Self(Union::Float(0.0)); - /// A `Dynamic` containing the floating-point one. + /// A [`Dynamic`] containing the floating-point one. #[cfg(not(feature = "no_float"))] pub const FLOAT_ONE: Dynamic = Self(Union::Float(1.0)); - /// A `Dynamic` containing the floating-point negative one. + /// A [`Dynamic`] containing the floating-point negative one. #[cfg(not(feature = "no_float"))] pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(-1.0)); - /// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is. + /// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is. /// /// # Safety /// @@ -548,11 +543,12 @@ impl Dynamic { /// /// # Notes /// - /// Beware that you need to pass in an `Array` type for it to be recognized as an `Array`. - /// A `Vec` does not get automatically converted to an `Array`, but will be a generic - /// restricted trait object instead, because `Vec` is not a supported standard type. + /// Beware that you need to pass in an [`Array`] type for it to be recognized as an [`Array`]. + /// A [`Vec`][Vec] does not get automatically converted to an [`Array`], but will be a generic + /// restricted trait object instead, because [`Vec`][Vec] is not a supported standard type. /// - /// Similarly, passing in a `HashMap` will not get a `Map` but a trait object. + /// Similarly, passing in a [`HashMap`][std::collections::HashMap] will not get a [`Map`] + /// but a trait object. /// /// # Examples /// @@ -657,15 +653,15 @@ impl Dynamic { Self(Union::Variant(Box::new(boxed))) } - /// Turn the `Dynamic` value into a shared `Dynamic` value backed by an `Rc>` - /// or `Arc>` depending on the `sync` feature. + /// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::RefCell]`<`[`Dynamic`]`>>` + /// or [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` depending on the [`Sync`] feature. /// - /// Shared `Dynamic` values are relatively cheap to clone as they simply increment the + /// Shared [`Dynamic`] values are relatively cheap to clone as they simply increment the /// reference counts. /// - /// Shared `Dynamic` values can be converted seamlessly to and from ordinary `Dynamic` values. + /// Shared [`Dynamic`] values can be converted seamlessly to and from ordinary [`Dynamic`] values. /// - /// If the `Dynamic` value is already shared, this method returns itself. + /// If the [`Dynamic`] value is already shared, this method returns itself. /// /// # Panics /// @@ -681,16 +677,16 @@ impl Dynamic { #[cfg(feature = "no_closure")] panic!("converting into a shared value is not supported under 'no_closure'"); } - /// Convert the `Dynamic` value into specific type. + /// Convert the [`Dynamic`] value into specific type. /// - /// Casting to a `Dynamic` just returns as is, but if it contains a shared value, - /// it is cloned into a `Dynamic` with a normal value. + /// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value, + /// it is cloned into a [`Dynamic`] with a normal value. /// - /// Returns `None` if types mismatched. + /// Returns [`None`] if types mismatched. /// /// # Panics or Deadlocks /// - /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). + /// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. /// /// These normally shouldn't occur since most operations in Rhai is single-threaded. @@ -811,19 +807,19 @@ impl Dynamic { _ => None, } } - /// Convert the `Dynamic` value into a specific type. + /// Convert the [`Dynamic`] value into a specific type. /// - /// Casting to a `Dynamic` just returns as is, but if it contains a shared value, - /// it is cloned into a `Dynamic` with a normal value. + /// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value, + /// it is cloned into a [`Dynamic`] with a normal value. /// - /// Returns `None` if types mismatched. + /// Returns [`None`] if types mismatched. /// /// # Panics or Deadlocks /// /// Panics if the cast fails (e.g. the type of the actual value is not the /// same as the specified type). /// - /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). + /// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. /// /// These normally shouldn't occur since most operations in Rhai is single-threaded. @@ -854,11 +850,11 @@ impl Dynamic { ) }) } - /// Flatten the `Dynamic` and clone it. + /// Flatten the [`Dynamic`] and clone it. /// - /// If the `Dynamic` is not a shared value, it returns a cloned copy. + /// If the [`Dynamic`] is not a shared value, it returns a cloned copy. /// - /// If the `Dynamic` is a shared value, it a cloned copy of the shared value. + /// If the [`Dynamic`] is a shared value, it a cloned copy of the shared value. #[inline(always)] pub fn flatten_clone(&self) -> Self { match &self.0 { @@ -873,11 +869,11 @@ impl Dynamic { _ => self.clone(), } } - /// Flatten the `Dynamic`. + /// Flatten the [`Dynamic`]. /// - /// If the `Dynamic` is not a shared value, it returns itself. + /// If the [`Dynamic`] is not a shared value, it returns itself. /// - /// If the `Dynamic` is a shared value, it returns the shared value if there are + /// If the [`Dynamic`] is a shared value, it returns the shared value if there are /// no outstanding references, or a cloned copy. #[inline(always)] pub fn flatten(self) -> Self { @@ -900,13 +896,13 @@ impl Dynamic { _ => self, } } - /// Is the `Dynamic` a shared value that is locked? + /// Is the [`Dynamic`] a shared value that is locked? /// /// ## Note /// - /// Under the `sync` feature, shared values use `RwLock` and they are never locked. - /// Access just waits until the `RwLock` is released. - /// So this method always returns `false` under `sync`. + /// Under the [`Sync`] feature, shared values use [`RwLock`][std::sync::RwLock] and they are never locked. + /// Access just waits until the [`RwLock`][std::sync::RwLock] is released. + /// So this method always returns [`false`] under [`Sync`]. #[inline(always)] pub fn is_locked(&self) -> bool { match self.0 { @@ -921,14 +917,14 @@ impl Dynamic { _ => false, } } - /// Get a reference of a specific type to the `Dynamic`. - /// Casting to `Dynamic` just returns a reference to it. + /// Get a reference of a specific type to the [`Dynamic`]. + /// Casting to [`Dynamic`] just returns a reference to it. /// - /// Returns `None` if the cast fails. + /// Returns [`None`] if the cast fails. /// /// # Panics or Deadlocks When Value is Shared /// - /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). + /// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. #[inline(always)] pub fn read_lock(&self) -> Option> { @@ -953,14 +949,14 @@ impl Dynamic { .map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))), } } - /// Get a mutable reference of a specific type to the `Dynamic`. - /// Casting to `Dynamic` just returns a mutable reference to it. + /// Get a mutable reference of a specific type to the [`Dynamic`]. + /// Casting to [`Dynamic`] just returns a mutable reference to it. /// - /// Returns `None` if the cast fails. + /// Returns [`None`] if the cast fails. /// /// # Panics or Deadlocks When Value is Shared /// - /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). + /// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. #[inline(always)] pub fn write_lock(&mut self) -> Option> { @@ -985,10 +981,10 @@ impl Dynamic { .map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))), } } - /// Get a reference of a specific type to the `Dynamic`. - /// Casting to `Dynamic` just returns a reference to it. + /// Get a reference of a specific type to the [`Dynamic`]. + /// Casting to [`Dynamic`] just returns a reference to it. /// - /// Returns `None` if the cast fails, or if the value is shared. + /// Returns [`None`] if the cast fails, or if the value is shared. #[inline(always)] pub(crate) fn downcast_ref(&self) -> Option<&T> { // Coded this way in order to maximally leverage potentials for dead-code removal. @@ -1074,10 +1070,10 @@ impl Dynamic { _ => None, } } - /// Get a mutable reference of a specific type to the `Dynamic`. - /// Casting to `Dynamic` just returns a mutable reference to it. + /// Get a mutable reference of a specific type to the [`Dynamic`]. + /// Casting to [`Dynamic`] just returns a mutable reference to it. /// - /// Returns `None` if the cast fails, or if the value is shared. + /// Returns [`None`] if the cast fails, or if the value is shared. #[inline(always)] pub(crate) fn downcast_mut(&mut self) -> Option<&mut T> { // Coded this way in order to maximally leverage potentials for dead-code removal. @@ -1157,7 +1153,7 @@ impl Dynamic { _ => None, } } - /// Cast the `Dynamic` as the system integer type `INT` and return it. + /// Cast the [`Dynamic`] as the system integer type [`INT`] and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] pub fn as_int(&self) -> Result { @@ -1168,7 +1164,7 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as the system floating-point type `FLOAT` and return it. + /// Cast the [`Dynamic`] as the system floating-point type [`FLOAT`] and return it. /// Returns the name of the actual type if the cast fails. #[cfg(not(feature = "no_float"))] #[inline(always)] @@ -1180,7 +1176,7 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a `bool` and return it. + /// Cast the [`Dynamic`] as a [`bool`] and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] pub fn as_bool(&self) -> Result { @@ -1191,7 +1187,7 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a `char` and return it. + /// Cast the [`Dynamic`] as a [`char`] and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] pub fn as_char(&self) -> Result { @@ -1202,7 +1198,7 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a string and return the string slice. + /// Cast the [`Dynamic`] as a [`String`] and return the string slice. /// Returns the name of the actual type if the cast fails. /// /// Cast is failing if `self` is Shared Dynamic @@ -1214,7 +1210,7 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Convert the `Dynamic` into `String` and return it. + /// Convert the [`Dynamic`] into a [`String`] and return it. /// If there are other references to the same string, a cloned copy is returned. /// Returns the name of the actual type if the cast fails. #[inline(always)] @@ -1222,7 +1218,7 @@ impl Dynamic { self.take_immutable_string() .map(ImmutableString::into_owned) } - /// Convert the `Dynamic` into `ImmutableString` and return it. + /// Convert the [`Dynamic`] into an [`ImmutableString`] and return it. /// Returns the name of the actual type if the cast fails. #[inline] pub fn take_immutable_string(self) -> Result { diff --git a/src/engine.rs b/src/engine.rs index 44af641d..ac747dfb 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,4 +1,4 @@ -//! Main module defining the script evaluation `Engine`. +//! Main module defining the script evaluation [`Engine`]. use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt}; use crate::dynamic::{map_std_type_name, Union, Variant}; @@ -25,7 +25,7 @@ use crate::syntax::CustomSyntax; use crate::utils::get_hasher; use crate::{ calc_native_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position, Scope, - Shared, StaticVec, NO_POS, + Shared, StaticVec, }; #[cfg(not(feature = "no_index"))] @@ -40,7 +40,7 @@ use crate::Map; #[cfg(not(feature = "no_object"))] pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical -/// _[INTERNALS]_ A stack of imported modules. +/// _(INTERNALS)_ A stack of imported modules. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -52,83 +52,97 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical // We cannot use &str or Cow here because `eval` may load a module and the module name will live beyond // the AST of the eval script text. The best we can do is a shared reference. #[derive(Debug, Clone, Default)] -pub struct Imports(StaticVec<(ImmutableString, Shared)>); +pub struct Imports(Option)>>); impl Imports { /// Get the length of this stack of imported modules. pub fn len(&self) -> usize { - self.0.len() + self.0.as_ref().map_or(0, StaticVec::len) } /// Is this stack of imported modules empty? pub fn is_empty(&self) -> bool { - self.0.is_empty() + self.0.as_ref().map_or(true, StaticVec::is_empty) } /// Get the imported module at a particular index. pub fn get(&self, index: usize) -> Option> { - self.0.get(index).map(|(_, m)| m).cloned() + self.0 + .as_ref() + .and_then(|x| x.get(index)) + .map(|(_, m)| m) + .cloned() } /// Get the index of an imported module by name. pub fn find(&self, name: &str) -> Option { - self.0 - .iter() - .enumerate() - .rev() - .find(|(_, (key, _))| key.as_str() == name) - .map(|(index, _)| index) + self.0.as_ref().and_then(|x| { + x.iter() + .enumerate() + .rev() + .find(|(_, (key, _))| key.as_str() == name) + .map(|(index, _)| index) + }) } /// Push an imported module onto the stack. pub fn push(&mut self, name: impl Into, module: impl Into>) { - self.0.push((name.into(), module.into())); + if self.0.is_none() { + self.0 = Some(Default::default()); + } + + self.0.as_mut().unwrap().push((name.into(), module.into())); } /// Truncate the stack of imported modules to a particular length. pub fn truncate(&mut self, size: usize) { - self.0.truncate(size); + if self.0.is_some() { + self.0.as_mut().unwrap().truncate(size); + } } /// Get an iterator to this stack of imported modules. #[allow(dead_code)] pub fn iter(&self) -> impl Iterator)> { - self.0 - .iter() - .map(|(name, module)| (name.as_str(), module.clone())) + self.0.iter().flat_map(|lib| { + lib.iter() + .map(|(name, module)| (name.as_str(), module.clone())) + }) } /// Get an iterator to this stack of imported modules. #[allow(dead_code)] pub(crate) fn iter_raw<'a>( &'a self, ) -> impl Iterator)> + 'a { - self.0.iter().cloned() + self.0.iter().flat_map(|lib| lib.iter().cloned()) } /// Get a consuming iterator to this stack of imported modules. pub fn into_iter(self) -> impl Iterator)> { - self.0.into_iter() + self.0.into_iter().flat_map(|lib| lib.into_iter()) } /// Add a stream of imported modules. pub fn extend(&mut self, stream: impl Iterator)>) { - self.0.extend(stream) + self.0.as_mut().unwrap().extend(stream) } /// Does the specified function hash key exist in this stack of imported modules? #[allow(dead_code)] pub fn contains_fn(&self, hash: u64) -> bool { - self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash)) + self.0.as_ref().map_or(false, |x| { + x.iter().any(|(_, m)| m.contains_qualified_fn(hash)) + }) } /// Get specified function via its hash key. pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { self.0 - .iter() - .rev() - .find_map(|(_, m)| m.get_qualified_fn(hash)) + .as_ref() + .and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_fn(hash))) } /// Does the specified TypeId iterator exist in this stack of imported modules? #[allow(dead_code)] pub fn contains_iter(&self, id: TypeId) -> bool { - self.0.iter().any(|(_, m)| m.contains_qualified_iter(id)) + self.0.as_ref().map_or(false, |x| { + x.iter().any(|(_, m)| m.contains_qualified_iter(id)) + }) } /// Get the specified TypeId iterator. pub fn get_iter(&self, id: TypeId) -> Option { self.0 - .iter() - .rev() - .find_map(|(_, m)| m.get_qualified_iter(id)) + .as_ref() + .and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_iter(id))) } } @@ -210,7 +224,7 @@ impl IndexChainValue { #[cfg(not(feature = "no_index"))] pub fn as_value(self) -> Dynamic { match self { - Self::None | Self::FnCallArgs(_) => panic!("expecting IndexChainValue::Value"), + Self::None | Self::FnCallArgs(_) => unreachable!("expecting IndexChainValue::Value"), Self::Value(value) => value, } } @@ -222,7 +236,7 @@ impl IndexChainValue { #[cfg(not(feature = "no_object"))] pub fn as_fn_call_args(self) -> StaticVec { match self { - Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"), + Self::None | Self::Value(_) => unreachable!("expecting IndexChainValue::FnCallArgs"), Self::FnCallArgs(value) => value, } } @@ -355,7 +369,7 @@ impl<'a> Target<'a> { #[cfg(not(feature = "no_index"))] Self::StringChar(_, _, ch) => { let char_value = ch.clone(); - self.set_value((char_value, NO_POS)).unwrap(); + self.set_value((char_value, Position::NONE)).unwrap(); } } } @@ -449,7 +463,7 @@ impl> From for Target<'_> { } } -/// _[INTERNALS]_ A type that holds all the current states of the Engine. +/// _(INTERNALS)_ A type that holds all the current states of the Engine. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -478,7 +492,7 @@ impl State { } } -/// _[INTERNALS]_ A type containing all the limits imposed by the `Engine`. +/// _(INTERNALS)_ A type containing all the limits imposed by the [`Engine`]. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -528,12 +542,12 @@ 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`. + /// The current [`Engine`]. #[inline(always)] pub fn engine(&self) -> &'e Engine { self.engine } - /// _[INTERNALS]_ The current set of modules imported via `import` statements. + /// _(INTERNALS)_ The current set of modules imported via `import` statements. /// Available under the `internals` feature only. #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] @@ -562,9 +576,10 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, /// /// # Thread Safety /// -/// `Engine` is re-entrant. +/// [`Engine`] is re-entrant. /// -/// Currently, `Engine` is neither `Send` nor `Sync`. Use the `sync` feature to make it `Send + Sync`. +/// Currently, [`Engine`] is neither [`Send`] nor [`Sync`]. +/// Use the [`Sync`] feature to make it [`Send`] `+` [`Sync`]. /// /// # Example /// @@ -581,11 +596,11 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, /// # } /// ``` pub struct Engine { - /// A unique ID identifying this scripting `Engine`. + /// A unique ID identifying this scripting [`Engine`]. pub id: String, /// A module containing all functions directly loaded into the Engine. - pub(crate) global_module: Module, + pub(crate) global_namespace: Module, /// A collection of all library packages loaded into the Engine. pub(crate) packages: PackagesCollection, /// A collection of all sub-modules directly loaded into the Engine. @@ -670,7 +685,7 @@ fn default_print(_s: &str) { } /// Search for a module within an imports stack. -/// Position in `EvalAltResult` is `None` and must be set afterwards. +/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. pub fn search_imports( mods: &Imports, state: &mut State, @@ -696,7 +711,7 @@ pub fn search_imports( } impl Engine { - /// Create a new `Engine` + /// Create a new [`Engine`] #[inline] pub fn new() -> Self { // Create the new scripting Engine @@ -704,7 +719,7 @@ impl Engine { id: Default::default(), packages: Default::default(), - global_module: Default::default(), + global_namespace: Default::default(), global_sub_modules: Default::default(), #[cfg(not(feature = "no_module"))] @@ -759,15 +774,15 @@ impl Engine { engine } - /// Create a new `Engine` with minimal built-in functions. - /// Use the `load_package` method to load additional packages of functions. + /// Create a new [`Engine`] with minimal built-in functions. + /// Use the [`load_package`][Engine::load_package] method to load additional packages of functions. #[inline] pub fn new_raw() -> Self { Self { id: Default::default(), packages: Default::default(), - global_module: Default::default(), + global_namespace: Default::default(), global_sub_modules: Default::default(), #[cfg(not(feature = "no_module"))] @@ -915,7 +930,7 @@ impl Engine { } /// Chain-evaluate a dot/index chain. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn eval_dot_index_chain_helper( &self, @@ -925,7 +940,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, rhs: &Expr, - mut idx_values: StaticVec, + idx_values: &mut StaticVec, chain_type: ChainType, level: usize, new_val: Option<(Dynamic, Position)>, @@ -1006,14 +1021,14 @@ impl Engine { { EvalAltResult::ErrorIndexingType( self.map_type_name(val_type_name).into(), - NO_POS, + Position::NONE, ) } err => err, })?; } - Ok(Default::default()) + Ok((Dynamic::UNIT, true)) } // xxx[rhs] _ => { @@ -1038,7 +1053,7 @@ impl Engine { def_value, .. } = x.as_ref(); - let def_value = def_value.map(Into::::into); + let def_value = def_value.as_ref(); let args = idx_val.as_fn_call_args(); self.make_method_call( mods, state, lib, name, *hash, target, args, def_value, *native, false, @@ -1112,7 +1127,7 @@ impl Engine { def_value, .. } = x.as_ref(); - let def_value = def_value.map(Into::::into); + let def_value = def_value.as_ref(); let args = idx_val.as_fn_call_args(); let (val, _) = self .make_method_call( @@ -1178,7 +1193,7 @@ impl Engine { |err| match *err { // If there is no setter, no need to feed it back because the property is read-only EvalAltResult::ErrorDotExpr(_, _) => { - Ok(Default::default()) + Ok((Dynamic::UNIT, false)) } _ => Err(err.fill_position(*x_pos)), }, @@ -1196,7 +1211,7 @@ impl Engine { def_value, .. } = f.as_ref(); - let def_value = def_value.map(Into::::into); + let def_value = def_value.as_ref(); let args = idx_val.as_fn_call_args(); let (mut val, _) = self .make_method_call( @@ -1247,19 +1262,10 @@ impl Engine { _ => unreachable!(), }; - let mut idx_values = StaticVec::new(); + let idx_values = &mut Default::default(); self.eval_indexed_chain( - scope, - mods, - state, - lib, - this_ptr, - rhs, - chain_type, - &mut idx_values, - 0, - level, + scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, 0, level, )?; match lhs { @@ -1309,8 +1315,8 @@ impl Engine { } } - /// Evaluate a chain of indexes and store the results in a StaticVec. - /// StaticVec is used to avoid an allocation in the overwhelming cases of just a few levels of indexing. + /// Evaluate a chain of indexes and store the results in a [`StaticVec`]. + /// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of just a few levels of indexing. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn eval_indexed_chain( &self, @@ -1384,8 +1390,8 @@ impl Engine { Ok(()) } - /// Get the value at the indexed position of a base type - /// Position in `EvalAltResult` may be None and should be set afterwards. + /// Get the value at the indexed position of a base type. + /// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn get_indexed_mut<'t>( &self, @@ -1474,7 +1480,10 @@ impl Engine { .map(|(v, _)| v.into()) .map_err(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => { - Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), NO_POS)) + Box::new(EvalAltResult::ErrorIndexingType( + type_name.into(), + Position::NONE, + )) } _ => err, }) @@ -1482,13 +1491,13 @@ impl Engine { _ => EvalAltResult::ErrorIndexingType( self.map_type_name(target.type_name()).into(), - NO_POS, + Position::NONE, ) .into(), } } - // Evaluate an 'in' expression + // Evaluate an 'in' expression. fn eval_in_expr( &self, scope: &mut Scope, @@ -1511,10 +1520,10 @@ impl Engine { Dynamic(Union::Array(mut rhs_value)) => { // Call the `==` operator to compare each value let def_value = Some(false.into()); + let def_value = def_value.as_ref(); for value in rhs_value.iter_mut() { let args = &mut [&mut lhs_value.clone(), value]; - let def_value = def_value.clone(); // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. let hash = @@ -1552,7 +1561,7 @@ impl Engine { } } - /// Get a `Target` from an expression. + /// Get a [`Target`] from an expression. pub(crate) fn eval_expr_as_target<'s>( &self, scope: &'s mut Scope, @@ -1665,7 +1674,7 @@ impl Engine { } } - /// Evaluate an expression + /// Evaluate an expression. pub(crate) fn eval_expr( &self, scope: &mut Scope, @@ -1705,7 +1714,7 @@ impl Engine { // Statement block Expr::Stmt(x, _) => { - self.eval_statements(scope, mods, state, lib, this_ptr, x.as_ref(), level) + self.eval_stmt_block(scope, mods, state, lib, this_ptr, x.as_ref(), level) } // lhs[idx_expr] @@ -1754,7 +1763,7 @@ impl Engine { def_value, .. } = x.as_ref(); - let def_value = def_value.map(Into::::into); + let def_value = def_value.as_ref(); self.make_function_call( scope, mods, state, lib, this_ptr, name, args, def_value, *hash, *native, false, *cap_scope, level, @@ -1773,8 +1782,9 @@ impl Engine { .. } = x.as_ref(); let namespace = namespace.as_ref().map(|v| v.as_ref()); + let def_value = def_value.as_ref(); self.make_qualified_function_call( - scope, mods, state, lib, this_ptr, namespace, name, args, *def_value, *hash, + scope, mods, state, lib, this_ptr, namespace, name, args, def_value, *hash, level, ) .map_err(|err| err.fill_position(*pos)) @@ -1810,8 +1820,7 @@ impl Engine { .into()) } - Expr::True(_) => Ok(true.into()), - Expr::False(_) => Ok(false.into()), + Expr::BoolConstant(x, _) => Ok((*x).into()), Expr::Unit(_) => Ok(Dynamic::UNIT), Expr::Custom(custom, _) => { @@ -1839,7 +1848,8 @@ impl Engine { .map_err(|err| err.fill_position(expr.position())) } - pub(crate) fn eval_statements<'a>( + /// Evaluate a statements block. + pub(crate) fn eval_stmt_block<'a>( &self, scope: &mut Scope, mods: &mut Imports, @@ -1849,6 +1859,7 @@ impl Engine { statements: impl IntoIterator, level: usize, ) -> Result> { + let prev_always_search = state.always_search; let prev_scope_len = scope.len(); let prev_mods_len = mods.len(); state.scope_level += 1; @@ -1863,15 +1874,14 @@ impl Engine { mods.truncate(prev_mods_len); state.scope_level -= 1; - // The impact of an eval statement goes away at the end of a block + // The impact of new local variables goes away at the end of a block // because any new variables introduced will go out of scope - state.always_search = false; + state.always_search = prev_always_search; result } - /// Evaluate a statement - /// + /// Evaluate a statement. /// /// # Safety /// @@ -1892,7 +1902,7 @@ impl Engine { let result = match stmt { // No-op - Stmt::Noop(_) => Ok(Default::default()), + Stmt::Noop(_) => Ok(Dynamic::UNIT), // Expression as statement Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level), @@ -1925,7 +1935,7 @@ impl Engine { } else { *lhs_ptr.as_mut() = rhs_val; } - Ok(Default::default()) + Ok(Dynamic::UNIT) } // Op-assignment - in order of precedence: ScopeEntryType::Normal => { @@ -1939,7 +1949,7 @@ impl Engine { let hash_fn = calc_native_fn_hash(empty(), op, arg_types); match self - .global_module + .global_namespace .get_fn(hash_fn, false) .or_else(|| self.packages.get_fn(hash_fn)) .or_else(|| mods.get_fn(hash_fn)) @@ -1993,7 +2003,7 @@ impl Engine { } } } - Ok(Default::default()) + Ok(Dynamic::UNIT) } } } @@ -2035,7 +2045,7 @@ impl Engine { self.eval_dot_index_chain( scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; - Ok(Default::default()) + Ok(Dynamic::UNIT) } // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] @@ -2043,7 +2053,7 @@ impl Engine { self.eval_dot_index_chain( scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; - Ok(Default::default()) + Ok(Dynamic::UNIT) } // Non-lvalue expression (should be caught during parsing) _ => unreachable!(), @@ -2052,7 +2062,7 @@ impl Engine { // Block scope Stmt::Block(statements, _) => { - self.eval_statements(scope, mods, state, lib, this_ptr, statements, level) + self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, level) } // If statement @@ -2067,7 +2077,7 @@ impl Engine { } else if let Some(stmt) = else_block { self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) } else { - Ok(Default::default()) + Ok(Dynamic::UNIT) } }) } @@ -2105,28 +2115,40 @@ impl Engine { Ok(_) => (), Err(err) => match *err { EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()), + EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), _ => return Err(err), }, } } - Ok(false) => return Ok(Default::default()), + Ok(false) => return Ok(Dynamic::UNIT), Err(err) => { return Err(self.make_type_mismatch_err::(err, expr.position())) } } }, - // Loop statement - Stmt::Loop(block, _) => loop { - match self.eval_stmt(scope, mods, state, lib, this_ptr, block, level) { + // Do loop + Stmt::Do(body, expr, is_while, _) => loop { + match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) { Ok(_) => (), Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()), + EvalAltResult::LoopBreak(false, _) => continue, + EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), _ => return Err(err), }, } + + match self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .as_bool() + { + Ok(true) if !*is_while => return Ok(Dynamic::UNIT), + Ok(false) if *is_while => return Ok(Dynamic::UNIT), + Ok(_) => (), + Err(err) => { + return Err(self.make_type_mismatch_err::(err, expr.position())) + } + } }, // For loop @@ -2136,7 +2158,7 @@ impl Engine { let iter_type = iter_obj.type_id(); let func = self - .global_module + .global_namespace .get_iter(iter_type) .or_else(|| self.packages.get_iter(iter_type)) .or_else(|| mods.get_iter(iter_type)); @@ -2177,7 +2199,7 @@ impl Engine { state.scope_level -= 1; scope.rewind(scope.len() - 1); - Ok(Default::default()) + Ok(Dynamic::UNIT) } else { EvalAltResult::ErrorFor(expr.position()).into() } @@ -2206,7 +2228,7 @@ impl Engine { let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err { x.clone() } else { - err.set_position(NO_POS); + err.set_position(Position::NONE); err.to_string().into() }; @@ -2302,7 +2324,7 @@ impl Engine { if let Some(alias) = _alias { scope.add_entry_alias(scope.len() - 1, alias); } - Ok(Default::default()) + Ok(Dynamic::UNIT) } // Import statement @@ -2334,7 +2356,7 @@ impl Engine { state.modules += 1; - Ok(Default::default()) + Ok(Dynamic::UNIT) } else { Err( EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position()) @@ -2359,7 +2381,7 @@ impl Engine { .into(); } } - Ok(Default::default()) + Ok(Dynamic::UNIT) } // Share statement @@ -2376,7 +2398,7 @@ impl Engine { } _ => (), } - Ok(Default::default()) + Ok(Dynamic::UNIT) } }; @@ -2385,7 +2407,7 @@ impl Engine { } /// Check a result to ensure that the data size is within allowable limit. - /// Position in `EvalAltResult` may be None and should be set afterwards. + /// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards. #[cfg(feature = "unchecked")] #[inline(always)] fn check_data_size( @@ -2396,7 +2418,7 @@ impl Engine { } /// Check a result to ensure that the data size is within allowable limit. - /// Position in `EvalAltResult` may be None and should be set afterwards. + /// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards. #[cfg(not(feature = "unchecked"))] fn check_data_size( &self, @@ -2489,39 +2511,47 @@ impl Engine { let (_arr, _map, s) = calc_size(result.as_ref().unwrap()); if s > self.max_string_size() { - return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into(); + return EvalAltResult::ErrorDataTooLarge( + "Length of string".to_string(), + Position::NONE, + ) + .into(); } #[cfg(not(feature = "no_index"))] if _arr > self.max_array_size() { - return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), NO_POS).into(); + return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::NONE) + .into(); } #[cfg(not(feature = "no_object"))] if _map > self.max_map_size() { - return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), NO_POS) - .into(); + return EvalAltResult::ErrorDataTooLarge( + "Size of object map".to_string(), + Position::NONE, + ) + .into(); } result } /// Check if the number of operations stay within limit. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. pub(crate) fn inc_operations(&self, state: &mut State) -> Result<(), Box> { state.operations += 1; #[cfg(not(feature = "unchecked"))] // Guard against too many operations if self.max_operations() > 0 && state.operations > self.max_operations() { - return EvalAltResult::ErrorTooManyOperations(NO_POS).into(); + return EvalAltResult::ErrorTooManyOperations(Position::NONE).into(); } // Report progress - only in steps if let Some(progress) = &self.progress { if let Some(token) = progress(&state.operations) { // Terminate script if progress returns a termination token - return EvalAltResult::ErrorTerminated(token, NO_POS).into(); + return EvalAltResult::ErrorTerminated(token, Position::NONE).into(); } } @@ -2537,7 +2567,7 @@ impl Engine { .unwrap_or_else(|| map_std_type_name(name)) } - /// Make a Box>. + /// Make a `Box<`[`EvalAltResult`][EvalAltResult::ErrorMismatchDataType]`>`. #[inline(always)] pub(crate) fn make_type_mismatch_err(&self, typ: &str, pos: Position) -> Box { EvalAltResult::ErrorMismatchDataType( diff --git a/src/engine_api.rs b/src/engine_api.rs index 20187b2e..f005071e 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1,4 +1,4 @@ -//! Module that defines the extern API of `Engine`. +//! Module that defines the extern API of [`Engine`]. use crate::dynamic::Variant; use crate::engine::{EvalContext, Imports}; @@ -13,7 +13,7 @@ use crate::stdlib::{ use crate::utils::get_hasher; use crate::{ scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, NativeCallContext, - ParseError, AST, NO_POS, + ParseError, Position, AST, }; #[cfg(not(feature = "no_index"))] @@ -31,14 +31,14 @@ fn calc_hash_for_scripts<'a>(scripts: impl IntoIterator) -> /// Engine public API impl Engine { - /// Register a function of the `Engine`. + /// Register a function of the [`Engine`]. /// /// ## WARNING - Low Level API /// - /// This function is very low level. It takes a list of `TypeId`'s indicating the actual types of the parameters. + /// This function is very low level. It takes a list of [`TypeId`]'s indicating the actual types of the parameters. /// - /// Arguments are simply passed in as a mutable array of `&mut Dynamic`, - /// The arguments are guaranteed to be of the correct types matching the `TypeId`'s. + /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic], + /// The arguments are guaranteed to be of the correct types matching the [`TypeId`]'s. /// /// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::()` /// @@ -56,12 +56,17 @@ impl Engine { + SendSync + 'static, ) -> &mut Self { - self.global_module - .set_raw_fn(name, FnNamespace::Global, FnAccess::Public, arg_types, func); + self.global_namespace.set_raw_fn( + name, + FnNamespace::Global, + FnAccess::Public, + arg_types, + func, + ); self } - /// Register a custom type for use with the `Engine`. - /// The type must implement `Clone`. + /// Register a custom type for use with the [`Engine`]. + /// The type must implement [`Clone`]. /// /// # Example /// @@ -101,8 +106,8 @@ impl Engine { pub fn register_type(&mut self) -> &mut Self { self.register_type_with_name::(type_name::()) } - /// Register a custom type for use with the `Engine`, with a pretty-print name - /// for the `type_of` function. The type must implement `Clone`. + /// Register a custom type for use with the [`Engine`], with a pretty-print name + /// for the `type_of` function. The type must implement [`Clone`]. /// /// # Example /// @@ -151,7 +156,7 @@ impl Engine { self.type_names.insert(type_name::().into(), name.into()); self } - /// Register an iterator adapter for an iterable type with the `Engine`. + /// Register an iterator adapter for an iterable type with the [`Engine`]. /// This is an advanced feature. #[inline(always)] pub fn register_iterator(&mut self) -> &mut Self @@ -159,10 +164,10 @@ impl Engine { T: Variant + Clone + Iterator, ::Item: Variant + Clone, { - self.global_module.set_iterable::(); + self.global_namespace.set_iterable::(); self } - /// Register a getter function for a member of a registered type with the `Engine`. + /// Register a getter function for a member of a registered type with the [`Engine`]. /// /// The function signature must start with `&mut self` and not `&self`. /// @@ -206,8 +211,7 @@ impl Engine { ) -> &mut Self { crate::RegisterFn::register_fn(self, &crate::engine::make_getter(name), callback) } - /// Register a getter function for a member of a registered type with the `Engine`. - /// Returns `Result>`. + /// Register a getter function for a member of a registered type with the [`Engine`]. /// /// The function signature must start with `&mut self` and not `&self`. /// @@ -257,7 +261,7 @@ impl Engine { callback, ) } - /// Register a setter function for a member of a registered type with the `Engine`. + /// Register a setter function for a member of a registered type with the [`Engine`]. /// /// # Example /// @@ -302,8 +306,7 @@ impl Engine { ) -> &mut Self { crate::RegisterFn::register_fn(self, &crate::engine::make_setter(name), callback) } - /// Register a setter function for a member of a registered type with the `Engine`. - /// Returns `Result<(), Box>`. + /// Register a setter function for a member of a registered type with the [`Engine`]. /// /// # Example /// @@ -356,7 +359,7 @@ impl Engine { ) } /// Short-hand for registering both getter and setter functions - /// of a registered type with the `Engine`. + /// of a registered type with the [`Engine`]. /// /// All function signatures must start with `&mut self` and not `&self`. /// @@ -403,13 +406,13 @@ impl Engine { ) -> &mut Self { self.register_get(name, get_fn).register_set(name, set_fn) } - /// Register an index getter for a custom type with the `Engine`. + /// Register an index getter for a custom type with the [`Engine`]. /// /// The function signature must start with `&mut self` and not `&self`. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -466,14 +469,13 @@ impl Engine { crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_GET, callback) } - /// Register an index getter for a custom type with the `Engine`. - /// Returns `Result>`. + /// Register an index getter for a custom type with the [`Engine`]. /// /// The function signature must start with `&mut self` and not `&self`. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -532,11 +534,11 @@ impl Engine { crate::RegisterResultFn::register_result_fn(self, crate::engine::FN_IDX_GET, callback) } - /// Register an index setter for a custom type with the `Engine`. + /// Register an index setter for a custom type with the [`Engine`]. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -595,12 +597,11 @@ impl Engine { crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_SET, callback) } - /// Register an index setter for a custom type with the `Engine`. - /// Returns `Result<(), Box>`. + /// Register an index setter for a custom type with the [`Engine`]. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -670,11 +671,11 @@ impl Engine { move |obj: &mut T, index: X, value: U| callback(obj, index, value).map(Into::into), ) } - /// Short-hand for register both index getter and setter functions for a custom type with the `Engine`. + /// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`]. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -720,7 +721,7 @@ impl Engine { self.register_indexer_get(getter) .register_indexer_set(setter) } - /// Register a `Module` as a fixed module namespace with the `Engine`. + /// Register a [`Module`][crate::Module] as a fixed module namespace with the [`Engine`]. /// /// # Example /// @@ -759,7 +760,7 @@ impl Engine { } self } - /// Compile a string into an `AST`, which can be used later for evaluation. + /// Compile a string into an [`AST`], which can be used later for evaluation. /// /// # Example /// @@ -782,10 +783,10 @@ impl Engine { pub fn compile(&self, script: &str) -> Result { self.compile_with_scope(&Default::default(), script) } - /// Compile a string into an `AST` using own scope, which can be used later for evaluation. + /// Compile a string into an [`AST`] using own scope, which can be used later for evaluation. /// /// The scope is useful for passing constants into the script for optimization - /// when using `OptimizationLevel::Full`. + /// when using [`OptimizationLevel::Full`]. /// /// # Example /// @@ -825,10 +826,10 @@ impl Engine { self.compile_scripts_with_scope(scope, &[script]) } /// When passed a list of strings, first join the strings into one large script, - /// and then compile them into an `AST` using own scope, which can be used later for evaluation. + /// and then compile them into an [`AST`] using own scope, which can be used later for evaluation. /// /// The scope is useful for passing constants into the script for optimization - /// when using `OptimizationLevel::Full`. + /// when using [`OptimizationLevel::Full`]. /// /// ## Note /// @@ -878,7 +879,7 @@ impl Engine { ) -> Result { self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level) } - /// Join a list of strings and compile into an `AST` using own scope at a specific optimization level. + /// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level. #[inline(always)] pub(crate) fn compile_with_scope_and_optimization_level( &self, @@ -915,7 +916,7 @@ impl Engine { Ok(contents) } - /// Compile a script file into an `AST`, which can be used later for evaluation. + /// Compile a script file into an [`AST`], which can be used later for evaluation. /// /// # Example /// @@ -944,10 +945,10 @@ impl Engine { ) -> Result> { self.compile_file_with_scope(&Default::default(), path) } - /// Compile a script file into an `AST` using own scope, which can be used later for evaluation. + /// Compile a script file into an [`AST`] using own scope, which can be used later for evaluation. /// /// The scope is useful for passing constants into the script for optimization - /// when using `OptimizationLevel::Full`. + /// when using [`OptimizationLevel::Full`]. /// /// # Example /// @@ -1025,7 +1026,7 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] pub fn parse_json(&self, json: &str, has_null: bool) -> Result> { - use crate::token::{Position, Token}; + use crate::token::Token; let mut scope = Default::default(); @@ -1071,7 +1072,7 @@ impl Engine { self.eval_ast_with_scope(&mut scope, &ast) } - /// Compile a string containing an expression into an `AST`, + /// Compile a string containing an expression into an [`AST`], /// which can be used later for evaluation. /// /// # Example @@ -1095,11 +1096,11 @@ impl Engine { pub fn compile_expression(&self, script: &str) -> Result { self.compile_expression_with_scope(&Default::default(), script) } - /// Compile a string containing an expression into an `AST` using own scope, + /// Compile a string containing an expression into an [`AST`] using own scope, /// which can be used later for evaluation. /// /// The scope is useful for passing constants into the script for optimization - /// when using `OptimizationLevel::Full`. + /// when using [`OptimizationLevel::Full`]. /// /// # Example /// @@ -1308,7 +1309,7 @@ impl Engine { self.eval_ast_with_scope(scope, &ast) } - /// Evaluate an `AST`. + /// Evaluate an [`AST`]. /// /// # Example /// @@ -1330,7 +1331,7 @@ impl Engine { pub fn eval_ast(&self, ast: &AST) -> Result> { self.eval_ast_with_scope(&mut Default::default(), ast) } - /// Evaluate an `AST` with own scope. + /// Evaluate an [`AST`] with own scope. /// /// # Example /// @@ -1375,12 +1376,12 @@ impl Engine { EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), - NO_POS, + Position::NONE, ) .into() }); } - /// Evaluate an `AST` with own scope. + /// Evaluate an [`AST`] with own scope. #[inline(always)] pub(crate) fn eval_ast_with_scope_raw<'a>( &self, @@ -1439,7 +1440,7 @@ impl Engine { pub fn consume_ast(&self, ast: &AST) -> Result<(), Box> { self.consume_ast_with_scope(&mut Default::default(), ast) } - /// Evaluate an `AST` with own scope, but throw away the result and only return error (if any). + /// Evaluate an [`AST`] 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. #[inline(always)] pub fn consume_ast_with_scope( @@ -1452,7 +1453,7 @@ impl Engine { self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()]) .map(|_| ()) } - /// Call a script function defined in an `AST` with multiple arguments. + /// Call a script function defined in an [`AST`] with multiple arguments. /// Arguments are passed as a tuple. /// /// # Example @@ -1509,13 +1510,13 @@ impl Engine { EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), - NO_POS, + Position::NONE, ) .into() }); } - /// Call a script function defined in an `AST` with multiple `Dynamic` arguments - /// and optionally a value for binding to the 'this' pointer. + /// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments + /// and optionally a value for binding to the `this` pointer. /// /// ## WARNING /// @@ -1577,7 +1578,7 @@ impl Engine { self.call_fn_dynamic_raw(scope, &[lib.as_ref()], name, &mut this_ptr, args.as_mut()) } - /// Call a script function defined in an `AST` with multiple `Dynamic` arguments. + /// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments. /// /// ## WARNING /// @@ -1598,7 +1599,7 @@ impl Engine { let fn_def = lib .iter() .find_map(|&m| m.get_script_fn(name, args.len(), true)) - .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?; + .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; let mut state = Default::default(); let mut mods = self.global_sub_modules.clone(); @@ -1610,17 +1611,19 @@ impl Engine { self.call_script_fn(scope, &mut mods, &mut state, lib, this_ptr, fn_def, args, 0) } - /// Optimize the `AST` with constants defined in an external Scope. - /// An optimized copy of the `AST` is returned while the original `AST` is consumed. + /// Optimize the [`AST`] with constants defined in an external Scope. + /// An optimized copy of the [`AST`] is returned while the original [`AST`] is consumed. /// /// Although optimization is performed by default during compilation, sometimes it is necessary to - /// _re_-optimize an AST. For example, when working with constants that are passed in via an - /// external scope, it will be more efficient to optimize the `AST` once again to take advantage + /// _re_-optimize an [`AST`]. For example, when working with constants that are passed in via an + /// external scope, it will be more efficient to optimize the [`AST`] once again to take advantage /// of the new constants. /// - /// With this method, it is no longer necessary to recompile a large script. The script `AST` can be - /// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope - /// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running. + /// With this method, it is no longer necessary to recompile a large script. + /// The script [`AST`] can be compiled just once. Before evaluation, + /// constants are passed into the [`Engine`] via an external scope + /// (i.e. with [`scope.push_constant(...)`][crate::Scope::push_constant]). + /// Then, the [`AST`] is cloned and the copy re-optimized before running. #[cfg(not(feature = "no_optimize"))] #[inline] pub fn optimize_ast( @@ -1727,7 +1730,7 @@ impl Engine { self.progress = Some(Box::new(callback)); self } - /// Override default action of `print` (print to stdout using `println!`) + /// Override default action of `print` (print to stdout using [`println!`]) /// /// # Example /// @@ -1756,7 +1759,7 @@ impl Engine { self.print = Box::new(callback); self } - /// Override default action of `debug` (print to stdout using `println!`) + /// Override default action of `debug` (print to stdout using [`println!`]) /// /// # Example /// diff --git a/src/engine_settings.rs b/src/engine_settings.rs index 09484087..319dbfb9 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -1,4 +1,4 @@ -//! Configuration settings for `Engine`. +//! Configuration settings for [`Engine`]. use crate::packages::PackageLibrary; use crate::stdlib::{format, string::String}; @@ -9,8 +9,8 @@ use crate::Engine; use crate::stdlib::boxed::Box; impl Engine { - /// Load a new package into the `Engine`. - /// Anything that can be converted into a `PackageLibrary` is accepted, including a simple `Module`. + /// Load a new package into the [`Engine`]. + /// A simple [`Module`][crate::Module] is automatically converted into a package. /// /// When searching for functions, packages loaded later are preferred. /// In other words, loaded packages are searched in reverse order. @@ -19,7 +19,7 @@ impl Engine { self.packages.add(package.into()); self } - /// Control whether and how the `Engine` will optimize an AST after compilation. + /// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation. /// /// Not available under the `no_optimize` feature. #[cfg(not(feature = "no_optimize"))] @@ -32,7 +32,7 @@ impl Engine { self } /// The current optimization level. - /// It controls whether and how the `Engine` will optimize an AST after compilation. + /// It controls whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation. /// /// Not available under the `no_optimize` feature. #[cfg(not(feature = "no_optimize"))] @@ -166,7 +166,7 @@ impl Engine { pub fn max_map_size(&self) -> usize { self.limits.max_map_size } - /// Set the module resolution service used by the `Engine`. + /// Set the module resolution service used by the [`Engine`]. /// /// Not available under the `no_module` feature. #[cfg(not(feature = "no_module"))] diff --git a/src/fn_args.rs b/src/fn_args.rs index d0c9de63..8c45a98a 100644 --- a/src/fn_args.rs +++ b/src/fn_args.rs @@ -1,4 +1,4 @@ -//! Helper module which defines `FuncArgs` to make function calling easier. +//! Helper module which defines [`FuncArgs`] to make function calling easier. #![allow(non_snake_case)] @@ -9,12 +9,12 @@ use crate::{Dynamic, StaticVec}; /// Any data type that can be converted into a `Vec` can be used /// as arguments to a function call. pub trait FuncArgs { - /// Convert to a `StaticVec` of the function call arguments. + /// Convert to a [`StaticVec`]`<`[`Dynamic`]`>` of the function call arguments. fn into_vec(self) -> StaticVec; } -/// Macro to implement `FuncArgs` for tuples of standard types (each can be -/// converted into `Dynamic`). +/// Macro to implement [`FuncArgs`] for tuples of standard types (each can be +/// converted into a [`Dynamic`]). macro_rules! impl_args { ($($p:ident),*) => { impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*) diff --git a/src/fn_call.rs b/src/fn_call.rs index 33bce76e..bbf40192 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1,4 +1,4 @@ -//! Implement function-calling mechanism for `Engine`. +//! Implement function-calling mechanism for [`Engine`]. use crate::ast::{Expr, Stmt}; use crate::engine::{ @@ -23,7 +23,7 @@ use crate::stdlib::{ }; use crate::{ calc_native_fn_hash, calc_script_fn_hash, Dynamic, Engine, EvalAltResult, FnPtr, - ImmutableString, Module, ParseErrorType, Scope, StaticVec, INT, NO_POS, + ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec, INT, }; #[cfg(not(feature = "no_float"))] @@ -140,7 +140,7 @@ pub fn ensure_no_data_race( { return EvalAltResult::ErrorDataRace( format!("argument #{} of function '{}'", n + 1 + skip, fn_name), - NO_POS, + Position::NONE, ) .into(); } @@ -150,8 +150,8 @@ pub fn ensure_no_data_race( } impl Engine { - /// Call a native Rust function registered with the `Engine`. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// Call a native Rust function registered with the [`Engine`]. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. /// /// ## WARNING /// @@ -168,7 +168,7 @@ impl Engine { args: &mut FnCallArgs, is_ref: bool, pub_only: bool, - def_val: Option, + def_val: Option<&Dynamic>, ) -> Result<(Dynamic, bool), Box> { self.inc_operations(state)?; @@ -176,7 +176,7 @@ impl Engine { // First search registered functions (can override packages) // Then search packages let func = //lib.get_fn(hash_fn, pub_only) - self.global_module.get_fn(hash_fn, pub_only) + self.global_namespace.get_fn(hash_fn, pub_only) .or_else(|| self.packages.get_fn(hash_fn)) .or_else(|| mods.get_fn(hash_fn)); @@ -206,7 +206,7 @@ impl Engine { EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), - NO_POS, + Position::NONE, ) })?) .into(), @@ -217,7 +217,7 @@ impl Engine { EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), - NO_POS, + Position::NONE, ) })?) .into(), @@ -237,7 +237,7 @@ impl Engine { // Return default value (if any) if let Some(val) = def_val { - return Ok((val, false)); + return Ok((val.clone(), false)); } // Getter function not found? @@ -249,7 +249,7 @@ impl Engine { prop, self.map_type_name(args[0].type_name()) ), - NO_POS, + Position::NONE, ) .into(); } @@ -264,7 +264,7 @@ impl Engine { self.map_type_name(args[0].type_name()), self.map_type_name(args[1].type_name()), ), - NO_POS, + Position::NONE, ) .into(); } @@ -278,7 +278,7 @@ impl Engine { self.map_type_name(args[0].type_name()), self.map_type_name(args[1].type_name()), ), - NO_POS, + Position::NONE, ) .into(); } @@ -292,7 +292,7 @@ impl Engine { self.map_type_name(args[0].type_name()), self.map_type_name(args[1].type_name()), ), - NO_POS, + Position::NONE, ) .into(); } @@ -311,13 +311,13 @@ impl Engine { .collect::>() .join(", ") ), - NO_POS, + Position::NONE, ) .into() } /// Call a script-defined function. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. /// /// ## WARNING /// @@ -342,7 +342,7 @@ impl Engine { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "unchecked"))] if level > self.max_call_levels() { - return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS))); + return Err(Box::new(EvalAltResult::ErrorStackOverflow(Position::NONE))); } let orig_scope_level = state.scope_level; @@ -385,25 +385,27 @@ impl Engine { // Evaluate the function at one higher level of call depth let stmt = &fn_def.body; - let result = - self.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1) - .or_else(|err| match *err { - // Convert return statement to return value - EvalAltResult::Return(x, _) => Ok(x), - EvalAltResult::ErrorInFunctionCall(name, err, _) => { - EvalAltResult::ErrorInFunctionCall( - format!("{} > {}", fn_def.name, name), - err, - NO_POS, - ) + let result = self + .eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1) + .or_else(|err| match *err { + // Convert return statement to return value + EvalAltResult::Return(x, _) => Ok(x), + EvalAltResult::ErrorInFunctionCall(name, err, _) => { + EvalAltResult::ErrorInFunctionCall( + format!("{} > {}", fn_def.name, name), + err, + Position::NONE, + ) + .into() + } + // 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, Position::NONE) .into() - } - // 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, NO_POS) - .into(), - }); + } + }); // Remove all local variables scope.rewind(prev_scope_len); @@ -440,14 +442,14 @@ impl Engine { hash_script: u64, pub_only: bool, ) -> bool { - // NOTE: We skip script functions for global_module and packages, and native functions for lib + // NOTE: We skip script functions for global_namespace and packages, and native functions for lib // First check script-defined functions lib.iter().any(|&m| m.contains_fn(hash_script, pub_only)) //|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only)) // Then check registered functions - //|| self.global_module.contains_fn(hash_script, pub_only) - || self.global_module.contains_fn(hash_fn, false) + //|| self.global_namespace.contains_fn(hash_script, pub_only) + || self.global_namespace.contains_fn(hash_fn, false) // Then check packages || self.packages.contains_fn(hash_script) || self.packages.contains_fn(hash_fn) @@ -457,7 +459,7 @@ impl Engine { } /// Perform an actual function call, native Rust or scripted, taking care of special functions. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. /// /// ## WARNING /// @@ -476,7 +478,7 @@ impl Engine { _is_method: bool, pub_only: bool, _capture_scope: Option, - def_val: Option, + def_val: Option<&Dynamic>, _level: usize, ) -> Result<(Dynamic, bool), Box> { // Check for data race. @@ -512,7 +514,7 @@ impl Engine { fn_name, fn_name ) .into(), - NO_POS, + Position::NONE, ) .into() } @@ -524,7 +526,7 @@ impl Engine { let func = lib .iter() .find_map(|&m| m.get_fn(hash_script, pub_only)) - //.or_else(|| self.global_module.get_fn(hash_script, pub_only)) + //.or_else(|| self.global_namespace.get_fn(hash_script, pub_only)) .or_else(|| self.packages.get_fn(hash_script)) //.or_else(|| mods.get_fn(hash_script)) .unwrap(); @@ -606,7 +608,7 @@ impl Engine { } /// Evaluate a list of statements with an empty state and no `this` pointer. - /// This is commonly used to evaluate a list of statements in an `AST` or a script function body. + /// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body. #[inline] pub(crate) fn eval_statements_raw<'a>( &self, @@ -631,7 +633,7 @@ impl Engine { } /// Evaluate a text string as a script - used primarily for 'eval'. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. fn eval_script_expr( &self, scope: &mut Scope, @@ -652,7 +654,7 @@ impl Engine { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "unchecked"))] if _level > self.max_call_levels() { - return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS))); + return Err(Box::new(EvalAltResult::ErrorStackOverflow(Position::NONE))); } // Compile the script text @@ -678,7 +680,7 @@ impl Engine { } /// Call a dot method. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. #[cfg(not(feature = "no_object"))] pub(crate) fn make_method_call( &self, @@ -689,7 +691,7 @@ impl Engine { hash_script: u64, target: &mut crate::engine::Target, mut call_args: StaticVec, - def_val: Option, + def_val: Option<&Dynamic>, native: bool, pub_only: bool, level: usize, @@ -830,7 +832,7 @@ impl Engine { } /// Call a function in normal function-call style. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. pub(crate) fn make_function_call( &self, scope: &mut Scope, @@ -840,7 +842,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, args_expr: impl AsRef<[Expr]>, - def_val: Option, + def_val: Option<&Dynamic>, mut hash_script: u64, native: bool, pub_only: bool, @@ -1075,7 +1077,7 @@ impl Engine { } /// Call a namespace-qualified function in normal function-call style. - /// Position in `EvalAltResult` is `None` and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. pub(crate) fn make_qualified_function_call( &self, scope: &mut Scope, @@ -1086,7 +1088,7 @@ impl Engine { namespace: Option<&NamespaceRef>, fn_name: &str, args_expr: impl AsRef<[Expr]>, - def_val: Option, + def_val: Option<&Dynamic>, hash_script: u64, level: usize, ) -> Result> { @@ -1202,7 +1204,7 @@ impl Engine { f.get_native_fn().clone()((self, &*mods, lib).into(), args.as_mut()) } Some(_) => unreachable!(), - None if def_val.is_some() => Ok(def_val.unwrap().into()), + None if def_val.is_some() => Ok(def_val.unwrap().clone()), None => EvalAltResult::ErrorFunctionNotFound( format!( "{}{} ({})", @@ -1217,7 +1219,7 @@ impl Engine { .collect::>() .join(", ") ), - NO_POS, + Position::NONE, ) .into(), } diff --git a/src/fn_func.rs b/src/fn_func.rs index 0a52d478..94b3cba5 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -11,8 +11,8 @@ use crate::{Engine, EvalAltResult, ParseError, Scope, AST}; pub trait Func { type Output; - /// Create a Rust closure from an `AST`. - /// The `Engine` and `AST` are consumed and basically embedded into the closure. + /// Create a Rust closure from an [`AST`]. + /// The [`Engine`] and [`AST`] are consumed and basically embedded into the closure. /// /// # Example /// @@ -43,7 +43,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. + /// The [`Engine`] is consumed and basically embedded into the closure. /// /// # Example /// diff --git a/src/fn_native.rs b/src/fn_native.rs index 8b6734c7..c29464f9 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -7,7 +7,7 @@ use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string: use crate::token::is_valid_identifier; use crate::{ calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, - StaticVec, NO_POS, + Position, StaticVec, }; #[cfg(not(feature = "sync"))] @@ -78,7 +78,7 @@ impl<'e, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'m M) } impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> { - /// Create a new `NativeCallContext`. + /// Create a new [`NativeCallContext`]. #[inline(always)] pub fn new(engine: &'e Engine, lib: &'m impl AsRef<[&'pm Module]>) -> Self { Self { @@ -87,7 +87,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> { lib: lib.as_ref(), } } - /// _[INTERNALS]_ Create a new `NativeCallContext`. + /// _(INTERNALS)_ Create a new [`NativeCallContext`]. /// Available under the `internals` feature only. #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] @@ -103,12 +103,12 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> { lib: lib.as_ref(), } } - /// The current `Engine`. + /// The current [`Engine`]. #[inline(always)] pub fn engine(&self) -> &'e Engine { self.engine } - /// _[INTERNALS]_ The current set of modules imported via `import` statements. + /// _(INTERNALS)_ The current set of modules imported via `import` statements. /// Available under the `internals` feature only. #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] @@ -127,10 +127,11 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> { /// /// All arguments may be _consumed_, meaning that they may be replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. + /// /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. /// - /// If `is_method` is `true`, the first argument is assumed to be passed + /// If `is_method` is [`true`], the first argument is assumed to be passed /// by reference and is not consumed. pub fn call_fn_dynamic_raw( &mut self, @@ -138,7 +139,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> { is_method: bool, public_only: bool, args: &mut [&mut Dynamic], - def_value: Option, + def_value: Option<&Dynamic>, ) -> Result> { let mut mods = self.mods.cloned().unwrap_or_default(); @@ -171,7 +172,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> { } } -/// Consume a `Shared` resource and return a mutable reference to the wrapped value. +/// Consume a [`Shared`] resource and return a mutable reference to the wrapped value. /// If the resource is shared (i.e. has other outstanding references), a cloned copy is used. #[inline(always)] pub fn shared_make_mut(value: &mut Shared) -> &mut T { @@ -181,13 +182,13 @@ pub fn shared_make_mut(value: &mut Shared) -> &mut T { return Arc::make_mut(value); } -/// Consume a `Shared` resource if is unique (i.e. not shared), or clone it otherwise. +/// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise. #[inline(always)] pub fn shared_take_or_clone(value: Shared) -> T { shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone()) } -/// Consume a `Shared` resource if is unique (i.e. not shared). +/// Consume a [`Shared`] resource if is unique (i.e. not shared). #[inline(always)] pub fn shared_try_take(value: Shared) -> Result> { #[cfg(not(feature = "sync"))] @@ -196,7 +197,7 @@ pub fn shared_try_take(value: Shared) -> Result> { return Arc::try_unwrap(value); } -/// Consume a `Shared` resource, assuming that it is unique (i.e. not shared). +/// Consume a [`Shared`] resource, assuming that it is unique (i.e. not shared). /// /// # Panics /// @@ -300,7 +301,7 @@ impl TryFrom for FnPtr { if is_valid_identifier(value.chars()) { Ok(Self(value, Default::default())) } else { - EvalAltResult::ErrorFunctionNotFound(value.into(), NO_POS).into() + EvalAltResult::ErrorFunctionNotFound(value.into(), Position::NONE).into() } } } @@ -485,7 +486,8 @@ impl CallableFunction { /// /// # Panics /// - /// Panics if the `CallableFunction` is not `Pure` or `Method`. + /// Panics if the [`CallableFunction`] is not [`Pure`][CallableFunction::Pure] or + /// [`Method`][CallableFunction::Method]. pub fn get_native_fn(&self) -> &Shared { match self { Self::Pure(f) | Self::Method(f) => f, @@ -499,7 +501,7 @@ impl CallableFunction { /// /// # Panics /// - /// Panics if the `CallableFunction` is not `Script`. + /// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script]. #[cfg(not(feature = "no_function"))] pub fn get_fn_def(&self) -> &Shared { match self { @@ -511,7 +513,7 @@ impl CallableFunction { /// /// # Panics /// - /// Panics if the `CallableFunction` is not `Iterator`. + /// Panics if the [`CallableFunction`] is not [`Iterator`][CallableFunction::Iterator]. pub fn get_iter_fn(&self) -> IteratorFn { match self { Self::Iterator(f) => *f, @@ -525,7 +527,7 @@ impl CallableFunction { /// /// # Panics /// - /// Panics if the `CallableFunction` is not `Plugin`. + /// Panics if the [`CallableFunction`] is not [`Plugin`][CallableFunction::Plugin]. pub fn get_plugin_fn<'s>(&'s self) -> &Shared { match self { Self::Plugin(f) => f, @@ -535,17 +537,17 @@ impl CallableFunction { Self::Script(_) => unreachable!(), } } - /// Create a new `CallableFunction::Pure`. + /// Create a new [`CallableFunction::Pure`]. #[inline(always)] pub fn from_pure(func: Box) -> Self { Self::Pure(func.into()) } - /// Create a new `CallableFunction::Method`. + /// Create a new [`CallableFunction::Method`]. #[inline(always)] pub fn from_method(func: Box) -> Self { Self::Method(func.into()) } - /// Create a new `CallableFunction::Plugin`. + /// Create a new [`CallableFunction::Plugin`]. #[inline(always)] pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self { Self::Plugin((Box::new(func) as Box).into()) diff --git a/src/fn_register.rs b/src/fn_register.rs index 79e68958..afc87194 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -187,7 +187,7 @@ macro_rules! def_register { { #[inline] fn register_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public, + self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, &[$(map_type_id::<$par>()),*], CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*)) ); @@ -202,7 +202,7 @@ macro_rules! def_register { { #[inline] fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public, + self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, &[$(map_type_id::<$par>()),*], CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*)) ); diff --git a/src/lib.rs b/src/lib.rs index 292521f7..f4aa7721 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ //! //! # Documentation //! -//! See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai script engine and language. +//! See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai scripting engine and language. #![cfg_attr(feature = "no_std", no_std)] @@ -121,7 +121,7 @@ pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use result::EvalAltResult; pub use scope::Scope; pub use syntax::Expression; -pub use token::{Position, NO_POS}; +pub use token::Position; pub use utils::ImmutableString; #[allow(dead_code)] @@ -157,7 +157,7 @@ pub use module::ModuleResolver; #[cfg(not(feature = "no_module"))] pub use crate::module::resolvers as module_resolvers; -/// _[SERDE]_ Serialization and deserialization support for [`serde`](https://crates.io/crates/serde). +/// _(SERDE)_ Serialization and deserialization support for [`serde`](https://crates.io/crates/serde). /// Exported under the `serde` feature. #[cfg(feature = "serde")] pub mod serde { @@ -187,13 +187,13 @@ pub use engine::{Imports, Limits, State as EvalState}; #[deprecated(note = "this type is volatile and may change")] pub use module::NamespaceRef; -/// _[INTERNALS]_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), +/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), /// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored. /// Exported under the `internals` feature only. #[cfg(not(feature = "internals"))] type StaticVec = smallvec::SmallVec<[T; 4]>; -/// _[INTERNALS]_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), +/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), /// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] diff --git a/src/module/mod.rs b/src/module/mod.rs index f14f191d..4e548e14 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -20,7 +20,7 @@ use crate::stdlib::{ use crate::token::Token; use crate::utils::StraightHasherBuilder; use crate::{ - Dynamic, EvalAltResult, ImmutableString, NativeCallContext, Shared, StaticVec, NO_POS, + Dynamic, EvalAltResult, ImmutableString, NativeCallContext, Position, Shared, StaticVec, }; #[cfg(not(feature = "no_index"))] @@ -42,6 +42,12 @@ pub enum FnNamespace { Internal, } +impl Default for FnNamespace { + fn default() -> Self { + Self::Internal + } +} + impl FnNamespace { /// Is this namespace global? #[inline(always)] @@ -258,7 +264,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`][crate::Dynamic]. /// /// # Example /// @@ -297,15 +303,15 @@ impl Module { /// Get a reference to a namespace-qualified variable. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. + /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. #[inline(always)] pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box> { if hash_var == 0 { - Err(EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into()) + Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()) } else { - self.all_variables - .get(&hash_var) - .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into()) + self.all_variables.get(&hash_var).ok_or_else(|| { + EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into() + }) } } @@ -437,7 +443,7 @@ impl Module { /// Does the particular Rust function exist in the module? /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. + /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. /// It is also returned by the `set_fn_XXX` calls. /// /// # Example @@ -511,12 +517,13 @@ impl Module { hash_fn } - /// Set a Rust function taking a reference to the scripting `Engine`, the current set of functions, - /// plus a list of mutable `Dynamic` references into the module, returning a hash key. + /// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine], + /// the current set of functions, plus a list of mutable [`Dynamic`][crate::Dynamic] references + /// into the module, returning a hash key. /// /// Use this to register a built-in function which must reference settings on the scripting - /// `Engine` (e.g. to prevent growing an array beyond the allowed maximum size), or to call a - /// script-defined function in the current evaluation context. + /// [`Engine`][crate::Engine] (e.g. to prevent growing an array beyond the allowed maximum size), + /// or to call a script-defined function in the current evaluation context. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -524,9 +531,9 @@ impl Module { /// /// This function is very low level. /// - /// A list of `TypeId`'s is taken as the argument types. + /// 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`][crate::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. @@ -545,8 +552,7 @@ impl Module { /// use rhai::{Module, FnNamespace, FnAccess}; /// /// let mut module = Module::new(); - /// let hash = module.set_raw_fn("double_or_not", - /// FnNamespace::Internal, FnAccess::Public, + /// let hash = module.set_raw_fn("double_or_not", FnNamespace::Internal, FnAccess::Public, /// // Pass parameter types via a slice with TypeId's /// &[std::any::TypeId::of::(), std::any::TypeId::of::()], /// // Fixed closure signature @@ -594,30 +600,6 @@ impl Module { ) } - /// Get the namespace of a registered function. - /// Returns `None` if a function with the hash does not exist. - /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. - #[inline(always)] - pub fn get_fn_namespace(&self, hash: u64) -> Option { - self.functions.get(&hash).map(|f| f.namespace) - } - - /// Set the namespace of a registered function. - /// Returns the original namespace or `None` if a function with the hash does not exist. - /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. - #[inline] - pub fn set_fn_namespace(&mut self, hash: u64, namespace: FnNamespace) -> Option { - if let Some(f) = self.functions.get_mut(&hash) { - let old_ns = f.namespace; - f.namespace = namespace; - Some(old_ns) - } else { - None - } - } - /// Set a Rust function taking no parameters into the module, returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. @@ -687,16 +669,19 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; + /// use rhai::{Module, FnNamespace}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) }); + /// let hash = module.set_fn_1_mut("calc", FnNamespace::Internal, + /// |x: &mut i64| { *x += 1; Ok(*x) } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_1_mut( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -705,7 +690,7 @@ impl Module { let arg_types = [TypeId::of::()]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -713,6 +698,7 @@ impl Module { } /// Set a Rust getter function taking one mutable parameter, returning a hash key. + /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing Rust getter function, it is replaced. /// @@ -732,7 +718,11 @@ impl Module { name: impl Into, func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> u64 { - self.set_fn_1_mut(crate::engine::make_getter(&name.into()), func) + self.set_fn_1_mut( + crate::engine::make_getter(&name.into()), + FnNamespace::Global, + func, + ) } /// Set a Rust function taking two parameters into the module, returning a hash key. @@ -780,18 +770,22 @@ impl Module { /// # Example /// /// ``` - /// use rhai::{Module, ImmutableString}; + /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: ImmutableString| { - /// *x += y.len() as i64; Ok(*x) - /// }); + /// let hash = module.set_fn_2_mut("calc", FnNamespace::Internal, + /// |x: &mut i64, y: ImmutableString| { + /// *x += y.len() as i64; + /// Ok(*x) + /// } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_2_mut( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -803,7 +797,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::()]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -812,6 +806,7 @@ impl 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. /// /// If there is a similar existing setter Rust function, it is replaced. /// @@ -834,17 +829,22 @@ impl Module { name: impl Into, func: impl Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, ) -> u64 { - self.set_fn_2_mut(crate::engine::make_setter(&name.into()), func) + self.set_fn_2_mut( + crate::engine::make_setter(&name.into()), + FnNamespace::Global, + func, + ) } /// 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. /// /// If there is a similar existing setter Rust function, it is replaced. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`] or [`Map`]. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -878,7 +878,7 @@ impl Module { panic!("Cannot register indexer for strings."); } - self.set_fn_2_mut(crate::engine::FN_IDX_GET, func) + 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. @@ -932,12 +932,15 @@ impl Module { /// # Example /// /// ``` - /// use rhai::{Module, ImmutableString}; + /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: ImmutableString, z: i64| { - /// *x += y.len() as i64 + z; Ok(*x) - /// }); + /// let hash = module.set_fn_3_mut("calc", FnNamespace::Internal, + /// |x: &mut i64, y: ImmutableString, z: i64| { + /// *x += y.len() as i64 + z; + /// Ok(*x) + /// } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] @@ -949,6 +952,7 @@ impl Module { >( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A, B, C) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -961,7 +965,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -970,12 +974,13 @@ impl 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. /// /// If there is a similar existing Rust function, it is replaced. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`] or [`Map`]. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -1028,13 +1033,14 @@ impl Module { } /// Set a pair of Rust index getter and setter functions, returning both hash keys. - /// This is a short-hand for `set_indexer_get_fn` and `set_indexer_set_fn`. + /// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and + /// [`set_indexer_set_fn`][Module::set_indexer_set_fn]. /// /// If there are similar existing Rust functions, they are replaced. /// /// # Panics /// - /// Panics if the type is `Array` or `Map`. + /// Panics if the type is [`Array`] or [`Map`]. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Example @@ -1126,12 +1132,15 @@ impl Module { /// # Example /// /// ``` - /// use rhai::{Module, ImmutableString}; + /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_4_mut("calc", |x: &mut i64, y: ImmutableString, z: i64, _w: ()| { - /// *x += y.len() as i64 + z; Ok(*x) - /// }); + /// let hash = module.set_fn_4_mut("calc", FnNamespace::Internal, + /// |x: &mut i64, y: ImmutableString, z: i64, _w: ()| { + /// *x += y.len() as i64 + z; + /// Ok(*x) + /// } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] @@ -1144,6 +1153,7 @@ impl Module { >( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A, B, C, D) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -1162,7 +1172,7 @@ impl Module { ]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -1171,7 +1181,7 @@ impl Module { /// Get a Rust function. /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. + /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. /// It is also returned by the `set_fn_XXX` calls. #[inline(always)] pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> { @@ -1190,8 +1200,8 @@ impl Module { /// Does the particular namespace-qualified function exist in the module? /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match - /// the hash calculated by `build_index`. + /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match + /// the hash calculated by [`build_index`][Module::build_index]. #[inline] pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool { self.all_functions.contains_key(&hash_fn) @@ -1200,8 +1210,8 @@ impl Module { /// Get a namespace-qualified function. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match - /// the hash calculated by `build_index`. + /// The [`u64`] hash is calculated by the function [`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(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { self.all_functions.get(&hash_qualified_fn) @@ -1386,10 +1396,11 @@ impl Module { /// Get an iterator over all script-defined functions in the module. /// /// Function metadata includes: - /// 1) Access mode (`FnAccess::Public` or `FnAccess::Private`). - /// 2) Function name (as string slice). - /// 3) Number of parameters. - /// 4) Shared reference to function definition `ScriptFnDef`. + /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). + /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]). + /// 3) Function name (as string slice). + /// 4) Number of parameters. + /// 5) Shared reference to function definition [`ScriptFnDef`][crate::ScriptFnDef]. #[cfg(not(feature = "no_function"))] #[inline(always)] pub(crate) fn iter_script_fn<'a>( @@ -1418,9 +1429,10 @@ impl Module { /// Get an iterator over all script-defined functions in the module. /// /// Function metadata includes: - /// 1) Access mode (`FnAccess::Public` or `FnAccess::Private`). - /// 2) Function name (as string slice). - /// 3) Number of parameters. + /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). + /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]). + /// 3) Function name (as string slice). + /// 4) Number of parameters. #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "internals"))] #[inline(always)] @@ -1441,11 +1453,12 @@ impl Module { /// Get an iterator over all script-defined functions in the module. /// /// Function metadata includes: - /// 1) Access mode (`FnAccess::Public` or `FnAccess::Private`). - /// 2) Function name (as string slice). - /// 3) Number of parameters. - /// 4) _[INTERNALS]_ Shared reference to function definition `ScriptFnDef`. - /// Exported under the internals feature only. + /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). + /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]). + /// 3) Function name (as string slice). + /// 4) Number of parameters. + /// 5) _(INTERNALS)_ Shared reference to function definition [`ScriptFnDef`][crate::ScriptFnDef]. + /// Exported under the `internals` feature only. #[cfg(not(feature = "no_function"))] #[cfg(feature = "internals")] #[inline(always)] @@ -1455,9 +1468,9 @@ impl Module { self.iter_script_fn() } - /// Create a new `Module` by evaluating an `AST`. + /// Create a new module by evaluating an [`AST`][crate::AST]. /// - /// The entire `AST` is encapsulated into each function, allowing functions + /// 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. /// Therefore, all functions will be found. @@ -1513,14 +1526,12 @@ impl Module { // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] { - let ast_lib: Shared = ast.lib().clone().into(); - ast.iter_functions() .filter(|(_, access, _, _, _)| !access.is_private()) .for_each(|(_, _, _, _, func)| { // Encapsulate AST environment let mut func = func.as_ref().clone(); - func.lib = Some(ast_lib.clone()); + func.lib = Some(ast.shared_lib()); func.mods = func_mods.clone(); module.set_script_fn(func.into()); }); @@ -1580,7 +1591,7 @@ impl Module { .. }, )| { - // Flatten all methods so they can be available without namespace qualifiers + // Flatten all functions with global namespace if namespace.is_global() { functions.insert(hash, func.clone()); } @@ -1596,7 +1607,7 @@ impl Module { // 1) Calculate a hash in a similar manner to script-defined functions, // i.e. qualifiers + function name + number of arguments. // 2) Calculate a second hash with no qualifiers, empty function name, - // and the actual list of argument `TypeId`'.s + // and the actual list of argument [`TypeId`]'.s let hash_fn_args = crate::calc_native_fn_hash( empty(), "", @@ -1686,13 +1697,13 @@ impl Module { } } -/// _[INTERNALS]_ A chain of module names to namespace-qualify a variable or function call. +/// _(INTERNALS)_ A chain of module names to namespace-qualify a variable or function call. /// Exported under the `internals` feature only. /// -/// A `u64` hash key is cached for quick search purposes. +/// A [`u64`] hash key is cached for quick search purposes. /// -/// 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. +/// 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 /// diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 6b4f6d9a..71c3fe27 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -22,7 +22,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; pub struct ModuleResolversCollection(Vec>); impl ModuleResolversCollection { - /// Create a new `ModuleResolversCollection`. + /// Create a new [`ModuleResolversCollection`]. /// /// # Example /// @@ -62,18 +62,18 @@ impl ModuleResolversCollection { pub fn clear(&mut self) { self.0.clear(); } - /// Is this `ModuleResolversCollection` empty? + /// Is this [`ModuleResolversCollection`] empty? #[inline(always)] 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 in this [`ModuleResolversCollection`]. #[inline(always)] pub fn len(&self) -> usize { self.0.len() } - /// Add another `ModuleResolversCollection` to the end of this collection. - /// The other `ModuleResolversCollection` is consumed. + /// Add another [`ModuleResolversCollection`] to the end of this collection. + /// The other [`ModuleResolversCollection`] is consumed. #[inline(always)] pub fn append(&mut self, other: Self) { self.0.extend(other.0.into_iter()); diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 4753937d..17423536 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -7,7 +7,8 @@ use crate::{Engine, EvalAltResult, Locked, Module, ModuleResolver, Position, Sha /// /// Script files are cached so they are are not reloaded and recompiled in subsequent requests. /// -/// The `new_with_path` and `new_with_path_and_extension` constructor functions +/// 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`). @@ -47,7 +48,7 @@ impl Default for FileModuleResolver { } impl FileModuleResolver { - /// Create a new `FileModuleResolver` with a specific base path. + /// Create a new [`FileModuleResolver`] with a specific base path. /// /// # Example /// @@ -67,7 +68,7 @@ impl FileModuleResolver { Self::new_with_path_and_extension(path, "rhai") } - /// Create a new `FileModuleResolver` with a specific base path and file extension. + /// Create a new [`FileModuleResolver`] with a specific base path and file extension. /// /// The default extension is `.rhai`. /// @@ -96,7 +97,7 @@ impl FileModuleResolver { } } - /// Create a new `FileModuleResolver` with the current directory as base path. + /// Create a new [`FileModuleResolver`] with the current directory as base path. /// /// # Example /// diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 09deb1b7..553a462f 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -22,7 +22,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; pub struct StaticModuleResolver(HashMap>); impl StaticModuleResolver { - /// Create a new `StaticModuleResolver`. + /// Create a new [`StaticModuleResolver`]. /// /// # Example /// @@ -83,18 +83,18 @@ impl StaticModuleResolver { pub fn clear(&mut self) { self.0.clear(); } - /// Is this `StaticModuleResolver` empty? + /// Is this [`StaticModuleResolver`] empty? #[inline(always)] pub fn is_empty(&self) -> bool { self.0.is_empty() } - /// Get the number of modules in this `StaticModuleResolver`. + /// Get the number of modules in this [`StaticModuleResolver`]. #[inline(always)] pub fn len(&self) -> usize { self.0.len() } - /// Merge another `StaticModuleResolver` into this. - /// The other `StaticModuleResolver` is consumed. + /// Merge another [`StaticModuleResolver`] into this. + /// The other [`StaticModuleResolver`] is consumed. #[inline(always)] pub fn merge(&mut self, other: Self) { if !other.is_empty() { diff --git a/src/optimize.rs b/src/optimize.rs index 01beb455..51fe2d2a 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -18,9 +18,7 @@ use crate::stdlib::{ }; use crate::token::is_valid_identifier; use crate::utils::get_hasher; -use crate::{ - calc_native_fn_hash, Dynamic, Engine, Module, Position, Scope, StaticVec, AST, NO_POS, -}; +use crate::{calc_native_fn_hash, Dynamic, Engine, Module, Position, Scope, StaticVec, AST}; /// Level of optimization performed. /// @@ -273,7 +271,7 @@ fn optimize_stmt_block( fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { match stmt { // expr op= expr - Stmt::Assignment(ref mut x, _) => match x.0 { + Stmt::Assignment(x, _) => match x.0 { Expr::Variable(_) => optimize_expr(&mut x.2, state), _ => { optimize_expr(&mut x.0, state); @@ -282,17 +280,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { }, // if false { if_block } -> Noop - Stmt::If(Expr::False(pos), x, _) if x.1.is_none() => { + Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_none() => { state.set_dirty(); *stmt = Stmt::Noop(*pos); } // if true { if_block } -> if_block - Stmt::If(Expr::True(_), x, _) if x.1.is_none() => { + Stmt::If(Expr::BoolConstant(true, _), x, _) if x.1.is_none() => { *stmt = mem::take(&mut x.0); optimize_stmt(stmt, state, true); } // if expr { Noop } - Stmt::If(ref mut condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => { + Stmt::If(condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => { state.set_dirty(); let pos = condition.position(); @@ -311,22 +309,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { }; } // if expr { if_block } - Stmt::If(ref mut condition, ref mut x, _) if x.1.is_none() => { + Stmt::If(condition, x, _) if x.1.is_none() => { optimize_expr(condition, state); optimize_stmt(&mut x.0, state, true); } // if false { if_block } else { else_block } -> else_block - Stmt::If(Expr::False(_), x, _) if x.1.is_some() => { + Stmt::If(Expr::BoolConstant(false, _), x, _) if x.1.is_some() => { *stmt = mem::take(x.1.as_mut().unwrap()); optimize_stmt(stmt, state, true); } // if true { if_block } else { else_block } -> if_block - Stmt::If(Expr::True(_), x, _) => { + Stmt::If(Expr::BoolConstant(true, _), x, _) => { *stmt = mem::take(&mut x.0); optimize_stmt(stmt, state, true); } // if expr { if_block } else { else_block } - Stmt::If(ref mut condition, ref mut x, _) => { + Stmt::If(condition, x, _) => { optimize_expr(condition, state); optimize_stmt(&mut x.0, state, true); if let Some(else_block) = x.1.as_mut() { @@ -375,15 +373,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { } // while false { block } -> Noop - Stmt::While(Expr::False(pos), _, _) => { + Stmt::While(Expr::BoolConstant(false, pos), _, _) => { state.set_dirty(); *stmt = Stmt::Noop(*pos) } - // while true { block } -> loop { block } - Stmt::While(Expr::True(_), block, pos) => { - optimize_stmt(block, state, false); - *stmt = Stmt::Loop(Box::new(mem::take(block)), *pos) - } // while expr { block } Stmt::While(condition, block, _) => { optimize_stmt(block, state, false); @@ -404,32 +397,30 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { _ => (), } } - // loop { block } - Stmt::Loop(block, _) => { - optimize_stmt(block, state, false); - - match **block { - // loop { break; } -> Noop - Stmt::Break(pos) => { - // Only a single break statement - state.set_dirty(); - *stmt = Stmt::Noop(pos) - } - _ => (), - } + // do { block } while false | do { block } until true -> { block } + Stmt::Do(block, Expr::BoolConstant(true, _), false, _) + | Stmt::Do(block, Expr::BoolConstant(false, _), true, _) => { + state.set_dirty(); + optimize_stmt(block.as_mut(), state, false); + *stmt = mem::take(block.as_mut()); + } + // do { block } while|until expr + Stmt::Do(block, condition, _, _) => { + optimize_stmt(block.as_mut(), state, false); + optimize_expr(condition, state); } // for id in expr { block } - Stmt::For(ref mut iterable, ref mut x, _) => { + Stmt::For(iterable, x, _) => { optimize_expr(iterable, state); optimize_stmt(&mut x.1, state, false); } // let id = expr; - Stmt::Let(_, Some(ref mut expr), _, _) => optimize_expr(expr, state), + Stmt::Let(_, Some(expr), _, _) => optimize_expr(expr, state), // let id; Stmt::Let(_, None, _, _) => (), // import expr as var; #[cfg(not(feature = "no_module"))] - Stmt::Import(ref mut expr, _, _) => optimize_expr(expr, state), + Stmt::Import(expr, _, _) => optimize_expr(expr, state), // { block } Stmt::Block(statements, pos) => { *stmt = optimize_stmt_block(mem::take(statements), *pos, state, preserve_result, true); @@ -448,7 +439,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { *stmt = Stmt::Block(statements, pos); } // try { block } catch ( var ) { block } - Stmt::TryCatch(ref mut x, _, _) => { + Stmt::TryCatch(x, _, _) => { optimize_stmt(&mut x.0, state, false); optimize_stmt(&mut x.2, state, false); } @@ -463,7 +454,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { *stmt = Stmt::Block(mem::take(x).into_vec(), *pos); } // expr; - Stmt::Expr(ref mut expr) => optimize_expr(expr, state), + Stmt::Expr(expr) => optimize_expr(expr, state), // return expr; Stmt::Return(_, Some(ref mut expr), _) => optimize_expr(expr, state), @@ -576,32 +567,23 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // "xxx" in "xxxxx" (Expr::StringConstant(a, pos), Expr::StringConstant(b, _)) => { state.set_dirty(); - *expr = if b.contains(a.as_str()) { Expr::True(*pos) } else { Expr::False(*pos) }; + *expr = Expr::BoolConstant( b.contains(a.as_str()), *pos); } // 'x' in "xxxxx" (Expr::CharConstant(a, pos), Expr::StringConstant(b, _)) => { state.set_dirty(); - *expr = if b.contains(*a) { Expr::True(*pos) } else { Expr::False(*pos) }; + *expr = Expr::BoolConstant(b.contains(*a), *pos); } // "xxx" in #{...} (Expr::StringConstant(a, pos), Expr::Map(b, _)) => { state.set_dirty(); - *expr = if b.iter().find(|(x, _)| x.name == *a).is_some() { - Expr::True(*pos) - } else { - Expr::False(*pos) - }; + *expr = Expr::BoolConstant(b.iter().find(|(x, _)| x.name == *a).is_some(), *pos); } // 'x' in #{...} (Expr::CharConstant(a, pos), Expr::Map(b, _)) => { state.set_dirty(); let ch = a.to_string(); - - *expr = if b.iter().find(|(x, _)| x.name == &ch).is_some() { - Expr::True(*pos) - } else { - Expr::False(*pos) - }; + *expr = Expr::BoolConstant(b.iter().find(|(x, _)| x.name == &ch).is_some(), *pos); } // lhs in rhs (lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); } @@ -609,18 +591,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // lhs && rhs Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) { // true && rhs -> rhs - (Expr::True(_), rhs) => { + (Expr::BoolConstant(true, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state); *expr = mem::take(rhs); } // false && rhs -> false - (Expr::False(pos), _) => { + (Expr::BoolConstant(false, pos), _) => { state.set_dirty(); - *expr = Expr::False(*pos); + *expr = Expr::BoolConstant(false, *pos); } // lhs && true -> lhs - (lhs, Expr::True(_)) => { + (lhs, Expr::BoolConstant(true, _)) => { state.set_dirty(); optimize_expr(lhs, state); *expr = mem::take(lhs); @@ -631,18 +613,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // lhs || rhs Expr::Or(ref mut x, _) => match (&mut x.lhs, &mut x.rhs) { // false || rhs -> rhs - (Expr::False(_), rhs) => { + (Expr::BoolConstant(false, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state); *expr = mem::take(rhs); } // true || rhs -> true - (Expr::True(pos), _) => { + (Expr::BoolConstant(true, pos), _) => { state.set_dirty(); - *expr = Expr::True(*pos); + *expr = Expr::BoolConstant(true, *pos); } // lhs || false - (lhs, Expr::False(_)) => { + (lhs, Expr::BoolConstant(false, _)) => { state.set_dirty(); optimize_expr(lhs, state); *expr = mem::take(lhs); @@ -712,7 +694,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { Some(arg_for_type_of.to_string().into()) } else { // Otherwise use the default value, if any - x.def_value.map(|v| v.into()) + x.def_value.clone() } }) .and_then(|result| map_dynamic_to_expr(result, *pos)) @@ -768,7 +750,7 @@ fn optimize( .iter() .filter(|(_, typ, _)| *typ) .for_each(|(name, _, value)| { - if let Some(val) = map_dynamic_to_expr(value, NO_POS) { + if let Some(val) = map_dynamic_to_expr(value, Position::NONE) { state.push_constant(name, val); } }); diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 5af2a2b8..9bc13cb8 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -2,7 +2,7 @@ use crate::plugin::*; use crate::stdlib::{format, string::String}; -use crate::{def_package, EvalAltResult, INT, NO_POS}; +use crate::{def_package, EvalAltResult, Position, INT}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; @@ -13,7 +13,7 @@ use num_traits::float::Float; #[inline(always)] pub fn make_err(msg: impl Into) -> Box { - EvalAltResult::ErrorArithmetic(msg.into(), NO_POS).into() + EvalAltResult::ErrorArithmetic(msg.into(), Position::NONE).into() } macro_rules! gen_arithmetic_functions { diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 8f493063..7858b775 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -5,8 +5,8 @@ use crate::engine::{OP_EQUALS, TYPICAL_ARRAY_SIZE}; use crate::plugin::*; use crate::stdlib::{any::TypeId, boxed::Box, cmp::max, cmp::Ordering, string::ToString}; use crate::{ - def_package, Array, Dynamic, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, INT, - NO_POS, + def_package, Array, Dynamic, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, + Position, INT, }; #[cfg(not(feature = "no_object"))] @@ -42,7 +42,7 @@ macro_rules! gen_array_functions { #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() { return EvalAltResult::ErrorDataTooLarge( - "Size of array".to_string(), NO_POS + "Size of array".to_string(), Position::NONE ).into(); } @@ -215,7 +215,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "map".to_string(), err, - NO_POS, + Position::NONE, )) })?, ); @@ -246,7 +246,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "filter".to_string(), err, - NO_POS, + Position::NONE, )) })? .as_bool() @@ -279,7 +279,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "index_of".to_string(), err, - NO_POS, + Position::NONE, )) })? .as_bool() @@ -312,7 +312,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "some".to_string(), err, - NO_POS, + Position::NONE, )) })? .as_bool() @@ -345,7 +345,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "all".to_string(), err, - NO_POS, + Position::NONE, )) })? .as_bool() @@ -380,7 +380,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), err, - NO_POS, + Position::NONE, )) })?; } @@ -398,7 +398,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), err, - NO_POS, + Position::NONE, )) })?; @@ -417,7 +417,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), err, - NO_POS, + Position::NONE, )) })?; } @@ -447,7 +447,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce_rev".to_string(), err, - NO_POS, + Position::NONE, )) })?; } @@ -465,7 +465,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce_rev".to_string(), err, - NO_POS, + Position::NONE, )) })?; @@ -484,7 +484,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce_rev".to_string(), err, - NO_POS, + Position::NONE, )) })?; } @@ -554,7 +554,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "drain".to_string(), err, - NO_POS, + Position::NONE, )) })? .as_bool() @@ -613,7 +613,7 @@ mod array_functions { Box::new(EvalAltResult::ErrorInFunctionCall( "retain".to_string(), err, - NO_POS, + Position::NONE, )) })? .as_bool() @@ -665,7 +665,7 @@ mod array_functions { for (a1, a2) in arr1.iter_mut().zip(arr2.iter_mut()) { let equals = ctx - .call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2], def_value.clone()) + .call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2], def_value.as_ref()) .map(|v| v.as_bool().unwrap_or(false))?; if !equals { diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 22cdbe09..a0039dc9 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -61,7 +61,7 @@ mod map_functions { for (m1, v1) in map1.iter_mut() { if let Some(v2) = map2.get_mut(m1) { let equals = ctx - .call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2], def_value.clone()) + .call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2], def_value.as_ref()) .map(|v| v.as_bool().unwrap_or(false))?; if !equals { diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index c186782a..1199d4cf 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, INT, NO_POS}; +use crate::{def_package, Position, INT}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; @@ -83,8 +83,11 @@ mod int_functions { #[rhai_fn(name = "parse_int", return_raw)] pub fn parse_int_radix(s: &str, radix: INT) -> Result> { if radix < 2 || radix > 36 { - return EvalAltResult::ErrorArithmetic(format!("Invalid radix: '{}'", radix), NO_POS) - .into(); + return EvalAltResult::ErrorArithmetic( + format!("Invalid radix: '{}'", radix), + Position::NONE, + ) + .into(); } INT::from_str_radix(s.trim(), radix as u32) @@ -92,7 +95,7 @@ mod int_functions { .map_err(|err| { EvalAltResult::ErrorArithmetic( format!("Error parsing integer number '{}': {}", s, err), - NO_POS, + Position::NONE, ) .into() }) @@ -201,8 +204,11 @@ mod float_functions { #[rhai_fn(name = "to_int", return_raw)] pub fn f32_to_int(x: f32) -> Result> { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) { - EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS) - .into() + EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::NONE, + ) + .into() } else { Ok((x.trunc() as INT).into()) } @@ -210,8 +216,11 @@ mod float_functions { #[rhai_fn(name = "to_int", return_raw)] pub fn f64_to_int(x: f64) -> Result> { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) { - EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS) - .into() + EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::NONE, + ) + .into() } else { Ok((x.trunc() as INT).into()) } @@ -224,7 +233,7 @@ mod float_functions { .map_err(|err| { EvalAltResult::ErrorArithmetic( format!("Error parsing floating-point number '{}': {}", s, err), - NO_POS, + Position::NONE, ) .into() }) diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 4b0f8ecc..ede3da61 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -44,39 +44,50 @@ pub trait Package { fn get(&self) -> PackageLibrary; } -/// A sharable `Module` to facilitate sharing library instances. +/// A sharable [`Module`][crate::Module] to facilitate sharing library instances. pub type PackageLibrary = Shared; -/// Type containing a collection of `PackageLibrary` instances. +/// Type containing a collection of [`PackageLibrary`] instances. /// All function and type iterator keys in the loaded packages are indexed for fast access. #[derive(Debug, Clone, Default)] -pub(crate) struct PackagesCollection(StaticVec); +pub(crate) struct PackagesCollection(Option>); impl PackagesCollection { - /// Add a `PackageLibrary` into the `PackagesCollection`. + /// Add a [`PackageLibrary`] into the [`PackagesCollection`]. /// /// Packages are searched in reverse order. pub fn add(&mut self, package: PackageLibrary) { + if self.0.is_none() { + self.0 = Some(Default::default()); + } // Later packages override previous ones. - self.0.insert(0, package); + self.0.as_mut().unwrap().insert(0, package); } - /// Does the specified function hash key exist in the `PackagesCollection`? + /// Does the specified function hash key exist in the [`PackagesCollection`]? #[allow(dead_code)] pub fn contains_fn(&self, hash: u64) -> bool { - self.0.iter().any(|p| p.contains_fn(hash, false)) + self.0 + .as_ref() + .map_or(false, |x| x.iter().any(|p| p.contains_fn(hash, false))) } /// Get specified function via its hash key. pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { - self.0.iter().find_map(|p| p.get_fn(hash, false)) + self.0 + .as_ref() + .and_then(|x| x.iter().find_map(|p| p.get_fn(hash, false))) } - /// Does the specified TypeId iterator exist in the `PackagesCollection`? + /// Does the specified [`TypeId`] iterator exist in the [`PackagesCollection`]? #[allow(dead_code)] pub fn contains_iter(&self, id: TypeId) -> bool { - self.0.iter().any(|p| p.contains_iter(id)) + self.0 + .as_ref() + .map_or(false, |x| x.iter().any(|p| p.contains_iter(id))) } - /// Get the specified TypeId iterator. + /// Get the specified [`TypeId`] iterator. pub fn get_iter(&self, id: TypeId) -> Option { - self.0.iter().find_map(|p| p.get_iter(id)) + self.0 + .as_ref() + .and_then(|x| x.iter().find_map(|p| p.get_iter(id))) } } @@ -84,7 +95,7 @@ impl PackagesCollection { /// and register functions into it. /// /// Functions can be added to the package using the standard module methods such as -/// `set_fn_2`, `set_fn_3_mut`, `set_fn_0` etc. +/// [`set_fn_2`][Module::set_fn_2], [`set_fn_3_mut`][Module::set_fn_3_mut], [`set_fn_0`][Module::set_fn_0] etc. /// /// # Example /// diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 661e00e8..1a50b32d 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -252,7 +252,7 @@ mod string_functions { if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { return crate::EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - crate::NO_POS, + crate::Position::NONE, ) .into(); } @@ -272,7 +272,7 @@ mod string_functions { { return crate::EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - crate::NO_POS, + crate::Position::NONE, ) .into(); } @@ -293,7 +293,7 @@ mod string_functions { if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { return crate::EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - crate::NO_POS, + crate::Position::NONE, ) .into(); } @@ -320,7 +320,7 @@ mod string_functions { { return crate::EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - crate::NO_POS, + crate::Position::NONE, ) .into(); } diff --git a/src/parse_error.rs b/src/parse_error.rs index cd90d2d7..9b851818 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -6,9 +6,9 @@ use crate::stdlib::{ fmt, string::{String, ToString}, }; -use crate::{EvalAltResult, Position, NO_POS}; +use crate::{EvalAltResult, Position}; -/// _[INTERNALS]_ Error encountered when tokenizing the script text. +/// _(INTERNALS)_ Error encountered when tokenizing the script text. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -65,7 +65,7 @@ impl LexError { Self::ImproperSymbol(_) => "Invalid symbol encountered", } } - /// Convert a `&LexError` into a `ParseError`. + /// Convert a `&LexError` into a [`ParseError`]. #[inline(always)] pub fn into_err(&self, pos: Position) -> ParseError { ParseError(Box::new(self.clone().into()), pos) @@ -160,7 +160,7 @@ pub enum ParseErrorType { } impl ParseErrorType { - /// Make a `ParseError` using the current type and position. + /// Make a [`ParseError`] using the current type and position. #[inline(always)] pub(crate) fn into_err(self, pos: Position) -> ParseError { ParseError(Box::new(self), pos) @@ -281,7 +281,7 @@ impl fmt::Display for ParseError { impl From for Box { #[inline(always)] fn from(err: ParseErrorType) -> Self { - Box::new(EvalAltResult::ErrorParsing(err, NO_POS)) + Box::new(EvalAltResult::ErrorParsing(err, Position::NONE)) } } diff --git a/src/parser.rs b/src/parser.rs index 795993de..aa64a272 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -26,7 +26,7 @@ use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream} use crate::utils::{get_hasher, StraightHasherBuilder}; use crate::{ calc_script_fn_hash, Dynamic, Engine, FnAccess, ImmutableString, LexError, ParseError, - ParseErrorType, Position, Scope, StaticVec, AST, NO_POS, + ParseErrorType, Position, Scope, StaticVec, AST, }; #[cfg(not(feature = "no_float"))] @@ -49,6 +49,8 @@ struct ParseState<'e> { /// Tracks a list of external variables (variables that are not explicitly declared in the scope). #[cfg(not(feature = "no_closure"))] externals: HashMap, + /// Always search for variables instead of direct indexing into the scope. + always_search: bool, /// An indicator that disables variable capturing into externals one single time /// up until the nearest consumed Identifier token. /// If set to false the next call to `access_var` will not capture the variable. @@ -92,6 +94,7 @@ impl<'e> ParseState<'e> { allow_capture: true, strings: HashMap::with_capacity(64), stack: Vec::with_capacity(16), + always_search: false, #[cfg(not(feature = "no_module"))] modules: Default::default(), } @@ -103,6 +106,7 @@ impl<'e> ParseState<'e> { /// /// The return value is the offset to be deducted from `Stack::len`, /// i.e. the top element of the `ParseState` is offset 1. + /// /// Return `None` when the variable name is not found in the `stack`. #[inline] fn access_var(&mut self, name: &str, _pos: Position) -> Option { @@ -123,7 +127,11 @@ impl<'e> ParseState<'e> { self.allow_capture = true } - index + if self.always_search { + None + } else { + index + } } /// Find a module by name in the `ParseState`, searching in reverse. @@ -464,8 +472,7 @@ fn parse_index_chain( | Expr::And(_, _) | Expr::Or(_, _) | Expr::In(_, _) - | Expr::True(_) - | Expr::False(_) + | Expr::BoolConstant(_, _) | Expr::Unit(_) => { return Err(PERR::MalformedIndexExpr( "Only arrays, object maps and strings can be indexed".into(), @@ -499,8 +506,7 @@ fn parse_index_chain( | Expr::And(_, _) | Expr::Or(_, _) | Expr::In(_, _) - | Expr::True(_) - | Expr::False(_) + | Expr::BoolConstant(_, _) | Expr::Unit(_) => { return Err(PERR::MalformedIndexExpr( "Only arrays, object maps and strings can be indexed".into(), @@ -541,7 +547,7 @@ fn parse_index_chain( .into_err(x.position())) } // lhs[true], lhs[false] - x @ Expr::True(_) | x @ Expr::False(_) => { + x @ Expr::BoolConstant(_, _) => { return Err(PERR::MalformedIndexExpr( "Array access expects integer index, not a boolean".into(), ) @@ -999,8 +1005,8 @@ fn parse_primary( Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?, #[cfg(not(feature = "no_object"))] Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?, - Token::True => Expr::True(settings.pos), - Token::False => Expr::False(settings.pos), + Token::True => Expr::BoolConstant(true, settings.pos), + Token::False => Expr::BoolConstant(false, settings.pos), Token::LexError(err) => return Err(err.into_err(settings.pos)), _ => { @@ -1197,7 +1203,7 @@ fn parse_unary( native_only: true, hash, args, - def_value: Some(false), // NOT operator, when operating on invalid operand, defaults to false + def_value: Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false ..Default::default() }), pos, @@ -1451,8 +1457,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( "'in' expression expects a string, array or object map".into(), @@ -1492,8 +1497,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + | (x @ Expr::BoolConstant(_, _), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not a boolean".into(), ) @@ -1545,8 +1549,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + | (x @ Expr::BoolConstant(_, _), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not a boolean".into(), ) @@ -1657,7 +1660,7 @@ fn parse_binary_op( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let cmp_def = Some(false); + let cmp_def = Some(false.into()); let op = op_token.syntax(); let hash = calc_script_fn_hash(empty(), &op, 2); @@ -1697,7 +1700,7 @@ fn parse_binary_op( Box::new(FnCallExpr { hash, args, - def_value: Some(true), + def_value: Some(true.into()), ..op_base }), pos, @@ -1790,6 +1793,7 @@ fn parse_custom_syntax( state.stack.len() + delta as usize, ("".into(), ScopeEntryType::Normal), ); + state.always_search = true; } delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(), delta if delta < 0 => state @@ -1975,49 +1979,68 @@ fn parse_if( } /// Parse a while loop. -fn parse_while( +fn parse_while_loop( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, mut settings: ParseSettings, ) -> Result { - // while ... - let token_pos = eat_token(input, Token::While); - settings.pos = token_pos; - #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - // while guard { body } - ensure_not_statement_expr(input, "a boolean")?; - let guard = parse_expr(input, state, lib, settings.level_up())?; - ensure_not_assignment(input)?; + // while|loops ... + let (guard, token_pos) = match input.next().unwrap() { + (Token::While, pos) => { + ensure_not_statement_expr(input, "a boolean")?; + (parse_expr(input, state, lib, settings.level_up())?, pos) + } + (Token::Loop, pos) => (Expr::BoolConstant(true, pos), pos), + _ => unreachable!(), + }; + settings.pos = token_pos; + ensure_not_assignment(input)?; settings.is_breakable = true; let body = Box::new(parse_block(input, state, lib, settings.level_up())?); Ok(Stmt::While(guard, body, token_pos)) } -/// Parse a loop statement. -fn parse_loop( +/// Parse a do loop. +fn parse_do( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, mut settings: ParseSettings, ) -> Result { - // loop ... - let token_pos = eat_token(input, Token::Loop); + // do ... + let token_pos = eat_token(input, Token::Do); settings.pos = token_pos; #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - // loop { body } + // do { body } [while|until] guard settings.is_breakable = true; let body = Box::new(parse_block(input, state, lib, settings.level_up())?); - Ok(Stmt::Loop(body, token_pos)) + let is_while = match input.next().unwrap() { + (Token::While, _) => true, + (Token::Until, _) => false, + (_, pos) => { + return Err( + PERR::MissingToken(Token::While.into(), "for the do statement".into()) + .into_err(pos), + ) + } + }; + + ensure_not_statement_expr(input, "a boolean")?; + settings.is_breakable = false; + let guard = parse_expr(input, state, lib, settings.level_up())?; + ensure_not_assignment(input)?; + + Ok(Stmt::Do(body, guard, is_while, token_pos)) } /// Parse a for loop. @@ -2270,6 +2293,7 @@ fn parse_block( settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut statements = Vec::with_capacity(8); + let prev_always_search = state.always_search; let prev_stack_len = state.stack.len(); #[cfg(not(feature = "no_module"))] @@ -2319,6 +2343,10 @@ fn parse_block( #[cfg(not(feature = "no_module"))] state.modules.truncate(prev_mods_len); + // The impact of new local variables goes away at the end of a block + // because any new variables introduced will go out of scope + state.always_search = prev_always_search; + Ok(Stmt::Block(statements, settings.pos)) } @@ -2358,10 +2386,11 @@ fn parse_stmt( settings.ensure_level_within_max_limit(state.max_expr_depth)?; match token { - // Semicolon - empty statement + // ; - empty statement Token::SemiColon => Ok(Some(Stmt::Noop(settings.pos))), - Token::LeftBrace => parse_block(input, state, lib, settings.level_up()).map(Some), + // { - statements block + Token::LeftBrace => Ok(Some(parse_block(input, state, lib, settings.level_up())?)), // fn ... #[cfg(not(feature = "no_function"))] @@ -2418,8 +2447,10 @@ fn parse_stmt( } Token::If => parse_if(input, state, lib, settings.level_up()).map(Some), - Token::While => parse_while(input, state, lib, settings.level_up()).map(Some), - Token::Loop => parse_loop(input, state, lib, settings.level_up()).map(Some), + Token::While | Token::Loop => { + parse_while_loop(input, state, lib, settings.level_up()).map(Some) + } + Token::Do => parse_do(input, state, lib, settings.level_up()).map(Some), Token::For => parse_for(input, state, lib, settings.level_up()).map(Some), Token::Continue if settings.is_breakable => { @@ -2838,7 +2869,7 @@ impl Engine { is_function_scope: false, is_breakable: false, level: 0, - pos: NO_POS, + pos: Position::NONE, }; let expr = parse_expr(input, &mut state, &mut functions, settings)?; @@ -2891,7 +2922,7 @@ impl Engine { is_function_scope: false, is_breakable: false, level: 0, - pos: NO_POS, + pos: Position::NONE, }; let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? { @@ -2961,8 +2992,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { Union::Int(value) => Some(Expr::IntegerConstant(value, pos)), Union::Char(value) => Some(Expr::CharConstant(value, pos)), Union::Str(value) => Some(Expr::StringConstant(value, pos)), - Union::Bool(true) => Some(Expr::True(pos)), - Union::Bool(false) => Some(Expr::False(pos)), + Union::Bool(value) => Some(Expr::BoolConstant(value, pos)), #[cfg(not(feature = "no_index"))] Union::Array(array) => { let items: Vec<_> = array diff --git a/src/result.rs b/src/result.rs index 96450c6d..e9c5d224 100644 --- a/src/result.rs +++ b/src/result.rs @@ -6,15 +6,16 @@ use crate::stdlib::{ fmt, string::{String, ToString}, }; -use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT, NO_POS}; +use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; /// Evaluation result. /// -/// All wrapped `Position` values represent the location in the script where the error occurs. +/// All wrapped [`Position`] values represent the location in the script where the error occurs. /// /// # Thread Safety /// -/// Currently, `EvalAltResult` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. +/// Currently, [`EvalAltResult`] is neither [`Send`] nor [`Sync`]. +/// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`]. #[derive(Debug)] #[non_exhaustive] pub enum EvalAltResult { @@ -246,7 +247,7 @@ impl fmt::Display for EvalAltResult { impl> From for EvalAltResult { #[inline(always)] fn from(err: T) -> Self { - Self::ErrorRuntime(err.as_ref().to_string().into(), NO_POS) + Self::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE) } } @@ -255,7 +256,7 @@ impl> From for Box { fn from(err: T) -> Self { Box::new(EvalAltResult::ErrorRuntime( err.as_ref().to_string().into(), - NO_POS, + Position::NONE, )) } } @@ -313,10 +314,10 @@ impl EvalAltResult { _ => false, } } - /// Get the `Position` of this error. + /// Get the [`Position`] of this error. pub fn position(&self) -> Position { match self { - Self::ErrorSystem(_, _) => NO_POS, + Self::ErrorSystem(_, _) => Position::NONE, Self::ErrorParsing(_, pos) | Self::ErrorFunctionNotFound(_, pos) @@ -346,7 +347,7 @@ impl EvalAltResult { | Self::Return(_, pos) => *pos, } } - /// Override the `Position` of this error. + /// Override the [`Position`] of this error. pub fn set_position(&mut self, new_position: Position) { match self { Self::ErrorSystem(_, _) => (), @@ -379,8 +380,8 @@ impl EvalAltResult { | Self::Return(_, pos) => *pos = new_position, } } - /// Consume the current `EvalAltResult` and return a new one with the specified `Position` - /// if the current position is `Position::None`. + /// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`] + /// if the current position is [`Position::None`]. #[inline(always)] pub(crate) fn fill_position(mut self: Box, new_position: Position) -> Box { if self.position().is_none() { diff --git a/src/scope.rs b/src/scope.rs index 07b8cd11..c54ada9d 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,4 +1,4 @@ -//! Module that defines the `Scope` type representing a function call-stack scope. +//! Module that defines the [`Scope`] type representing a function call-stack scope. use crate::dynamic::Variant; use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec}; @@ -25,11 +25,12 @@ impl EntryType { } /// Type containing information about the current scope. -/// Useful for keeping state between `Engine` evaluation runs. +/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs. /// /// # Thread Safety /// -/// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. +/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. +/// Turn on the [`Sync`] feature to make it [`Send`] `+` [`Sync`]. /// /// # Example /// @@ -57,12 +58,12 @@ impl EntryType { // // # Implementation Notes // -// `Scope` is implemented as three `Vec`'s of exactly the same length. Variables data (name, type, etc.) +// [`Scope`] is implemented as three [`Vec`]'s of exactly the same length. Variables data (name, type, etc.) // is manually split into three equal-length arrays. That's because variable names take up the most space, -// with `Cow` being four words long, but in the vast majority of cases the name is NOT used to look up +// with [`Cow`][Cow] being four words long, but in the vast majority of cases the name is NOT used to look up // a variable's value. Variable lookup is usually via direct index, by-passing the name altogether. // -// Since `Dynamic` is reasonably small, packing it tightly improves cache locality when variables are accessed. +// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed. // The variable type is packed separately into another array because it is even smaller. #[derive(Debug, Clone)] pub struct Scope<'a> { @@ -85,7 +86,7 @@ impl Default for Scope<'_> { } impl<'a> Scope<'a> { - /// Create a new Scope. + /// Create a new [`Scope`]. /// /// # Example /// @@ -101,7 +102,7 @@ impl<'a> Scope<'a> { pub fn new() -> Self { Default::default() } - /// Empty the Scope. + /// Empty the [`Scope`]. /// /// # Example /// @@ -127,7 +128,7 @@ impl<'a> Scope<'a> { self.values.clear(); self } - /// Get the number of entries inside the Scope. + /// Get the number of entries inside the [`Scope`]. /// /// # Example /// @@ -144,7 +145,7 @@ impl<'a> Scope<'a> { pub fn len(&self) -> usize { self.values.len() } - /// Is the Scope empty? + /// Is the [`Scope`] empty? /// /// # Example /// @@ -161,7 +162,7 @@ impl<'a> Scope<'a> { pub fn is_empty(&self) -> bool { self.values.len() == 0 } - /// Add (push) a new entry to the Scope. + /// Add (push) a new entry to the [`Scope`]. /// /// # Example /// @@ -181,7 +182,7 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value)) } - /// Add (push) a new `Dynamic` entry to the Scope. + /// Add (push) a new [`Dynamic`] entry to the [`Scope`]. /// /// # Example /// @@ -197,13 +198,10 @@ impl<'a> Scope<'a> { pub fn push_dynamic(&mut self, name: impl Into>, value: Dynamic) -> &mut Self { self.push_dynamic_value(name, EntryType::Normal, value) } - /// Add (push) a new constant to the Scope. + /// Add (push) a new constant to the [`Scope`]. /// /// Constants are immutable and cannot be assigned to. Their values never change. - /// Constants propagation is a technique used to optimize an AST. - /// - /// However, in order to be used for optimization, constants must be in one of the recognized types: - /// `INT` (default to `i64`, `i32` if `only_i32`), `f64`, `String`, `char` and `bool`. + /// Constants propagation is a technique used to optimize an [`AST`][crate::AST]. /// /// # Example /// @@ -223,14 +221,10 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value)) } - /// Add (push) a new constant with a `Dynamic` value to the Scope. + /// Add (push) a new constant with a [`Dynamic`] value to the Scope. /// /// Constants are immutable and cannot be assigned to. Their values never change. - /// Constants propagation is a technique used to optimize an AST. - /// - /// However, in order to be used for optimization, the `Dynamic` value must be in one of the - /// recognized types: - /// `INT` (default to `i64`, `i32` if `only_i32`), `f64`, `String`, `char` and `bool`. + /// Constants propagation is a technique used to optimize an [`AST`][crate::AST]. /// /// # Example /// @@ -250,7 +244,7 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Constant, value) } - /// Add (push) a new entry with a `Dynamic` value to the Scope. + /// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`]. #[inline] pub(crate) fn push_dynamic_value( &mut self, @@ -263,7 +257,7 @@ impl<'a> Scope<'a> { self.values.push(value.into()); self } - /// Truncate (rewind) the Scope to a previous size. + /// Truncate (rewind) the [`Scope`] to a previous size. /// /// # Example /// @@ -296,7 +290,7 @@ impl<'a> Scope<'a> { self.values.truncate(size); self } - /// Does the scope contain the entry? + /// Does the [`Scope`] contain the entry? /// /// # Example /// @@ -316,7 +310,7 @@ impl<'a> Scope<'a> { .rev() // Always search a Scope in reverse order .any(|(key, _)| name == key.as_ref()) } - /// Find an entry in the Scope, starting from the last. + /// Find an entry in the [`Scope`], starting from the last. #[inline(always)] pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> { self.names @@ -331,7 +325,7 @@ impl<'a> Scope<'a> { } }) } - /// Get the value of an entry in the Scope, starting from the last. + /// Get the value of an entry in the [`Scope`], starting from the last. /// /// # Example /// @@ -352,7 +346,8 @@ impl<'a> Scope<'a> { .find(|(_, (key, _))| name == key.as_ref()) .and_then(|(index, _)| self.values[index].flatten_clone().try_cast()) } - /// Update the value of the named entry. + /// Update the value of the named entry in the [`Scope`]. + /// /// Search starts backwards from the last, and only the first entry matching the specified name is updated. /// If no entry matching the specified name is found, a new one is added. /// @@ -386,7 +381,7 @@ impl<'a> Scope<'a> { } self } - /// Get a mutable reference to an entry in the Scope. + /// Get a mutable reference to an entry in the [`Scope`]. #[inline(always)] pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) { ( @@ -394,7 +389,7 @@ impl<'a> Scope<'a> { self.types[index], ) } - /// Update the access type of an entry in the Scope. + /// Update the access type of an entry in the [`Scope`]. #[cfg(not(feature = "no_module"))] #[inline(always)] pub(crate) fn add_entry_alias(&mut self, index: usize, alias: String) -> &mut Self { @@ -404,7 +399,7 @@ impl<'a> Scope<'a> { } self } - /// Clone the Scope, keeping only the last instances of each variable name. + /// Clone the [`Scope`], keeping only the last instances of each variable name. /// Shadowed variables are omitted in the copy. #[inline] pub(crate) fn clone_visible(&self) -> Self { @@ -424,7 +419,7 @@ impl<'a> Scope<'a> { entries } - /// Get an iterator to entries in the Scope. + /// Get an iterator to entries in the [`Scope`]. #[inline(always)] #[allow(dead_code)] pub(crate) fn into_iter( @@ -435,7 +430,7 @@ impl<'a> Scope<'a> { .zip(self.types.into_iter().zip(self.values.into_iter())) .map(|((name, alias), (typ, value))| (name, typ, value, alias.to_vec())) } - /// Get an iterator to entries in the Scope. + /// Get an iterator to entries in the [`Scope`]. /// Shared values are flatten-cloned. /// /// # Example @@ -465,7 +460,7 @@ impl<'a> Scope<'a> { self.iter_raw() .map(|(name, constant, value)| (name, constant, value.flatten_clone())) } - /// Get an iterator to entries in the Scope. + /// Get an iterator to entries in the [`Scope`]. /// Shared values are not expanded. #[inline(always)] pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator + 'x { diff --git a/src/serde_impl/de.rs b/src/serde_impl/de.rs index a259377a..b4e6786c 100644 --- a/src/serde_impl/de.rs +++ b/src/serde_impl/de.rs @@ -1,9 +1,9 @@ -//! Implement deserialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). +//! Implement deserialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. use super::str::ImmutableStringDeserializer; use crate::dynamic::Union; use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString}; -use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, ParseErrorType, NO_POS}; +use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, ParseErrorType, Position}; use serde::de::{ DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor, }; @@ -15,19 +15,19 @@ use crate::Array; #[cfg(not(feature = "no_object"))] use crate::Map; -/// Deserializer for `Dynamic` which is kept as a reference. +/// Deserializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. /// /// The reference is necessary because the deserialized type may hold references -/// (especially `&str`) to the source `Dynamic`. +/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. pub struct DynamicDeserializer<'a> { value: &'a Dynamic, } impl<'de> DynamicDeserializer<'de> { - /// Create a `DynamicDeserializer` from a reference to a `Dynamic` value. + /// Create a [`DynamicDeserializer`] from a reference to a [`Dynamic`][crate::Dynamic] value. /// /// The reference is necessary because the deserialized type may hold references - /// (especially `&str`) to the source `Dynamic`. + /// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. pub fn from_dynamic(value: &'de Dynamic) -> Self { Self { value } } @@ -37,8 +37,12 @@ impl<'de> DynamicDeserializer<'de> { } /// Shortcut for a type conversion error. fn type_error_str(&self, error: &str) -> Result> { - EvalAltResult::ErrorMismatchOutputType(error.into(), self.value.type_name().into(), NO_POS) - .into() + EvalAltResult::ErrorMismatchOutputType( + error.into(), + self.value.type_name().into(), + Position::NONE, + ) + .into() } fn deserialize_int>( &mut self, @@ -56,7 +60,7 @@ impl<'de> DynamicDeserializer<'de> { } } -/// Deserialize a `Dynamic` value into a Rust type that implements `serde::Deserialize`. +/// Deserialize a [`Dynamic`][crate::Dynamic] value into a Rust type that implements [`serde::Deserialize`]. /// /// # Example /// @@ -117,7 +121,7 @@ impl Error for Box { fn custom(err: T) -> Self { EvalAltResult::ErrorParsing( ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())), - NO_POS, + Position::NONE, ) .into() } @@ -445,7 +449,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { /// `SeqAccess` implementation for arrays. struct IterateArray<'a, ITER: Iterator> { - /// Iterator for a stream of `Dynamic` values. + /// Iterator for a stream of [`Dynamic`][crate::Dynamic] values. iter: ITER, } @@ -479,9 +483,9 @@ where KEYS: Iterator, VALUES: Iterator, { - // Iterator for a stream of `Dynamic` keys. + // Iterator for a stream of [`Dynamic`][crate::Dynamic] keys. keys: KEYS, - // Iterator for a stream of `Dynamic` values. + // Iterator for a stream of [`Dynamic`][crate::Dynamic] values. values: VALUES, } diff --git a/src/serde_impl/mod.rs b/src/serde_impl/mod.rs index 2ed95bb4..da853425 100644 --- a/src/serde_impl/mod.rs +++ b/src/serde_impl/mod.rs @@ -1,4 +1,4 @@ -//! Helper module defining serialization/deserialization support for [`serde`](https://crates.io/crates/serde). +//! Helper module defining serialization/deserialization support for [`serde`]. pub mod de; pub mod ser; diff --git a/src/serde_impl/ser.rs b/src/serde_impl/ser.rs index 97ef4bfb..b4c6dedd 100644 --- a/src/serde_impl/ser.rs +++ b/src/serde_impl/ser.rs @@ -1,7 +1,7 @@ -//! Implement serialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). +//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. use crate::stdlib::{boxed::Box, fmt, string::ToString}; -use crate::{Dynamic, EvalAltResult, NO_POS}; +use crate::{Dynamic, EvalAltResult, Position}; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, Serializer, @@ -14,7 +14,7 @@ use crate::Array; #[cfg(not(feature = "no_object"))] use crate::Map; -/// Serializer for `Dynamic` which is kept as a reference. +/// Serializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. pub struct DynamicSerializer { /// Buffer to hold a temporary key. _key: Dynamic, @@ -23,7 +23,7 @@ pub struct DynamicSerializer { } impl DynamicSerializer { - /// Create a `DynamicSerializer` from a `Dynamic` value. + /// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value. pub fn new(_value: Dynamic) -> Self { Self { _key: Default::default(), @@ -32,7 +32,7 @@ impl DynamicSerializer { } } -/// Serialize a Rust type that implements `serde::Serialize` into a `Dynamic`. +/// Serialize a Rust type that implements [`serde::Serialize`] into a [`Dynamic`][crate::Dynamic]. /// /// # Example /// @@ -87,7 +87,7 @@ pub fn to_dynamic(value: T) -> Result> impl Error for Box { fn custom(err: T) -> Self { - EvalAltResult::ErrorRuntime(err.to_string().into(), NO_POS).into() + EvalAltResult::ErrorRuntime(err.to_string().into(), Position::NONE).into() } } @@ -283,16 +283,24 @@ impl Serializer for &mut DynamicSerializer { make_variant(_variant, content) } #[cfg(feature = "no_object")] - return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS) - .into(); + return EvalAltResult::ErrorMismatchOutputType( + "Dynamic".into(), + "map".into(), + Position::NONE, + ) + .into(); } fn serialize_seq(self, _len: Option) -> Result> { #[cfg(not(feature = "no_index"))] return Ok(DynamicSerializer::new(Array::new().into())); #[cfg(feature = "no_index")] - return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "array".into(), NO_POS) - .into(); + return EvalAltResult::ErrorMismatchOutputType( + "Dynamic".into(), + "array".into(), + Position::NONE, + ) + .into(); } fn serialize_tuple(self, len: usize) -> Result> { @@ -325,7 +333,12 @@ impl Serializer for &mut DynamicSerializer { let err_type = "map"; #[cfg(not(feature = "no_object"))] let err_type = "array"; - EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), err_type.into(), NO_POS).into() + EvalAltResult::ErrorMismatchOutputType( + "Dynamic".into(), + err_type.into(), + Position::NONE, + ) + .into() } } @@ -333,8 +346,12 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_object"))] return Ok(DynamicSerializer::new(Map::new().into())); #[cfg(feature = "no_object")] - return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS) - .into(); + return EvalAltResult::ErrorMismatchOutputType( + "Dynamic".into(), + "map".into(), + Position::NONE, + ) + .into(); } fn serialize_struct( @@ -358,8 +375,12 @@ impl Serializer for &mut DynamicSerializer { map: Map::with_capacity(_len), }); #[cfg(feature = "no_object")] - return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS) - .into(); + return EvalAltResult::ErrorMismatchOutputType( + "Dynamic".into(), + "map".into(), + Position::NONE, + ) + .into(); } } @@ -468,7 +489,11 @@ impl SerializeMap for DynamicSerializer { let key = crate::stdlib::mem::take(&mut self._key) .take_immutable_string() .map_err(|typ| { - EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS) + EvalAltResult::ErrorMismatchOutputType( + "string".into(), + typ.into(), + Position::NONE, + ) })?; let _value = _value.serialize(&mut *self)?; let map = self._value.downcast_mut::().unwrap(); @@ -488,7 +513,7 @@ impl SerializeMap for DynamicSerializer { { let _key: Dynamic = _key.serialize(&mut *self)?; let _key = _key.take_immutable_string().map_err(|typ| { - EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS) + EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), Position::NONE) })?; let _value = _value.serialize(&mut *self)?; let map = self._value.downcast_mut::().unwrap(); diff --git a/src/serde_impl/str.rs b/src/serde_impl/str.rs index 4c98966b..6c14be25 100644 --- a/src/serde_impl/str.rs +++ b/src/serde_impl/str.rs @@ -1,7 +1,7 @@ -//! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde). +//! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`]. use crate::stdlib::{any::type_name, boxed::Box}; -use crate::{EvalAltResult, ImmutableString, NO_POS}; +use crate::{EvalAltResult, ImmutableString, Position}; use serde::de::{Deserializer, Visitor}; /// Deserializer for `ImmutableString`. @@ -16,8 +16,12 @@ impl<'a> ImmutableStringDeserializer<'a> { } /// Shortcut for a type conversion error. fn type_error(&self) -> Result> { - EvalAltResult::ErrorMismatchOutputType(type_name::().into(), "string".into(), NO_POS) - .into() + EvalAltResult::ErrorMismatchOutputType( + type_name::().into(), + "string".into(), + Position::NONE, + ) + .into() } } diff --git a/src/syntax.rs b/src/syntax.rs index 77527fab..e3e6f1ca 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,4 +1,4 @@ -//! Module implementing custom syntax for `Engine`. +//! Module implementing custom syntax for [`Engine`]. use crate::ast::Expr; use crate::engine::{EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; @@ -11,7 +11,7 @@ use crate::stdlib::{ use crate::token::{is_valid_identifier, Token}; use crate::{ Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseError, Position, Shared, - StaticVec, NO_POS, + StaticVec, }; /// A general expression evaluation trait object. @@ -31,7 +31,7 @@ pub type FnCustomSyntaxParse = dyn Fn(&[String]) -> Result, Parse pub type FnCustomSyntaxParse = dyn Fn(&[String]) -> Result, ParseError> + Send + Sync; -/// An expression sub-tree in an AST. +/// An expression sub-tree in an [`AST`][crate::AST]. #[derive(Debug, Clone)] pub struct Expression<'a>(&'a Expr); @@ -43,7 +43,7 @@ impl<'a> From<&'a Expr> for Expression<'a> { } impl Expression<'_> { - /// If this expression is a variable name, return it. Otherwise `None`. + /// If this expression is a variable name, return it. Otherwise [`None`]. #[inline(always)] pub fn get_variable_name(&self) -> Option<&str> { self.0.get_variable_access(true) @@ -65,7 +65,7 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> { /// /// ## WARNING - Low Level API /// - /// This function is very low level. It evaluates an expression from an AST. + /// This function is very low level. It evaluates an expression from an [`AST`][crate::AST]. #[inline(always)] pub fn eval_expression_tree( &mut self, @@ -95,10 +95,10 @@ pub struct CustomSyntax { } impl Engine { - /// Register a custom syntax with the `Engine`. + /// Register a custom syntax with the [`Engine`]. /// /// * `keywords` holds a slice of strings that define the custom syntax. - /// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative). + /// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative). /// * `func` is the implementation function. pub fn register_custom_syntax + ToString>( &mut self, @@ -142,7 +142,7 @@ impl Engine { segments.len() + 1, s )) - .into_err(NO_POS) + .into_err(Position::NONE) .into()); } // Identifier in first position @@ -159,7 +159,7 @@ impl Engine { segments.len() + 1, s )) - .into_err(NO_POS) + .into_err(Position::NONE) .into()); } }; @@ -191,17 +191,17 @@ impl Engine { Ok(self) } - /// Register a custom syntax with the `Engine`. + /// Register a custom syntax with the [`Engine`]. /// /// ## WARNING - Low Level API /// /// This function is very low level. /// - /// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative). + /// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative). /// * `parse` is the parsing function. /// * `func` is the implementation function. /// - /// All custom keywords must be manually registered via `Engine::register_custom_operator`. + /// All custom keywords must be manually registered via [`register_custom_operator`][Engine::register_custom_operator]. /// Otherwise, custom keywords won't be recognized. pub fn register_custom_syntax_raw( &mut self, diff --git a/src/token.rs b/src/token.rs index cdf48dd4..c36a5651 100644 --- a/src/token.rs +++ b/src/token.rs @@ -37,19 +37,16 @@ pub struct Position { pos: u16, } -/// No `Position`. -pub const NO_POS: Position = Position::NONE; - impl Position { - /// A `Position` representing no position. + /// A [`Position`] representing no position. pub const NONE: Self = Self { line: 0, pos: 0 }; - /// A `Position` representing the first position. + /// A [`Position`] representing the first position. pub const START: Self = Self { line: 1, pos: 0 }; - /// Create a new `Position`. + /// Create a new [`Position`]. /// /// `line` must not be zero. - /// If `position` is zero, then it is at the beginning of a line. + /// If [`Position`] is zero, then it is at the beginning of a line. /// /// # Panics /// @@ -63,7 +60,7 @@ impl Position { pos: position, } } - /// Get the line number (1-based), or `None` if there is no position. + /// Get the line number (1-based), or [`None`] if there is no position. #[inline(always)] pub fn line(self) -> Option { if self.is_none() { @@ -72,7 +69,7 @@ impl Position { Some(self.line as usize) } } - /// Get the character position (1-based), or `None` if at beginning of a line. + /// Get the character position (1-based), or [`None`] if at beginning of a line. #[inline(always)] pub fn position(self) -> Option { if self.is_none() || self.pos == 0 { @@ -113,12 +110,12 @@ impl Position { self.pos = 0; } } - /// Is this `Position` at the beginning of a line? + /// Is this [`Position`] at the beginning of a line? #[inline(always)] pub fn is_beginning_of_line(self) -> bool { self.line == 0 && !self.is_none() } - /// Is there no `Position`? + /// Is there no [`Position`]? #[inline(always)] pub fn is_none(self) -> bool { self == Self::NONE @@ -150,7 +147,7 @@ impl fmt::Debug for Position { } } -/// _[INTERNALS]_ A Rhai language token. +/// _(INTERNALS)_ A Rhai language token. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -235,8 +232,12 @@ pub enum Token { Else, /// `switch` Switch, + /// `do` + Do, /// `while` While, + /// `until` + Until, /// `loop` Loop, /// `for` @@ -383,7 +384,9 @@ impl Token { If => "if", Else => "else", Switch => "switch", + Do => "do", While => "while", + Until => "until", Loop => "loop", For => "for", In => "in", @@ -470,7 +473,9 @@ impl Token { "if" => If, "else" => Else, "switch" => Switch, + "do" => Do, "while" => While, + "until" => Until, "loop" => Loop, "for" => For, "in" => In, @@ -527,8 +532,8 @@ impl Token { "import" | "export" | "as" => Reserved(syntax.into()), "===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new" - | "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "do" - | "each" | "then" | "goto" | "exit" | "match" | "case" | "default" | "void" + | "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "each" + | "then" | "goto" | "unless" | "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => { Reserved(syntax.into()) } @@ -544,7 +549,7 @@ impl Token { }) } - // Is this token EOF? + // Is this token [`EOF`][Token::EOF]? #[inline(always)] pub fn is_eof(&self) -> bool { use Token::*; @@ -556,7 +561,7 @@ impl Token { } // If another operator is after these, it's probably an unary operator - // (not sure about fn name). + // (not sure about `fn` name). pub fn is_next_unary(&self) -> bool { use Token::*; @@ -589,7 +594,9 @@ impl Token { Ampersand | And | If | + Do | While | + Until | PlusAssign | MinusAssign | MultiplyAssign | @@ -693,8 +700,8 @@ impl Token { #[cfg(not(feature = "no_module"))] Import | Export | As => true, - True | False | Let | Const | If | Else | While | Loop | For | In | Continue | Break - | Return | Throw | Try | Catch => true, + True | False | Let | Const | If | Else | Do | While | Until | Loop | For | In + | Continue | Break | Return | Throw | Try | Catch => true, _ => false, } @@ -736,7 +743,7 @@ impl From for String { } } -/// _[INTERNALS]_ State of the tokenizer. +/// _(INTERNALS)_ State of the tokenizer. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -750,13 +757,13 @@ pub struct TokenizeState { pub non_unary: bool, /// Is the tokenizer currently inside a block comment? pub comment_level: usize, - /// Return `None` at the end of the stream instead of `Some(Token::EOF)`? + /// Return [`None`] at the end of the stream instead of [`Some(Token::EOF)`][Token::EOF]? pub end_with_none: bool, /// Include comments? pub include_comments: bool, } -/// _[INTERNALS]_ Trait that encapsulates a peekable character input stream. +/// _(INTERNALS)_ Trait that encapsulates a peekable character input stream. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -770,7 +777,7 @@ pub trait InputStream { fn peek_next(&mut self) -> Option; } -/// _[INTERNALS]_ Parse a string literal wrapped by `enclosing_char`. +/// _(INTERNALS)_ Parse a string literal wrapped by `enclosing_char`. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -959,7 +966,7 @@ fn scan_comment( } } -/// _[INTERNALS]_ Get the next token from the `InputStream`. +/// _(INTERNALS)_ Get the next token from the `stream`. /// Exported under the `internals` feature only. /// /// ## WARNING @@ -1528,6 +1535,7 @@ pub fn can_override_keyword(name: &str) -> bool { } } +/// Is a text string a valid identifier? pub fn is_valid_identifier(name: impl Iterator) -> bool { let mut first_alphabetic = false; @@ -1568,7 +1576,7 @@ fn is_id_continue(x: char) -> bool { x.is_ascii_alphanumeric() || x == '_' } -/// A type that implements the `InputStream` trait. +/// A type that implements the [`InputStream`] trait. /// Multiple character streams are jointed together to form one single stream. pub struct MultiInputsStream<'a> { /// Buffered character, if any. @@ -1625,7 +1633,7 @@ impl InputStream for MultiInputsStream<'_> { } } -/// An iterator on a `Token` stream. +/// An iterator on a [`Token`] stream. pub struct TokenIterator<'a, 'e> { /// Reference to the scripting `Engine`. engine: &'e Engine, diff --git a/src/unsafe.rs b/src/unsafe.rs index eca454fa..2cef9997 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -44,11 +44,11 @@ pub fn unsafe_cast_box(item: Box) -> Result, B /// /// A dangerous function that blindly casts a `&str` from one lifetime to a `&str` of /// another lifetime. This is mainly used to let us push a block-local variable into the -/// current `Scope` without cloning the variable name. Doing this is safe because all local -/// variables in the `Scope` are cleared out before existing the block. +/// current [`Scope`][crate::Scope] without cloning the variable name. Doing this is safe because all local +/// variables in the [`Scope`][crate::Scope] are cleared out before existing the block. /// -/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves -/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s. +/// Force-casting a local variable's lifetime to the current [`Scope`][crate::Scope]'s larger lifetime saves +/// on allocations and string cloning, thus avoids us having to maintain a chain of [`Scope`][crate::Scope]'s. #[inline] pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str { // WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it diff --git a/src/utils.rs b/src/utils.rs index ab9c661d..8513dd6d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -15,11 +15,11 @@ use crate::stdlib::{ }; use crate::Shared; -/// A hasher that only takes one single `u64` and returns it as a hash key. +/// A hasher that only takes one single [`u64`] and returns it as a hash key. /// /// # Panics /// -/// Panics when hashing any data type other than a `u64`. +/// Panics when hashing any data type other than a [`u64`]. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] pub struct StraightHasher(u64); @@ -49,11 +49,11 @@ impl BuildHasher for StraightHasherBuilder { } } -/// _[INTERNALS]_ Calculate a `u64` hash key from a namespace-qualified function name and parameter types. +/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types. /// Exported under the `internals` feature only. /// /// Module names are passed in via `&str` references from an iterator. -/// Parameter types are passed in via `TypeId` values from an iterator. +/// Parameter types are passed in via [`TypeId`] values from an iterator. /// /// # Note /// @@ -67,12 +67,12 @@ pub fn calc_native_fn_hash<'a>( calc_fn_hash(modules, fn_name, None, params) } -/// _[INTERNALS]_ Calculate a `u64` hash key from a namespace-qualified function name +/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name /// and the number of parameters, but no parameter types. /// Exported under the `internals` feature only. /// /// Module names are passed in via `&str` references from an iterator. -/// Parameter types are passed in via `TypeId` values from an iterator. +/// Parameter types are passed in via [`TypeId`] values from an iterator. /// /// # Note /// @@ -96,10 +96,10 @@ pub fn get_hasher() -> impl Hasher { s } -/// Calculate a `u64` hash key from a namespace-qualified function name and parameter types. +/// Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types. /// /// Module names are passed in via `&str` references from an iterator. -/// Parameter types are passed in via `TypeId` values from an iterator. +/// Parameter types are passed in via [`TypeId`] values from an iterator. /// /// # Note /// @@ -116,9 +116,9 @@ fn calc_fn_hash<'a>( modules.next().is_some().hash(s); // We always skip the first module modules.for_each(|m| m.hash(s)); - s.write(fn_name.as_bytes()); + fn_name.hash(s); if let Some(num) = num { - s.write_usize(num); + num.hash(s); } else { params.for_each(|t| t.hash(s)); } @@ -127,7 +127,8 @@ fn calc_fn_hash<'a>( /// The system immutable string type. /// -/// An `ImmutableString` wraps an `Rc` (or `Arc` under the `sync` feature) +/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>` +/// (or [`Arc`][std::sync::Arc]`<`[`String`]`>` under the `sync` feature) /// so that it can be simply shared and not cloned. /// /// # Example @@ -469,15 +470,15 @@ impl PartialOrd for String { } impl ImmutableString { - /// Consume the `ImmutableString` and convert it into a `String`. + /// Consume the [`ImmutableString`] and convert it into a [`String`]. /// If there are other references to the same string, a cloned copy is returned. #[inline(always)] pub fn into_owned(mut self) -> String { self.make_mut(); // Make sure it is unique reference shared_take(self.0) // Should succeed } - /// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references). - /// Then return a mutable reference to the `String`. + /// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references). + /// Then return a mutable reference to the [`String`]. #[inline(always)] pub fn make_mut(&mut self) -> &mut String { shared_make_mut(&mut self.0) diff --git a/tests/eval.rs b/tests/eval.rs index 28618fc5..7732bd25 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -8,7 +8,7 @@ fn test_eval() -> Result<(), Box> { engine.eval::( r#" eval("40 + 2") - "# + "# )?, 42 ); @@ -16,6 +16,33 @@ fn test_eval() -> Result<(), Box> { Ok(()) } +#[test] +fn test_eval_blocks() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + r#" + let x = 999; + + eval("let x = x + 123"); + + let y = if x > 0 { + eval("let x = 42"); + x + } else { + 0 + }; + + x + y + "# + )?, + 1164 + ); + + Ok(()) +} + #[test] #[cfg(not(feature = "no_function"))] fn test_eval_function() -> Result<(), Box> { diff --git a/tests/modules.rs b/tests/modules.rs index de8e4de3..ef148bc9 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -22,9 +22,11 @@ fn test_module_sub_module() -> Result<(), Box> { let mut sub_module2 = Module::new(); sub_module2.set_var("answer", 41 as INT); - let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1)); - let hash_super_inc = sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1)); - sub_module2.set_fn_namespace(hash_super_inc, FnNamespace::Global); + let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1)); + sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1)); + + #[cfg(not(feature = "no_object"))] + sub_module2.set_getter_fn("doubled", |x: &mut INT| Ok(*x * 2)); sub_module.set_sub_module("universe", sub_module2); module.set_sub_module("life", sub_module); @@ -57,6 +59,11 @@ fn test_module_sub_module() -> Result<(), Box> { assert!(engine .eval::("inc(question::life::universe::answer)") .is_err()); + #[cfg(not(feature = "no_object"))] + assert_eq!( + engine.eval::("question::life::universe::answer.doubled")?, + 82 + ); assert_eq!( engine.eval::("super_inc(question::life::universe::answer)")?, 42 @@ -72,17 +79,16 @@ fn test_module_resolver() -> Result<(), Box> { let mut module = Module::new(); module.set_var("answer", 42 as INT); - module.set_fn_4("sum".to_string(), |x: INT, y: INT, z: INT, w: INT| { - Ok(x + y + z + w) - }); - module.set_fn_1_mut("double".to_string(), |x: &mut INT| { + module.set_fn_4("sum", |x: INT, y: INT, z: INT, w: INT| Ok(x + y + z + w)); + module.set_fn_1_mut("double", FnNamespace::Global, |x: &mut INT| { *x *= 2; Ok(()) }); #[cfg(not(feature = "no_float"))] module.set_fn_4_mut( - "sum_of_three_args".to_string(), + "sum_of_three_args", + FnNamespace::Internal, |target: &mut INT, a: INT, b: INT, c: rhai::FLOAT| { *target = a + b + c as INT; Ok(()) @@ -105,6 +111,15 @@ fn test_module_resolver() -> Result<(), Box> { 42 ); + assert!(engine + .eval::( + r#" + import "hello" as h; + sum(h::answer, -10, 3, 7) + "# + ) + .is_err()); + assert_eq!( engine.eval::( r#" @@ -141,6 +156,17 @@ fn test_module_resolver() -> Result<(), Box> { )?, 42 ); + assert_eq!( + engine.eval::( + r#" + import "hello" as h; + let x = 21; + double(x); + x + "# + )?, + 42 + ); #[cfg(not(feature = "no_float"))] { assert_eq!( diff --git a/tests/syntax.rs b/tests/syntax.rs index 70272cef..819931e6 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -1,4 +1,4 @@ -use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseError, ParseErrorType, INT, NO_POS}; +use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseError, ParseErrorType, Position, INT}; #[test] fn test_custom_syntax() -> Result<(), Box> { @@ -52,6 +52,16 @@ fn test_custom_syntax() -> Result<(), Box> { }, )?; + assert_eq!( + engine.eval::( + r" + let x = 0; + exec |x| -> { x += 1 } while x < 42; + x + " + )?, + 42 + ); assert_eq!( engine.eval::( r" @@ -91,13 +101,15 @@ fn test_custom_syntax_raw() -> Result<(), Box> { Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol( s.to_string(), ))), - NO_POS, + Position::NONE, )), }, _ => unreachable!(), }, - 0, - |_, inputs| { + 1, + |context, inputs| { + context.scope.push("foo", 999 as INT); + Ok(match inputs[0].get_variable_name().unwrap() { "world" => 123 as INT, "kitty" => 42 as INT, @@ -109,6 +121,11 @@ fn test_custom_syntax_raw() -> Result<(), Box> { assert_eq!(engine.eval::("hello world")?, 123); assert_eq!(engine.eval::("hello kitty")?, 42); + assert_eq!( + engine.eval::("let foo = 0; (hello kitty) + foo")?, + 1041 + ); + assert_eq!(engine.eval::("(hello kitty) + foo")?, 1041); assert_eq!( *engine.compile("hello hey").expect_err("should error").0, ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string())) diff --git a/tests/var_scope.rs b/tests/var_scope.rs index e6dc825a..52fff87f 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, Scope, INT, NO_POS}; +use rhai::{Engine, EvalAltResult, Position, Scope, INT}; #[test] fn test_var_scope() -> Result<(), Box> { @@ -67,7 +67,7 @@ fn test_var_resolver() -> Result<(), Box> { "MYSTIC_NUMBER" => Ok(Some((42 as INT).into())), // Override a variable - make it not found even if it exists! "DO_NOT_USE" => { - Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into()) + Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE).into()) } // Silently maps 'chameleon' into 'innocent'. "chameleon" => context @@ -75,7 +75,7 @@ fn test_var_resolver() -> Result<(), Box> { .get_value("innocent") .map(Some) .ok_or_else(|| { - EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into() + EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE).into() }), // Return Ok(None) to continue with the normal variable resolution process. _ => Ok(None), diff --git a/tests/while_loop.rs b/tests/while_loop.rs index bbcd091b..f9b74846 100644 --- a/tests/while_loop.rs +++ b/tests/while_loop.rs @@ -24,3 +24,28 @@ fn test_while() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_do() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + r" + let x = 0; + + do { + x += 1; + if x > 5 { break; } + if x > 3 { continue; } + x += 3; + } while x < 10; + + x + ", + )?, + 6 + ); + + Ok(()) +}