From e795a50ae20a00c9b7bfb04db998de4e20c132ac Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 7 Apr 2020 13:23:06 +0800 Subject: [PATCH] Make Engine reentrant to prepare for parallel execution. --- README.md | 19 +- examples/hello.rs | 2 +- examples/no_std.rs | 2 +- examples/reuse_scope.rs | 2 +- src/api.rs | 204 +++++++++---------- src/engine.rs | 384 ++++++++++++++++++------------------ src/lib.rs | 2 +- src/optimize.rs | 37 ++-- src/parser.rs | 2 +- src/result.rs | 2 + src/scope.rs | 2 +- tests/arrays.rs | 2 +- tests/binary_ops.rs | 2 +- tests/bit_shift.rs | 4 +- tests/bool_op.rs | 12 +- tests/call_fn.rs | 2 +- tests/chars.rs | 2 +- tests/comments.rs | 4 +- tests/compound_equality.rs | 16 +- tests/constants.rs | 2 +- tests/decrement.rs | 2 +- tests/eval.rs | 6 +- tests/expressions.rs | 2 +- tests/float.rs | 2 +- tests/for.rs | 4 +- tests/if_block.rs | 4 +- tests/increment.rs | 2 +- tests/internal_fn.rs | 15 +- tests/looping.rs | 2 +- tests/maps.rs | 8 +- tests/math.rs | 2 +- tests/mismatched_op.rs | 2 +- tests/not.rs | 2 +- tests/number_literals.rs | 8 +- tests/ops.rs | 6 +- tests/power_of.rs | 4 +- tests/side_effects.rs | 24 ++- tests/string.rs | 2 +- tests/throw.rs | 2 +- tests/unary_after_binary.rs | 2 +- tests/unary_minus.rs | 2 +- tests/unit.rs | 6 +- tests/var_scope.rs | 4 +- tests/while_loop.rs | 2 +- 44 files changed, 415 insertions(+), 404 deletions(-) diff --git a/README.md b/README.md index 0213d628..4b5c239e 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ use rhai::{Engine, EvalAltResult}; fn main() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let result = engine.eval::("40 + 2")?; @@ -446,7 +446,7 @@ fn get_an_any() -> Dynamic { fn main() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); engine.register_fn("add", add); @@ -497,7 +497,7 @@ fn show_it(x: &mut T) -> () { fn main() { - let mut engine = Engine::new(); + let engine = Engine::new(); engine.register_fn("print", show_it as fn(x: &mut i64)->()); engine.register_fn("print", show_it as fn(x: &mut bool)->()); @@ -533,7 +533,7 @@ fn safe_divide(x: i64, y: i64) -> Result { fn main() { - let mut engine = Engine::new(); + let engine = Engine::new(); // Fallible functions that return Result values must use register_result_fn() engine.register_result_fn("divide", safe_divide); @@ -584,7 +584,7 @@ impl TestStruct { fn main() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); engine.register_type::(); @@ -622,7 +622,7 @@ impl TestStruct { } } -let mut engine = Engine::new(); +let engine = Engine::new(); engine.register_type::(); ``` @@ -712,7 +712,7 @@ impl TestStruct { } } -let mut engine = Engine::new(); +let engine = Engine::new(); engine.register_type::(); @@ -748,7 +748,7 @@ use rhai::{Engine, Scope, EvalAltResult}; fn main() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); // First create the state let mut scope = Scope::new(); @@ -1885,7 +1885,8 @@ print("z = " + z); // <- error: variable 'z' not found Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_, including all variables that are visible at that position in code! It is almost as if the script segments were -physically pasted in at the position of the `eval` call. +physically pasted in at the position of the `eval` call. But because of this, new functions cannot be defined +within an `eval` call, since functions can only be defined at the global level, not inside a function call! ```rust let script = "x += 32"; diff --git a/examples/hello.rs b/examples/hello.rs index 8527d466..c72a38b5 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -1,7 +1,7 @@ use rhai::{Engine, EvalAltResult, INT}; fn main() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let result = engine.eval::("40 + 2")?; diff --git a/examples/no_std.rs b/examples/no_std.rs index 32cf14a2..ad9d9a8a 100644 --- a/examples/no_std.rs +++ b/examples/no_std.rs @@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, INT}; fn main() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let result = engine.eval::("40 + 2")?; diff --git a/examples/reuse_scope.rs b/examples/reuse_scope.rs index 549ccea1..ab936656 100644 --- a/examples/reuse_scope.rs +++ b/examples/reuse_scope.rs @@ -1,7 +1,7 @@ use rhai::{Engine, EvalAltResult, Scope, INT}; fn main() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let mut scope = Scope::new(); engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?; diff --git a/src/api.rs b/src/api.rs index 047cf525..461c97e9 100644 --- a/src/api.rs +++ b/src/api.rs @@ -334,7 +334,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Compile a script to an AST and store it for later evaluation /// let ast = engine.compile("40 + 2")?; @@ -413,7 +413,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Compile a script file to an AST and store it for later evaluation. /// // Notice that a PathBuf is required which can easily be constructed from a string. @@ -481,7 +481,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Compile a script to an AST and store it for later evaluation /// let ast = engine.compile_expression("40 + 2")?; @@ -552,7 +552,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Notice that a PathBuf is required which can easily be constructed from a string. /// let result = engine.eval_file::("script.rhai".into())?; @@ -560,7 +560,7 @@ impl<'e> Engine<'e> { /// # } /// ``` #[cfg(not(feature = "no_std"))] - pub fn eval_file(&mut self, path: PathBuf) -> Result { + pub fn eval_file(&self, path: PathBuf) -> Result { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } @@ -572,7 +572,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::{Engine, Scope}; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Create initialized scope /// let mut scope = Scope::new(); @@ -585,7 +585,7 @@ impl<'e> Engine<'e> { /// ``` #[cfg(not(feature = "no_std"))] pub fn eval_file_with_scope( - &mut self, + &self, scope: &mut Scope, path: PathBuf, ) -> Result { @@ -600,13 +600,13 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// assert_eq!(engine.eval::("40 + 2")?, 42); /// # Ok(()) /// # } /// ``` - pub fn eval(&mut self, input: &str) -> Result { + pub fn eval(&self, input: &str) -> Result { self.eval_with_scope(&mut Scope::new(), input) } @@ -618,7 +618,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::{Engine, Scope}; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Create initialized scope /// let mut scope = Scope::new(); @@ -633,7 +633,7 @@ impl<'e> Engine<'e> { /// # } /// ``` pub fn eval_with_scope( - &mut self, + &self, scope: &mut Scope, input: &str, ) -> Result { @@ -649,13 +649,13 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// assert_eq!(engine.eval_expression::("40 + 2")?, 42); /// # Ok(()) /// # } /// ``` - pub fn eval_expression(&mut self, input: &str) -> Result { + pub fn eval_expression(&self, input: &str) -> Result { self.eval_expression_with_scope(&mut Scope::new(), input) } @@ -667,7 +667,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::{Engine, Scope}; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Create initialized scope /// let mut scope = Scope::new(); @@ -678,7 +678,7 @@ impl<'e> Engine<'e> { /// # } /// ``` pub fn eval_expression_with_scope( - &mut self, + &self, scope: &mut Scope, input: &str, ) -> Result { @@ -697,7 +697,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Compile a script to an AST and store it for later evaluation /// let ast = engine.compile("40 + 2")?; @@ -707,7 +707,7 @@ impl<'e> Engine<'e> { /// # Ok(()) /// # } /// ``` - pub fn eval_ast(&mut self, ast: &AST) -> Result { + pub fn eval_ast(&self, ast: &AST) -> Result { self.eval_ast_with_scope(&mut Scope::new(), ast) } @@ -719,7 +719,7 @@ impl<'e> Engine<'e> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::{Engine, Scope}; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// // Compile a script to an AST and store it for later evaluation /// let ast = engine.compile("x + 2")?; @@ -741,7 +741,7 @@ impl<'e> Engine<'e> { /// # } /// ``` pub fn eval_ast_with_scope( - &mut self, + &self, scope: &mut Scope, ast: &AST, ) -> Result { @@ -756,32 +756,25 @@ impl<'e> Engine<'e> { } pub(crate) fn eval_ast_with_scope_raw( - &mut self, + &self, scope: &mut Scope, ast: &AST, ) -> Result { - let statements = { - let AST(statements, functions) = ast; - self.fn_lib = Some(functions.clone()); - statements - }; - - let result = statements + ast.0 .iter() - .try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0)); - - self.fn_lib = None; - - result.or_else(|err| match err { - EvalAltResult::Return(out, _) => Ok(out), - _ => Err(err), - }) + .try_fold(().into_dynamic(), |_, stmt| { + self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0) + }) + .or_else(|err| match err { + EvalAltResult::Return(out, _) => Ok(out), + _ => Err(err), + }) } /// Evaluate a file, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[cfg(not(feature = "no_std"))] - pub fn consume_file(&mut self, path: PathBuf) -> Result<(), EvalAltResult> { + pub fn consume_file(&self, path: PathBuf) -> Result<(), EvalAltResult> { Self::read_file(path).and_then(|contents| self.consume(&contents)) } @@ -789,7 +782,7 @@ impl<'e> Engine<'e> { /// Useful for when you don't need the result, but still need to keep track of possible errors. #[cfg(not(feature = "no_std"))] pub fn consume_file_with_scope( - &mut self, + &self, scope: &mut Scope, path: PathBuf, ) -> Result<(), EvalAltResult> { @@ -798,17 +791,13 @@ impl<'e> Engine<'e> { /// Evaluate a string, 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. - pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> { + pub fn consume(&self, input: &str) -> Result<(), EvalAltResult> { self.consume_with_scope(&mut Scope::new(), input) } /// Evaluate a string 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. - pub fn consume_with_scope( - &mut self, - scope: &mut Scope, - input: &str, - ) -> Result<(), EvalAltResult> { + pub fn consume_with_scope(&self, scope: &mut Scope, input: &str) -> Result<(), EvalAltResult> { let tokens_stream = lex(input); let ast = parse(&mut tokens_stream.peekable(), self, scope) @@ -819,33 +808,27 @@ impl<'e> Engine<'e> { /// Evaluate an AST, 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. - pub fn consume_ast(&mut self, ast: &AST) -> Result<(), EvalAltResult> { + pub fn consume_ast(&self, ast: &AST) -> Result<(), EvalAltResult> { self.consume_ast_with_scope(&mut Scope::new(), ast) } /// 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. pub fn consume_ast_with_scope( - &mut self, + &self, scope: &mut Scope, ast: &AST, ) -> Result<(), EvalAltResult> { - let statements = { - let AST(statements, functions) = ast; - self.fn_lib = Some(functions.clone()); - statements - }; - - let result = statements + ast.0 .iter() - .try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0)); - - self.fn_lib = None; - - result.map(|_| ()).or_else(|err| match err { - EvalAltResult::Return(_, _) => Ok(()), - _ => Err(err), - }) + .try_fold(().into_dynamic(), |_, stmt| { + self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0) + }) + .map(|_| ()) + .or_else(|err| match err { + EvalAltResult::Return(_, _) => Ok(()), + _ => Err(err), + }) } /// Call a script function defined in an `AST` with no argument. @@ -859,7 +842,7 @@ impl<'e> Engine<'e> { /// # { /// use rhai::{Engine, Scope}; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// let ast = engine.compile("fn num() { 42 + foo }")?; /// @@ -876,7 +859,7 @@ impl<'e> Engine<'e> { /// ``` #[cfg(not(feature = "no_function"))] pub fn call_fn0( - &mut self, + &self, scope: &mut Scope, ast: &AST, name: &str, @@ -895,7 +878,7 @@ impl<'e> Engine<'e> { /// # { /// use rhai::{Engine, Scope}; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// let ast = engine.compile("fn inc(x) { x + foo }")?; /// @@ -912,7 +895,7 @@ impl<'e> Engine<'e> { /// ``` #[cfg(not(feature = "no_function"))] pub fn call_fn1( - &mut self, + &self, scope: &mut Scope, ast: &AST, name: &str, @@ -932,7 +915,7 @@ impl<'e> Engine<'e> { /// # { /// use rhai::{Engine, Scope}; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// let ast = engine.compile("fn add(x, y) { len(x) + y + foo }")?; /// @@ -949,7 +932,7 @@ impl<'e> Engine<'e> { /// ``` #[cfg(not(feature = "no_function"))] pub fn call_fn( - &mut self, + &self, scope: &mut Scope, ast: &AST, name: &str, @@ -960,29 +943,24 @@ impl<'e> Engine<'e> { #[cfg(not(feature = "no_function"))] fn call_fn_internal( - &mut self, + &self, scope: &mut Scope, ast: &AST, name: &str, mut arg_values: Vec, ) -> Result { let mut args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect(); + let fn_lib = Some(ast.1.as_ref()); + let pos = Position::none(); - self.fn_lib = Some(ast.1.clone()); - - let result = self - .call_fn_raw(Some(scope), name, &mut args, None, Position::none(), 0)? + self.call_fn_raw(Some(scope), fn_lib, name, &mut args, None, pos, 0)? .try_cast() .map_err(|a| { EvalAltResult::ErrorMismatchOutputType( self.map_type_name((*a).type_name()).into(), - Position::none(), + pos, ) - }); - - self.fn_lib = None; - - result + }) } /// Optimize the `AST` with constants defined in an external Scope. @@ -998,12 +976,8 @@ impl<'e> Engine<'e> { /// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running. #[cfg(not(feature = "no_optimize"))] pub fn optimize_ast(&self, scope: &Scope, ast: AST) -> AST { - optimize_into_ast( - self, - scope, - ast.0, - ast.1.iter().map(|fn_def| fn_def.as_ref().clone()).collect(), - ) + let fn_lib = ast.1.iter().map(|fn_def| fn_def.as_ref().clone()).collect(); + optimize_into_ast(self, scope, ast.0, fn_lib) } /// Override default action of `print` (print to stdout using `println!`) @@ -1012,22 +986,24 @@ impl<'e> Engine<'e> { /// /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { + /// # use std::sync::RwLock; /// use rhai::Engine; /// - /// let mut result = String::from(""); + /// let result = RwLock::new(String::from("")); /// { - /// let mut engine = Engine::new(); + /// let mut engine = Engine::new(); /// - /// // Override action of 'print' function - /// engine.on_print(|s| result.push_str(s)); - /// engine.consume("print(40 + 2);")?; + /// // Override action of 'print' function + /// engine.on_print(|s| result.write().unwrap().push_str(s)); + /// + /// engine.consume("print(40 + 2);")?; /// } - /// assert_eq!(result, "42"); + /// assert_eq!(*result.read().unwrap(), "42"); /// # Ok(()) /// # } /// ``` #[cfg(feature = "sync")] - pub fn on_print(&mut self, callback: impl FnMut(&str) + Send + Sync + 'e) { + pub fn on_print(&mut self, callback: impl Fn(&str) + Send + Sync + 'e) { self.on_print = Some(Box::new(callback)); } /// Override default action of `print` (print to stdout using `println!`) @@ -1036,22 +1012,24 @@ impl<'e> Engine<'e> { /// /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { + /// # use std::sync::RwLock; /// use rhai::Engine; /// - /// let mut result = String::from(""); + /// let result = RwLock::new(String::from("")); /// { - /// let mut engine = Engine::new(); + /// let mut engine = Engine::new(); /// - /// // Override action of 'print' function - /// engine.on_print(|s| result.push_str(s)); - /// engine.consume("print(40 + 2);")?; + /// // Override action of 'print' function + /// engine.on_print(|s| result.write().unwrap().push_str(s)); + /// + /// engine.consume("print(40 + 2);")?; /// } - /// assert_eq!(result, "42"); + /// assert_eq!(*result.read().unwrap(), "42"); /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "sync"))] - pub fn on_print(&mut self, callback: impl FnMut(&str) + 'e) { + pub fn on_print(&mut self, callback: impl Fn(&str) + 'e) { self.on_print = Some(Box::new(callback)); } @@ -1061,22 +1039,24 @@ impl<'e> Engine<'e> { /// /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { + /// # use std::sync::RwLock; /// use rhai::Engine; /// - /// let mut result = String::from(""); + /// let result = RwLock::new(String::from("")); /// { - /// let mut engine = Engine::new(); + /// let mut engine = Engine::new(); /// - /// // Override action of 'debug' function - /// engine.on_debug(|s| result.push_str(s)); - /// engine.consume(r#"debug("hello");"#)?; + /// // Override action of 'print' function + /// engine.on_debug(|s| result.write().unwrap().push_str(s)); + /// + /// engine.consume(r#"debug("hello");"#)?; /// } - /// assert_eq!(result, "\"hello\""); + /// assert_eq!(*result.read().unwrap(), r#""hello""#); /// # Ok(()) /// # } /// ``` #[cfg(feature = "sync")] - pub fn on_debug(&mut self, callback: impl FnMut(&str) + Send + Sync + 'e) { + pub fn on_debug(&mut self, callback: impl Fn(&str) + Send + Sync + 'e) { self.on_debug = Some(Box::new(callback)); } /// Override default action of `debug` (print to stdout using `println!`) @@ -1085,22 +1065,24 @@ impl<'e> Engine<'e> { /// /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { + /// # use std::sync::RwLock; /// use rhai::Engine; /// - /// let mut result = String::from(""); + /// let result = RwLock::new(String::from("")); /// { - /// let mut engine = Engine::new(); + /// let mut engine = Engine::new(); /// - /// // Override action of 'debug' function - /// engine.on_debug(|s| result.push_str(s)); - /// engine.consume(r#"debug("hello");"#)?; + /// // Override action of 'print' function + /// engine.on_debug(|s| result.write().unwrap().push_str(s)); + /// + /// engine.consume(r#"debug("hello");"#)?; /// } - /// assert_eq!(result, "\"hello\""); + /// assert_eq!(*result.read().unwrap(), r#""hello""#); /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "sync"))] - pub fn on_debug(&mut self, callback: impl FnMut(&str) + 'e) { + pub fn on_debug(&mut self, callback: impl Fn(&str) + 'e) { self.on_debug = Some(Box::new(callback)); } } diff --git a/src/engine.rs b/src/engine.rs index e8c24034..e0ce5c43 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,8 @@ //! Main module defining the script evaluation `Engine`. use crate::any::{Any, AnyExt, Dynamic, Variant}; -use crate::parser::{Expr, FnDef, Position, ReturnType, Stmt, AST, INT}; +use crate::error::ParseErrorType; +use crate::parser::{Expr, FnDef, Position, ReturnType, Stmt, INT}; use crate::result::EvalAltResult; use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope}; @@ -232,7 +233,7 @@ impl DerefMut for FunctionsLib { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::Engine; /// -/// let mut engine = Engine::new(); +/// let engine = Engine::new(); /// /// let result = engine.eval::("40 + 2")?; /// @@ -246,13 +247,6 @@ pub struct Engine<'e> { /// A hashmap containing all compiled functions known to the engine. pub(crate) functions: Option, Box>>, - /// A hashmap containing all script-defined functions. - #[cfg(feature = "sync")] - pub(crate) fn_lib: Option>, - /// A hashmap containing all script-defined functions. - #[cfg(not(feature = "sync"))] - pub(crate) fn_lib: Option>, - /// A hashmap containing all iterators known to the engine. pub(crate) type_iterators: Option>>, /// A hashmap mapping type names to pretty-print names. @@ -260,17 +254,17 @@ pub struct Engine<'e> { /// Closure for implementing the `print` command. #[cfg(feature = "sync")] - pub(crate) on_print: Option>, + pub(crate) on_print: Option>, /// Closure for implementing the `print` command. #[cfg(not(feature = "sync"))] - pub(crate) on_print: Option>, + pub(crate) on_print: Option>, /// Closure for implementing the `debug` command. #[cfg(feature = "sync")] - pub(crate) on_debug: Option>, + pub(crate) on_debug: Option>, /// Closure for implementing the `debug` command. #[cfg(not(feature = "sync"))] - pub(crate) on_debug: Option>, + pub(crate) on_debug: Option>, /// Optimize the AST after compilation. #[cfg(not(feature = "no_optimize"))] @@ -299,7 +293,6 @@ impl Default for Engine<'_> { // Create the new scripting Engine let mut engine = Engine { functions: None, - fn_lib: None, type_iterators: None, type_names: Some(type_names), on_print: Some(Box::new(default_print)), // default print/debug implementations @@ -368,7 +361,6 @@ impl Engine<'_> { pub fn new_raw() -> Self { let mut engine = Engine { functions: None, - fn_lib: None, type_iterators: None, type_names: None, on_print: None, @@ -435,8 +427,9 @@ impl Engine<'_> { /// Universal method for calling functions either registered with the `Engine` or written in Rhai pub(crate) fn call_fn_raw( - &mut self, + &self, scope: Option<&mut Scope>, + fn_lib: Option<&FunctionsLib>, fn_name: &str, args: &mut FnCallArgs, def_val: Option<&Dynamic>, @@ -444,8 +437,8 @@ impl Engine<'_> { level: usize, ) -> Result { // First search in script-defined functions (can override built-in) - if let Some(fn_lib_arc) = &self.fn_lib { - if let Some(fn_def) = fn_lib_arc.clone().get_function(fn_name, args.len()) { + if let Some(lib) = fn_lib { + if let Some(fn_def) = lib.get_function(fn_name, args.len()) { match scope { // Extern scope passed in which is not empty Some(scope) if scope.len() > 0 => { @@ -462,13 +455,13 @@ impl Engine<'_> { ); // Evaluate the function at one higher level of call depth - let result = self.eval_stmt(scope, &fn_def.body, level + 1).or_else( - |err| match err { + let result = self + .eval_stmt(scope, fn_lib, &fn_def.body, level + 1) + .or_else(|err| match err { // Convert return statement to return value EvalAltResult::Return(x, _) => Ok(x), err => Err(err.set_position(pos)), - }, - ); + }); scope.rewind(scope_len); @@ -488,13 +481,13 @@ impl Engine<'_> { ); // Evaluate the function at one higher level of call depth - return self.eval_stmt(&mut scope, &fn_def.body, level + 1).or_else( - |err| match err { + return self + .eval_stmt(&mut scope, fn_lib, &fn_def.body, level + 1) + .or_else(|err| match err { // Convert return statement to return value EvalAltResult::Return(x, _) => Ok(x), err => Err(err.set_position(pos)), - }, - ); + }); } } } @@ -521,11 +514,11 @@ impl Engine<'_> { // See if the function match print/debug (which requires special processing) return Ok(match fn_name { KEYWORD_PRINT if self.on_print.is_some() => { - self.on_print.as_deref_mut().unwrap()(cast_to_string(result.as_ref(), pos)?) + self.on_print.as_ref().unwrap()(cast_to_string(result.as_ref(), pos)?) .into_dynamic() } KEYWORD_DEBUG if self.on_debug.is_some() => { - self.on_debug.as_deref_mut().unwrap()(cast_to_string(result.as_ref(), pos)?) + self.on_debug.as_ref().unwrap()(cast_to_string(result.as_ref(), pos)?) .into_dynamic() } KEYWORD_PRINT | KEYWORD_DEBUG => ().into_dynamic(), @@ -590,8 +583,9 @@ impl Engine<'_> { /// Chain-evaluate a dot setter. #[cfg(not(feature = "no_object"))] fn get_dot_val_helper( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, target: Target, dot_rhs: &Expr, level: usize, @@ -601,7 +595,7 @@ impl Engine<'_> { Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => { let mut values = arg_expr_list .iter() - .map(|arg_expr| self.eval_expr(scope, arg_expr, level)) + .map(|arg_expr| self.eval_expr(scope, fn_lib, arg_expr, level)) .collect::, _>>()?; let this_ptr = target.get_mut(scope); @@ -612,13 +606,13 @@ impl Engine<'_> { let def_val = def_val.as_ref(); - self.call_fn_raw(None, fn_name, &mut args, def_val, *pos, 0) + self.call_fn_raw(None, fn_lib, fn_name, &mut args, def_val, *pos, 0) } // xxx.id Expr::Property(id, pos) => { let mut args = [target.get_mut(scope)]; - self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, fn_lib, &make_getter(id), &mut args, None, *pos, 0) } // xxx.idx_lhs[idx_expr] @@ -628,11 +622,11 @@ impl Engine<'_> { // xxx.id[idx_expr] Expr::Property(id, pos) => { let mut args = [target.get_mut(scope)]; - self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0)? + self.call_fn_raw(None, fn_lib, &make_getter(id), &mut args, None, *pos, 0)? } // xxx.???[???][idx_expr] Expr::Index(_, _, _) => { - self.get_dot_val_helper(scope, target, idx_lhs, level)? + self.get_dot_val_helper(scope, fn_lib, target, idx_lhs, level)? } // Syntax error _ => { @@ -643,7 +637,7 @@ impl Engine<'_> { } }; - self.get_indexed_value(scope, &value, idx_expr, *op_pos, level) + self.get_indexed_value(scope, fn_lib, &value, idx_expr, *op_pos, level) .map(|(val, _, _)| val) } @@ -652,9 +646,10 @@ impl Engine<'_> { // xxx.id.rhs Expr::Property(id, pos) => { let mut args = [target.get_mut(scope)]; - self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, fn_lib, &make_getter(id), &mut args, None, *pos, 0) .and_then(|mut val| { - self.get_dot_val_helper(scope, Target::from(val.as_mut()), rhs, level) + let target = Target::from(val.as_mut()); + self.get_dot_val_helper(scope, fn_lib, target, rhs, level) }) } // xxx.idx_lhs[idx_expr].rhs @@ -663,12 +658,13 @@ impl Engine<'_> { let val = match idx_lhs.as_ref() { // xxx.id[idx_expr].rhs Expr::Property(id, pos) => { + let fn_name = make_getter(id); let mut args = [target.get_mut(scope)]; - self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0)? + self.call_fn_raw(None, fn_lib, &fn_name, &mut args, None, *pos, 0)? } // xxx.???[???][idx_expr].rhs Expr::Index(_, _, _) => { - self.get_dot_val_helper(scope, target, idx_lhs, level)? + self.get_dot_val_helper(scope, fn_lib, target, idx_lhs, level)? } // Syntax error _ => { @@ -679,9 +675,10 @@ impl Engine<'_> { } }; - self.get_indexed_value(scope, &val, idx_expr, *op_pos, level) + self.get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level) .and_then(|(mut val, _, _)| { - self.get_dot_val_helper(scope, Target::from(val.as_mut()), rhs, level) + let target = Target::from(val.as_mut()); + self.get_dot_val_helper(scope, fn_lib, target, rhs, level) }) } // Syntax error @@ -702,8 +699,9 @@ impl Engine<'_> { /// Evaluate a dot chain getter #[cfg(not(feature = "no_object"))] fn get_dot_val( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, dot_lhs: &Expr, dot_rhs: &Expr, level: usize, @@ -718,16 +716,16 @@ impl Engine<'_> { // This is a variable property access (potential function call). // Use a direct index into `scope` to directly mutate the variable value. - self.get_dot_val_helper(scope, Target::from_src(entry), dot_rhs, level) + self.get_dot_val_helper(scope, fn_lib, Target::from_src(entry), dot_rhs, level) } // idx_lhs[idx_expr].??? #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, idx_expr, op_pos) => { let (idx_src_type, src, idx, mut val) = - self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?; - let value = - self.get_dot_val_helper(scope, Target::from(val.as_mut()), dot_rhs, level); + self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?; + let target = Target::from(val.as_mut()); + let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level); // In case the expression mutated `target`, we need to update it back into the scope because it is cloned. if let Some(src) = src { @@ -755,8 +753,8 @@ impl Engine<'_> { // {expr}.??? expr => { - let mut val = self.eval_expr(scope, expr, level)?; - self.get_dot_val_helper(scope, Target::from(val.as_mut()), dot_rhs, level) + let mut val = self.eval_expr(scope, fn_lib, expr, level)?; + self.get_dot_val_helper(scope, fn_lib, Target::from(val.as_mut()), dot_rhs, level) } } } @@ -775,8 +773,9 @@ impl Engine<'_> { /// Get the value at the indexed position of a base type #[cfg(not(feature = "no_index"))] fn get_indexed_value( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, val: &Dynamic, idx_expr: &Expr, op_pos: Position, @@ -787,7 +786,7 @@ impl Engine<'_> { // val_array[idx] if let Some(arr) = val.downcast_ref::() { let idx = self - .eval_expr(scope, idx_expr, level)? + .eval_expr(scope, fn_lib, idx_expr, level)? .try_cast::() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; @@ -806,7 +805,7 @@ impl Engine<'_> { // val_map[idx] if let Some(map) = val.downcast_ref::() { let idx = self - .eval_expr(scope, idx_expr, level)? + .eval_expr(scope, fn_lib, idx_expr, level)? .try_cast::() .map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?; @@ -821,7 +820,7 @@ impl Engine<'_> { // val_string[idx] if let Some(s) = val.downcast_ref::() { let idx = self - .eval_expr(scope, idx_expr, level)? + .eval_expr(scope, fn_lib, idx_expr, level)? .try_cast::() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; @@ -857,8 +856,9 @@ impl Engine<'_> { /// Evaluate an index expression #[cfg(not(feature = "no_index"))] fn eval_index_expr<'a>( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, lhs: &'a Expr, idx_expr: &Expr, op_pos: Position, @@ -885,7 +885,7 @@ impl Engine<'_> { ) = Self::search_scope(scope, &id, lhs.position())?; let (val, idx_src_type, idx) = - self.get_indexed_value(scope, &val, idx_expr, op_pos, level)?; + self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)?; Ok(( idx_src_type, @@ -901,9 +901,9 @@ impl Engine<'_> { // (expr)[idx_expr] expr => { - let val = self.eval_expr(scope, expr, level)?; + let val = self.eval_expr(scope, fn_lib, expr, level)?; - self.get_indexed_value(scope, &val, idx_expr, op_pos, level) + self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level) .map(|(val, _, idx)| (IndexSourceType::Expression, None, idx, val)) } } @@ -1002,8 +1002,9 @@ impl Engine<'_> { /// Chain-evaluate a dot setter #[cfg(not(feature = "no_object"))] fn set_dot_val_helper( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, this_ptr: &mut Variant, dot_rhs: &Expr, new_val: (&mut Dynamic, Position), @@ -1013,7 +1014,7 @@ impl Engine<'_> { // xxx.id Expr::Property(id, pos) => { let mut args = [this_ptr, new_val.0.as_mut()]; - self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, fn_lib, &make_setter(id), &mut args, None, *pos, 0) } // xxx.lhs[idx_expr] @@ -1021,18 +1022,21 @@ impl Engine<'_> { #[cfg(not(feature = "no_index"))] Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() { // xxx.id[idx_expr] - Expr::Property(id, pos) => self - .call_fn_raw(None, &make_getter(id), &mut [this_ptr], None, *pos, 0) - .and_then(|val| { - let (_, _, idx) = - self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)?; + Expr::Property(id, pos) => { + let fn_name = make_getter(id); + self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0) + .and_then(|val| { + let (_, _, idx) = self + .get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level)?; - Self::update_indexed_value(val, idx, new_val.0.clone(), new_val.1) - }) - .and_then(|mut val| { - let mut args = [this_ptr, val.as_mut()]; - self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) - }), + Self::update_indexed_value(val, idx, new_val.0.clone(), new_val.1) + }) + .and_then(|mut val| { + let fn_name = make_setter(id); + let mut args = [this_ptr, val.as_mut()]; + self.call_fn_raw(None, fn_lib, &fn_name, &mut args, None, *pos, 0) + }) + } // All others - syntax error for setters chain _ => Err(EvalAltResult::ErrorDotExpr( @@ -1045,14 +1049,17 @@ impl Engine<'_> { Expr::Dot(lhs, rhs, _) => match lhs.as_ref() { // xxx.id.rhs Expr::Property(id, pos) => { - self.call_fn_raw(None, &make_getter(id), &mut [this_ptr], None, *pos, 0) + let fn_name = make_getter(id); + self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0) .and_then(|mut val| { - self.set_dot_val_helper(scope, val.as_mut(), rhs, new_val, level) + let value = val.as_mut(); + self.set_dot_val_helper(scope, fn_lib, value, rhs, new_val, level) .map(|_| val) // Discard Ok return value }) .and_then(|mut val| { + let fn_name = make_setter(id); let mut args = [this_ptr, val.as_mut()]; - self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, fn_lib, &fn_name, &mut args, None, *pos, 0) }) } @@ -1062,21 +1069,26 @@ impl Engine<'_> { Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() { // xxx.id[idx_expr].rhs Expr::Property(id, pos) => { - self.call_fn_raw(None, &make_getter(id), &mut [this_ptr], None, *pos, 0) + let fn_name = make_getter(id); + self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0) .and_then(|v| { - let (mut value, _, idx) = - self.get_indexed_value(scope, &v, idx_expr, *op_pos, level)?; + let (mut value, _, idx) = self.get_indexed_value( + scope, fn_lib, &v, idx_expr, *op_pos, level, + )?; let val_pos = new_val.1; let this_ptr = value.as_mut(); - self.set_dot_val_helper(scope, this_ptr, rhs, new_val, level)?; + self.set_dot_val_helper( + scope, fn_lib, this_ptr, rhs, new_val, level, + )?; // In case the expression mutated `target`, we need to update it back into the scope because it is cloned. Self::update_indexed_value(v, idx, value, val_pos) }) .and_then(|mut v| { + let fn_name = make_setter(id); let mut args = [this_ptr, v.as_mut()]; - self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, fn_lib, &fn_name, &mut args, None, *pos, 0) }) } @@ -1105,8 +1117,9 @@ impl Engine<'_> { // Evaluate a dot chain setter #[cfg(not(feature = "no_object"))] fn set_dot_val( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, dot_lhs: &Expr, dot_rhs: &Expr, new_val: (&mut Dynamic, Position), @@ -1127,8 +1140,8 @@ impl Engine<'_> { // Avoid referencing scope which is used below as mut let entry = ScopeSource { name: id, ..entry }; let this_ptr = target.as_mut(); - let value = - self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level); + let value = self + .set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level); // In case the expression mutated `target`, we need to update it back into the scope because it is cloned. *scope.get_mut(entry) = target; @@ -1143,10 +1156,11 @@ impl Engine<'_> { #[cfg(not(feature = "no_index"))] Expr::Index(lhs, idx_expr, op_pos) => { let (idx_src_type, src, idx, mut target) = - self.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)?; + self.eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level)?; let val_pos = new_val.1; let this_ptr = target.as_mut(); - let value = self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level); + let value = + self.set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level); // In case the expression mutated `target`, we need to update it back into the scope because it is cloned. if let Some(src) = src { @@ -1182,14 +1196,15 @@ impl Engine<'_> { // Evaluate an 'in' expression fn eval_in_expr( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, lhs: &Expr, rhs: &Expr, level: usize, ) -> Result { - let mut lhs_value = self.eval_expr(scope, lhs, level)?; - let rhs_value = self.eval_expr(scope, rhs, level)?; + let mut lhs_value = self.eval_expr(scope, fn_lib, lhs, level)?; + let rhs_value = self.eval_expr(scope, fn_lib, rhs, level)?; #[cfg(not(feature = "no_index"))] { @@ -1200,15 +1215,10 @@ impl Engine<'_> { // Call the '==' operator to compare each value for value in rhs_value.iter_mut() { + let args = &mut [lhs_value.as_mut(), value.as_mut()]; + let def_value = Some(&def_value); if self - .call_fn_raw( - None, - "==", - &mut [lhs_value.as_mut(), value.as_mut()], - Some(&def_value), - rhs.position(), - level, - )? + .call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)? .try_cast::() .unwrap_or(false) { @@ -1261,8 +1271,9 @@ impl Engine<'_> { /// Evaluate an expression fn eval_expr( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, expr: &Expr, level: usize, ) -> Result { @@ -1279,15 +1290,15 @@ impl Engine<'_> { // lhs[idx_expr] #[cfg(not(feature = "no_index"))] Expr::Index(lhs, idx_expr, op_pos) => self - .eval_index_expr(scope, lhs, idx_expr, *op_pos, level) + .eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level) .map(|(_, _, _, x)| x), // Statement block - Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt, level), + Expr::Stmt(stmt, _) => self.eval_stmt(scope, fn_lib, stmt, level), // lhs = rhs Expr::Assignment(lhs, rhs, op_pos) => { - let mut rhs_val = self.eval_expr(scope, rhs, level)?; + let mut rhs_val = self.eval_expr(scope, fn_lib, rhs, level)?; match lhs.as_ref() { // name = rhs @@ -1324,7 +1335,7 @@ impl Engine<'_> { #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, idx_expr, op_pos) => { let (idx_src_type, src, idx, _) = - self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?; + self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?; if let Some(src) = src { match src.typ { @@ -1351,14 +1362,10 @@ impl Engine<'_> { // dot_lhs.dot_rhs = rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(dot_lhs, dot_rhs, _) => self.set_dot_val( - scope, - dot_lhs, - dot_rhs, - (&mut rhs_val, rhs.position()), - *op_pos, - level, - ), + Expr::Dot(dot_lhs, dot_rhs, _) => { + let new_val = (&mut rhs_val, rhs.position()); + self.set_dot_val(scope, fn_lib, dot_lhs, dot_rhs, new_val, *op_pos, level) + } // Error assignment to constant expr if expr.is_constant() => Err(EvalAltResult::ErrorAssignmentToConstant( @@ -1372,14 +1379,15 @@ impl Engine<'_> { } #[cfg(not(feature = "no_object"))] - Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs, level), + Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, fn_lib, lhs, rhs, level), #[cfg(not(feature = "no_index"))] Expr::Array(contents, _) => { let mut arr = Array::new(); contents.into_iter().try_for_each(|item| { - self.eval_expr(scope, item, level).map(|val| arr.push(val)) + self.eval_expr(scope, fn_lib, item, level) + .map(|val| arr.push(val)) })?; Ok((arr).into_dynamic()) @@ -1390,7 +1398,7 @@ impl Engine<'_> { let mut map = Map::new(); contents.into_iter().try_for_each(|item| { - self.eval_expr(scope, &item.1, level).map(|val| { + self.eval_expr(scope, fn_lib, &item.1, level).map(|val| { map.insert(item.0.clone(), val); }) })?; @@ -1400,14 +1408,17 @@ impl Engine<'_> { Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => { // Has a system function an override? - fn has_override(engine: &Engine, name: &str) -> bool { - (engine.functions.is_some() && { - engine.functions.as_ref().unwrap().contains_key(&FnSpec { + fn has_override( + engine: &Engine, + fn_lib: Option<&FunctionsLib>, + name: &str, + ) -> bool { + engine.functions.as_ref().map_or(false, |lib| { + lib.contains_key(&FnSpec { name: name.into(), args: vec![TypeId::of::()], }) - }) || (engine.fn_lib.is_some() - && engine.fn_lib.as_ref().unwrap().has_function(name, 1)) + }) || fn_lib.map_or(false, |lib| lib.has_function(name, 1)) } match fn_name.as_ref() { @@ -1429,14 +1440,15 @@ impl Engine<'_> { // Redirect call to `print` let mut args = [result.as_mut()]; - self.call_fn_raw(None, KEYWORD_PRINT, &mut args, None, pos, level) + self.call_fn_raw(None, fn_lib, KEYWORD_PRINT, &mut args, None, pos, level) } // type_of KEYWORD_TYPE_OF - if args_expr_list.len() == 1 && !has_override(self, KEYWORD_TYPE_OF) => + if args_expr_list.len() == 1 + && !has_override(self, fn_lib, KEYWORD_TYPE_OF) => { - let r = self.eval_expr(scope, &args_expr_list[0], level)?; + let r = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; Ok(self .map_type_name((*r).type_name()) .to_string() @@ -1445,10 +1457,11 @@ impl Engine<'_> { // eval KEYWORD_EVAL - if args_expr_list.len() == 1 && !has_override(self, KEYWORD_EVAL) => + if args_expr_list.len() == 1 + && !has_override(self, fn_lib, KEYWORD_EVAL) => { let pos = args_expr_list[0].position(); - let r = self.eval_expr(scope, &args_expr_list[0], level)?; + let r = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; // Get the script text by evaluating the expression let script = @@ -1462,81 +1475,59 @@ impl Engine<'_> { })?; // Compile the script text - #[cfg(not(feature = "no_optimize"))] - let ast = { - let orig_optimization_level = self.optimization_level; + let mut ast = self.compile(script).map_err(EvalAltResult::ErrorParsing)?; - self.set_optimization_level(OptimizationLevel::None); - let ast = self.compile(script); - self.set_optimization_level(orig_optimization_level); + // If new functions are defined within the eval string, it is an error + if ast.1.len() > 0 { + return Err(EvalAltResult::ErrorParsing( + ParseErrorType::WrongFnDefinition.into_err(pos), + )); + } - ast.map_err(EvalAltResult::ErrorParsing)? - }; - - #[cfg(feature = "no_optimize")] - let ast = self.compile(script).map_err(EvalAltResult::ErrorParsing)?; - - // If new functions are defined, merge it into the current functions library - let merged = AST( - ast.0, - if let Some(fn_lib) = &self.fn_lib { - #[cfg(feature = "sync")] - { - Arc::new(fn_lib.as_ref().merge(&ast.1)) - } - #[cfg(not(feature = "sync"))] - { - Rc::new(fn_lib.as_ref().merge(&ast.1)) - } - } else { - ast.1 - }, - ); + if let Some(lib) = fn_lib { + #[cfg(feature = "sync")] + { + ast.1 = Arc::new(lib.clone()); + } + #[cfg(not(feature = "sync"))] + { + ast.1 = Rc::new(lib.clone()); + } + } // Evaluate the AST - let result = self - .eval_ast_with_scope_raw(scope, &merged) - .map_err(|err| err.set_position(pos)); - - // Update the new functions library if there are new functions - self.fn_lib = if !merged.1.is_empty() { - Some(merged.1) - } else { - None - }; - - Ok(result?) + self.eval_ast_with_scope_raw(scope, &ast) + .map_err(|err| err.set_position(pos)) } // Normal function call _ => { - let mut values = args_expr_list + let mut arg_values = args_expr_list .iter() - .map(|expr| self.eval_expr(scope, expr, level)) + .map(|expr| self.eval_expr(scope, fn_lib, expr, level)) .collect::, _>>()?; - let mut arg_values: Vec<_> = - values.iter_mut().map(Dynamic::as_mut).collect(); - + let mut args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect(); let def_val = def_val.as_ref(); - - self.call_fn_raw(None, fn_name, &mut arg_values, def_val, *pos, level) + self.call_fn_raw(None, fn_lib, fn_name, &mut args, def_val, *pos, level) } } } - Expr::In(lhs, rhs, _) => self.eval_in_expr(scope, lhs.as_ref(), rhs.as_ref(), level), + Expr::In(lhs, rhs, _) => { + self.eval_in_expr(scope, fn_lib, lhs.as_ref(), rhs.as_ref(), level) + } Expr::And(lhs, rhs, _) => Ok(Box::new( self - .eval_expr(scope, lhs.as_ref(), level)? + .eval_expr(scope, fn_lib,lhs.as_ref(), level)? .try_cast::() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position()) })? && // Short-circuit using && self - .eval_expr(scope, rhs.as_ref(), level)? + .eval_expr(scope, fn_lib,rhs.as_ref(), level)? .try_cast::() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position()) @@ -1545,14 +1536,14 @@ impl Engine<'_> { Expr::Or(lhs, rhs, _) => Ok(Box::new( self - .eval_expr(scope, lhs.as_ref(), level)? + .eval_expr(scope,fn_lib, lhs.as_ref(), level)? .try_cast::() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position()) })? || // Short-circuit using || self - .eval_expr(scope, rhs.as_ref(), level)? + .eval_expr(scope,fn_lib, rhs.as_ref(), level)? .try_cast::() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) @@ -1567,8 +1558,9 @@ impl Engine<'_> { /// Evaluate a statement pub(crate) fn eval_stmt( - &mut self, + &self, scope: &mut Scope, + fn_lib: Option<&FunctionsLib>, stmt: &Stmt, level: usize, ) -> Result { @@ -1578,7 +1570,7 @@ impl Engine<'_> { // Expression as statement Stmt::Expr(expr) => { - let result = self.eval_expr(scope, expr, level)?; + let result = self.eval_expr(scope, fn_lib, expr, level)?; Ok(if !matches!(expr.as_ref(), Expr::Assignment(_, _, _)) { result @@ -1593,7 +1585,7 @@ impl Engine<'_> { let prev_len = scope.len(); let result = block.iter().try_fold(().into_dynamic(), |_, stmt| { - self.eval_stmt(scope, stmt, level) + self.eval_stmt(scope, fn_lib, stmt, level) }); scope.rewind(prev_len); @@ -1603,14 +1595,14 @@ impl Engine<'_> { // If-else statement Stmt::IfThenElse(guard, if_body, else_body) => self - .eval_expr(scope, guard, level)? + .eval_expr(scope, fn_lib, guard, level)? .try_cast::() .map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position())) .and_then(|guard_val| { if guard_val { - self.eval_stmt(scope, if_body, level) + self.eval_stmt(scope, fn_lib, if_body, level) } else if let Some(stmt) = else_body { - self.eval_stmt(scope, stmt.as_ref(), level) + self.eval_stmt(scope, fn_lib, stmt.as_ref(), level) } else { Ok(().into_dynamic()) } @@ -1618,12 +1610,19 @@ impl Engine<'_> { // While loop Stmt::While(guard, body) => loop { - match self.eval_expr(scope, guard, level)?.try_cast::() { - Ok(guard_val) if guard_val => match self.eval_stmt(scope, body, level) { - Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (), - Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()), - Err(x) => return Err(x), - }, + match self + .eval_expr(scope, fn_lib, guard, level)? + .try_cast::() + { + Ok(guard_val) if guard_val => { + match self.eval_stmt(scope, fn_lib, body, level) { + Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (), + Err(EvalAltResult::ErrorLoopBreak(true, _)) => { + return Ok(().into_dynamic()) + } + Err(x) => return Err(x), + } + } Ok(_) => return Ok(().into_dynamic()), Err(_) => return Err(EvalAltResult::ErrorLogicGuard(guard.position())), } @@ -1631,7 +1630,7 @@ impl Engine<'_> { // Loop statement Stmt::Loop(body) => loop { - match self.eval_stmt(scope, body, level) { + match self.eval_stmt(scope, fn_lib, body, level) { Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (), Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()), Err(x) => return Err(x), @@ -1640,7 +1639,7 @@ impl Engine<'_> { // For loop Stmt::For(name, expr, body) => { - let arr = self.eval_expr(scope, expr, level)?; + let arr = self.eval_expr(scope, fn_lib, expr, level)?; let tid = Any::type_id(arr.as_ref()); if let Some(type_iterators) = &self.type_iterators { @@ -1658,7 +1657,7 @@ impl Engine<'_> { for a in iter_fn(&arr) { *scope.get_mut(entry) = a; - match self.eval_stmt(scope, body, level) { + match self.eval_stmt(scope, fn_lib, body, level) { Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (), Err(EvalAltResult::ErrorLoopBreak(true, _)) => break, Err(x) => return Err(x), @@ -1688,7 +1687,7 @@ impl Engine<'_> { // Return value Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(EvalAltResult::Return( - self.eval_expr(scope, a, level)?, + self.eval_expr(scope, fn_lib, a, level)?, *pos, )), @@ -1699,7 +1698,7 @@ impl Engine<'_> { // Throw value Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => { - let val = self.eval_expr(scope, a, level)?; + let val = self.eval_expr(scope, fn_lib, a, level)?; Err(EvalAltResult::ErrorRuntime( val.try_cast::().unwrap_or_else(|_| "".to_string()), *pos, @@ -1708,7 +1707,7 @@ impl Engine<'_> { // Let statement Stmt::Let(name, Some(expr), _) => { - let val = self.eval_expr(scope, expr, level)?; + let val = self.eval_expr(scope, fn_lib, expr, level)?; // TODO - avoid copying variable name in inner block? scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false); Ok(().into_dynamic()) @@ -1722,7 +1721,7 @@ impl Engine<'_> { // Const statement Stmt::Const(name, expr, _) if expr.is_constant() => { - let val = self.eval_expr(scope, expr, level)?; + let val = self.eval_expr(scope, fn_lib, expr, level)?; // TODO - avoid copying variable name in inner block? scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true); Ok(().into_dynamic()) @@ -1745,11 +1744,6 @@ impl Engine<'_> { .unwrap_or(name) } } - - /// Clean up all script-defined functions within the `Engine`. - pub fn clear_functions(&mut self) { - self.fn_lib = None; - } } /// Print/debug to stdout diff --git a/src/lib.rs b/src/lib.rs index 3321fccc..dba21d7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ //! //! engine.register_fn("compute_something", compute_something); //! -//! # #[cfg(not(feature = "no_std"))] +//! # #[cfg(not(feature = "no_std"))] //! assert_eq!(engine.eval_file::("my_script.rhai".into())?, true); //! //! Ok(()) diff --git a/src/optimize.rs b/src/optimize.rs index f20e719a..85c61d1e 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -39,15 +39,18 @@ struct State<'a> { constants: Vec<(String, Expr)>, /// An `Engine` instance for eager function evaluation. engine: &'a Engine<'a>, + /// Library of script-defined functions. + fn_lib: &'a [(&'a str, usize)], } impl<'a> State<'a> { /// Create a new State. - pub fn new(engine: &'a Engine<'a>) -> Self { + pub fn new(engine: &'a Engine<'a>, fn_lib: &'a [(&'a str, usize)]) -> Self { Self { changed: false, constants: vec![], engine, + fn_lib, } } /// Reset the state from dirty to clean. @@ -502,11 +505,9 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { && args.iter().all(|expr| expr.is_constant()) // all arguments are constants => { // First search in script-defined functions (can override built-in) - if let Some(fn_lib_arc) = &state.engine.fn_lib { - if fn_lib_arc.has_function(&id, args.len()) { - // A script-defined function overrides the built-in function - do not make the call - return Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos); - } + if state.fn_lib.iter().find(|(name, len)| name == &id && *len == args.len()).is_some() { + // A script-defined function overrides the built-in function - do not make the call + return Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos); } let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect(); @@ -554,14 +555,19 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { } } -pub(crate) fn optimize<'a>(statements: Vec, engine: &Engine<'a>, scope: &Scope) -> Vec { +pub(crate) fn optimize<'a>( + statements: Vec, + engine: &Engine<'a>, + scope: &Scope, + fn_lib: &'a [(&'a str, usize)], +) -> Vec { // If optimization level is None then skip optimizing if engine.optimization_level == OptimizationLevel::None { return statements; } // Set up the state - let mut state = State::new(engine); + let mut state = State::new(engine, fn_lib); // Add constants from the scope into the state scope @@ -635,7 +641,12 @@ pub fn optimize_into_ast( statements: Vec, functions: Vec, ) -> AST { - let fn_lib = FunctionsLib::from_vec( + let fn_lib: Vec<_> = functions + .iter() + .map(|fn_def| (fn_def.name.as_str(), fn_def.params.len())) + .collect(); + + let lib = FunctionsLib::from_vec( functions .iter() .cloned() @@ -644,7 +655,7 @@ pub fn optimize_into_ast( let pos = fn_def.body.position(); // Optimize the function body - let mut body = optimize(vec![fn_def.body], engine, &Scope::new()); + let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib); // {} -> Noop fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { @@ -667,12 +678,12 @@ pub fn optimize_into_ast( match engine.optimization_level { OptimizationLevel::None => statements, OptimizationLevel::Simple | OptimizationLevel::Full => { - optimize(statements, engine, &scope) + optimize(statements, engine, &scope, &fn_lib) } }, #[cfg(feature = "sync")] - Arc::new(fn_lib), + Arc::new(lib), #[cfg(not(feature = "sync"))] - Rc::new(fn_lib), + Rc::new(lib), ) } diff --git a/src/parser.rs b/src/parser.rs index e425dc4b..98a3aff3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -199,7 +199,7 @@ impl AST { /// # { /// use rhai::Engine; /// - /// let mut engine = Engine::new(); + /// let engine = Engine::new(); /// /// let ast1 = engine.compile(r#"fn foo(x) { 42 + x } foo(1)"#)?; /// let ast2 = engine.compile(r#"fn foo(n) { "hello" + n } foo("!")"#)?; diff --git a/src/result.rs b/src/result.rs index b9f57f86..29af3bbd 100644 --- a/src/result.rs +++ b/src/result.rs @@ -16,6 +16,8 @@ use crate::stdlib::path::PathBuf; /// Evaluation result. /// /// All wrapped `Position` values represent the location in the script where the error occurs. +/// +/// Currently, `EvalAltResult` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. #[derive(Debug)] pub enum EvalAltResult { /// Syntax error. diff --git a/src/scope.rs b/src/scope.rs index 15eda16c..b5e22170 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -49,7 +49,7 @@ pub(crate) struct EntryRef<'a> { /// # fn main() -> Result<(), rhai::EvalAltResult> { /// use rhai::{Engine, Scope}; /// -/// let mut engine = Engine::new(); +/// let engine = Engine::new(); /// let mut my_scope = Scope::new(); /// /// my_scope.push("z", 40_i64); diff --git a/tests/arrays.rs b/tests/arrays.rs index 438ae4ea..e1dc066a 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -3,7 +3,7 @@ use rhai::{Array, Engine, EvalAltResult, RegisterFn, INT}; #[test] fn test_arrays() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = [1, 2, 3]; x[1]")?, 2); assert_eq!(engine.eval::("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5); diff --git a/tests/binary_ops.rs b/tests/binary_ops.rs index 3e59e8fd..922c6a43 100644 --- a/tests/binary_ops.rs +++ b/tests/binary_ops.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_binary_ops() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("10 % 4")?, 2); assert_eq!(engine.eval::("10 << 4")?, 160); diff --git a/tests/bit_shift.rs b/tests/bit_shift.rs index 7dd3aa76..e8e360f3 100644 --- a/tests/bit_shift.rs +++ b/tests/bit_shift.rs @@ -2,14 +2,14 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_left_shift() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("4 << 2")?, 16); Ok(()) } #[test] fn test_right_shift() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("9 >> 1")?, 4); Ok(()) } diff --git a/tests/bool_op.rs b/tests/bool_op.rs index a39001a2..3bd5859a 100644 --- a/tests/bool_op.rs +++ b/tests/bool_op.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult}; #[test] fn test_bool_op1() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("true && (false || true)")?, true); assert_eq!(engine.eval::("true & (false | true)")?, true); @@ -12,7 +12,7 @@ fn test_bool_op1() -> Result<(), EvalAltResult> { #[test] fn test_bool_op2() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("false && (false || true)")?, false); assert_eq!(engine.eval::("false & (false | true)")?, false); @@ -22,7 +22,7 @@ fn test_bool_op2() -> Result<(), EvalAltResult> { #[test] fn test_bool_op3() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert!(engine.eval::("true && (false || 123)").is_err()); assert_eq!(engine.eval::("true && (true || 123)")?, true); @@ -34,7 +34,7 @@ fn test_bool_op3() -> Result<(), EvalAltResult> { #[test] fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( @@ -63,7 +63,7 @@ fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> { #[test] fn test_bool_op_no_short_circuit1() { - let mut engine = Engine::new(); + let engine = Engine::new(); assert!(engine .eval::( @@ -78,7 +78,7 @@ fn test_bool_op_no_short_circuit1() { #[test] fn test_bool_op_no_short_circuit2() { - let mut engine = Engine::new(); + let engine = Engine::new(); assert!(engine .eval::( diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 909e8a90..b0b3793d 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -20,7 +20,7 @@ fn test_fn() -> Result<(), EvalAltResult> { #[test] fn test_call_fn() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let mut scope = Scope::new(); scope.push("foo", 42 as INT); diff --git a/tests/chars.rs b/tests/chars.rs index 2739401b..8d6be4d8 100644 --- a/tests/chars.rs +++ b/tests/chars.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult}; #[test] fn test_chars() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("'y'")?, 'y'); assert_eq!(engine.eval::("'\\u2764'")?, '❤'); diff --git a/tests/comments.rs b/tests/comments.rs index 8c071f07..d49b7586 100644 --- a/tests/comments.rs +++ b/tests/comments.rs @@ -2,13 +2,13 @@ use rhai::{Engine, INT}; #[test] fn test_comments() { - let mut engine = Engine::new(); + let engine = Engine::new(); assert!(engine .eval::("let x = 5; x // I am a single line comment, yay!") .is_ok()); assert!(engine - .eval::("let /* I am a multiline comment, yay! */ x = 5; x") + .eval::("let /* I am a multi-line comment, yay! */ x = 5; x") .is_ok()); } diff --git a/tests/compound_equality.rs b/tests/compound_equality.rs index 569cfd94..b516bb97 100644 --- a/tests/compound_equality.rs +++ b/tests/compound_equality.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_or_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 16; x |= 74; x")?, 90); assert_eq!(engine.eval::("let x = true; x |= false; x")?, true); @@ -13,7 +13,7 @@ fn test_or_equals() -> Result<(), EvalAltResult> { #[test] fn test_and_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 16; x &= 31; x")?, 16); assert_eq!(engine.eval::("let x = true; x &= false; x")?, false); @@ -25,42 +25,42 @@ fn test_and_equals() -> Result<(), EvalAltResult> { #[test] fn test_xor_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 90; x ^= 12; x")?, 86); Ok(()) } #[test] fn test_multiply_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 2; x *= 3; x")?, 6); Ok(()) } #[test] fn test_divide_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 6; x /= 2; x")?, 3); Ok(()) } #[test] fn test_left_shift_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 9; x >>=1; x")?, 4); Ok(()) } #[test] fn test_right_shift_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 4; x<<= 2; x")?, 16); Ok(()) } #[test] fn test_modulo_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 10; x %= 4; x")?, 2); Ok(()) } diff --git a/tests/constants.rs b/tests/constants.rs index 16fbf7bd..2dd62000 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_constant() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("const x = 123; x")?, 123); diff --git a/tests/decrement.rs b/tests/decrement.rs index d653a7aa..a8cb2634 100644 --- a/tests/decrement.rs +++ b/tests/decrement.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_decrement() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 10; x -= 7; x")?, 3); diff --git a/tests/eval.rs b/tests/eval.rs index fc4424a4..bf6f750f 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, Scope, INT}; #[test] fn test_eval() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( @@ -19,7 +19,7 @@ fn test_eval() -> Result<(), EvalAltResult> { #[test] #[cfg(not(feature = "no_function"))] fn test_eval_function() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let mut scope = Scope::new(); assert_eq!( @@ -62,7 +62,7 @@ fn test_eval_function() -> Result<(), EvalAltResult> { #[test] #[cfg(not(feature = "no_function"))] fn test_eval_override() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( diff --git a/tests/expressions.rs b/tests/expressions.rs index 9023f97c..2091fe4e 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, Scope, INT}; #[test] fn test_expressions() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let mut scope = Scope::new(); scope.push("x", 10 as INT); diff --git a/tests/float.rs b/tests/float.rs index 20349098..94ec7a03 100644 --- a/tests/float.rs +++ b/tests/float.rs @@ -5,7 +5,7 @@ const EPSILON: FLOAT = 0.000_000_000_1; #[test] fn test_float() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::("let x = 0.0; let y = 1.0; x < y")?, diff --git a/tests/for.rs b/tests/for.rs index 448c7fcb..f36f3ae3 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[cfg(not(feature = "no_index"))] #[test] fn test_for_array() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let script = r" let sum1 = 0; @@ -33,7 +33,7 @@ fn test_for_array() -> Result<(), EvalAltResult> { #[cfg(not(feature = "no_object"))] #[test] fn test_for_object() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let script = r#" let sum = 0; diff --git a/tests/if_block.rs b/tests/if_block.rs index 4699ee57..dcde8143 100644 --- a/tests/if_block.rs +++ b/tests/if_block.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_if() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("if true { 55 }")?, 55); assert_eq!(engine.eval::("if false { 55 } else { 44 }")?, 44); @@ -30,7 +30,7 @@ fn test_if() -> Result<(), EvalAltResult> { #[test] fn test_if_expr() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( diff --git a/tests/increment.rs b/tests/increment.rs index 72c6eb35..4be9397a 100644 --- a/tests/increment.rs +++ b/tests/increment.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_increment() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 1; x += 2; x")?, 3); assert_eq!( diff --git a/tests/internal_fn.rs b/tests/internal_fn.rs index 4a6720d5..cc99187a 100644 --- a/tests/internal_fn.rs +++ b/tests/internal_fn.rs @@ -4,9 +4,12 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_internal_fn() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); - assert_eq!(engine.eval::("fn addme(a, b) { a+b } addme(3, 4)")?, 7); + assert_eq!( + engine.eval::("fn add_me(a, b) { a+b } add_me(3, 4)")?, + 7 + ); assert_eq!(engine.eval::("fn bob() { return 4; 5 } bob()")?, 4); Ok(()) @@ -14,15 +17,15 @@ fn test_internal_fn() -> Result<(), EvalAltResult> { #[test] fn test_big_internal_fn() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( r" - fn mathme(a, b, c, d, e, f) { + fn math_me(a, b, c, d, e, f) { a - b * c + d * e - f } - mathme(100, 5, 2, 9, 6, 32) + math_me(100, 5, 2, 9, 6, 32) ", )?, 112 @@ -33,7 +36,7 @@ fn test_big_internal_fn() -> Result<(), EvalAltResult> { #[test] fn test_internal_fn_overloading() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( diff --git a/tests/looping.rs b/tests/looping.rs index bf2c1a72..c7aace27 100644 --- a/tests/looping.rs +++ b/tests/looping.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_loop() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( diff --git a/tests/maps.rs b/tests/maps.rs index 746be5cd..c256a1a8 100644 --- a/tests/maps.rs +++ b/tests/maps.rs @@ -4,7 +4,7 @@ use rhai::{AnyExt, Engine, EvalAltResult, Map, INT}; #[test] fn test_map_indexing() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); #[cfg(not(feature = "no_index"))] { @@ -75,7 +75,7 @@ fn test_map_indexing() -> Result<(), EvalAltResult> { #[test] fn test_map_assign() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let x = engine.eval::(r#"let x = #{a: 1, b: true, "c$": "hello"}; x"#)?; let a = x.get("a").cloned().expect("should have property a"); @@ -91,7 +91,7 @@ fn test_map_assign() -> Result<(), EvalAltResult> { #[test] fn test_map_return() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let x = engine.eval::(r#"#{a: 1, b: true, "c$": "hello"}"#)?; let a = x.get("a").cloned().expect("should have property a"); @@ -107,7 +107,7 @@ fn test_map_return() -> Result<(), EvalAltResult> { #[test] fn test_map_for() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::( diff --git a/tests/math.rs b/tests/math.rs index 24181c5f..500b35b8 100644 --- a/tests/math.rs +++ b/tests/math.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_math() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("1 + 2")?, 3); assert_eq!(engine.eval::("1 - 2")?, -1); diff --git a/tests/mismatched_op.rs b/tests/mismatched_op.rs index 5629f6cd..57cbb86e 100644 --- a/tests/mismatched_op.rs +++ b/tests/mismatched_op.rs @@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, RegisterFn, INT}; #[test] #[cfg(not(feature = "no_stdlib"))] fn test_mismatched_op() { - let mut engine = Engine::new(); + let engine = Engine::new(); assert!( matches!(engine.eval::(r#"60 + "hello""#).expect_err("expects error"), diff --git a/tests/not.rs b/tests/not.rs index 52913136..4ce9cd3e 100644 --- a/tests/not.rs +++ b/tests/not.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult}; #[test] fn test_not() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::("let not_true = !true; not_true")?, diff --git a/tests/number_literals.rs b/tests/number_literals.rs index f6466967..3fe8c78f 100644 --- a/tests/number_literals.rs +++ b/tests/number_literals.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_number_literal() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("65")?, 65); @@ -11,7 +11,7 @@ fn test_number_literal() -> Result<(), EvalAltResult> { #[test] fn test_hex_literal() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 0xf; x")?, 15); assert_eq!(engine.eval::("let x = 0xff; x")?, 255); @@ -21,7 +21,7 @@ fn test_hex_literal() -> Result<(), EvalAltResult> { #[test] fn test_octal_literal() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 0o77; x")?, 63); assert_eq!(engine.eval::("let x = 0o1234; x")?, 668); @@ -31,7 +31,7 @@ fn test_octal_literal() -> Result<(), EvalAltResult> { #[test] fn test_binary_literal() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 0b1111; x")?, 15); assert_eq!( diff --git a/tests/ops.rs b/tests/ops.rs index aaddea8a..c817d65d 100644 --- a/tests/ops.rs +++ b/tests/ops.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_ops() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("60 + 5")?, 65); assert_eq!(engine.eval::("(1 + 2) * (6 - 4) / 2")?, 3); @@ -11,8 +11,8 @@ fn test_ops() -> Result<(), EvalAltResult> { } #[test] -fn test_op_prec() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); +fn test_op_precedence() -> Result<(), EvalAltResult> { + let engine = Engine::new(); assert_eq!( engine.eval::("let x = 0; if x == 10 || true { x = 1} x")?, diff --git a/tests/power_of.rs b/tests/power_of.rs index b8b83f08..3762ff0f 100644 --- a/tests/power_of.rs +++ b/tests/power_of.rs @@ -8,7 +8,7 @@ const EPSILON: FLOAT = 0.000_000_000_1; #[test] fn test_power_of() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("2 ~ 3")?, 8); assert_eq!(engine.eval::("(-2 ~ 3)")?, -8); @@ -29,7 +29,7 @@ fn test_power_of() -> Result<(), EvalAltResult> { #[test] fn test_power_of_equals() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = 2; x ~= 3; x")?, 8); assert_eq!(engine.eval::("let x = -2; x ~= 3; x")?, -8); diff --git a/tests/side_effects.rs b/tests/side_effects.rs index d97b6f33..0375d1af 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -1,5 +1,3 @@ -#![cfg(not(feature = "no_object"))] - ///! This test simulates an external command object that is driven by a script. use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT}; use std::sync::{Arc, Mutex}; @@ -40,8 +38,9 @@ impl CommandWrapper { } } +#[cfg(not(feature = "no_object"))] #[test] -fn test_side_effects() -> Result<(), EvalAltResult> { +fn test_side_effects_command() -> Result<(), EvalAltResult> { let mut engine = Engine::new(); let mut scope = Scope::new(); @@ -79,3 +78,22 @@ fn test_side_effects() -> Result<(), EvalAltResult> { Ok(()) } + +#[test] +fn test_side_effects_print() -> Result<(), EvalAltResult> { + use std::sync::RwLock; + + let result = RwLock::new(String::from("")); + + { + let mut engine = Engine::new(); + + // Override action of 'print' function + engine.on_print(|s| result.write().unwrap().push_str(s)); + + engine.consume("print(40 + 2);")?; + } + + assert_eq!(*result.read().unwrap(), "42"); + Ok(()) +} diff --git a/tests/string.rs b/tests/string.rs index 1ffd2e2e..3999df9e 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult}; #[test] fn test_string() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::(r#""Test string: \u2764""#)?, diff --git a/tests/throw.rs b/tests/throw.rs index 690d9c6e..6e55303c 100644 --- a/tests/throw.rs +++ b/tests/throw.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult}; #[test] fn test_throw() { - let mut engine = Engine::new(); + let engine = Engine::new(); assert!(matches!( engine.eval::<()>(r#"if true { throw "hello" }"#).expect_err("expects error"), diff --git a/tests/unary_after_binary.rs b/tests/unary_after_binary.rs index 16d4730a..ba74c9f9 100644 --- a/tests/unary_after_binary.rs +++ b/tests/unary_after_binary.rs @@ -4,7 +4,7 @@ use rhai::{Engine, EvalAltResult, INT}; // TODO also add test case for unary after compound // Hah, turns out unary + has a good use after all! fn test_unary_after_binary() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("10 % +4")?, 2); assert_eq!(engine.eval::("10 << +4")?, 160); diff --git a/tests/unary_minus.rs b/tests/unary_minus.rs index 80170498..e1a359d7 100644 --- a/tests/unary_minus.rs +++ b/tests/unary_minus.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_unary_minus() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = -5; x")?, -5); diff --git a/tests/unit.rs b/tests/unit.rs index 91e9d9f2..465c7b48 100644 --- a/tests/unit.rs +++ b/tests/unit.rs @@ -2,21 +2,21 @@ use rhai::{Engine, EvalAltResult}; #[test] fn test_unit() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); engine.eval::<()>("let x = (); x")?; Ok(()) } #[test] fn test_unit_eq() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = (); let y = (); x == y")?, true); Ok(()) } #[test] fn test_unit_with_spaces() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); engine.eval::<()>("let x = ( ); x")?; Ok(()) } diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 39cf5b76..8d5244c8 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, Scope, INT}; #[test] fn test_var_scope() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); let mut scope = Scope::new(); engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?; @@ -21,7 +21,7 @@ fn test_var_scope() -> Result<(), EvalAltResult> { #[test] fn test_scope_eval() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); // First create the state let mut scope = Scope::new(); diff --git a/tests/while_loop.rs b/tests/while_loop.rs index 18d5a03b..3a035039 100644 --- a/tests/while_loop.rs +++ b/tests/while_loop.rs @@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_while() -> Result<(), EvalAltResult> { - let mut engine = Engine::new(); + let engine = Engine::new(); assert_eq!( engine.eval::(