Make Engine reentrant to prepare for parallel execution.

This commit is contained in:
Stephen Chung 2020-04-07 13:23:06 +08:00
parent e204ae1a2c
commit e795a50ae2
44 changed files with 415 additions and 404 deletions

View File

@ -167,7 +167,7 @@ use rhai::{Engine, EvalAltResult};
fn main() -> Result<(), EvalAltResult> fn main() -> Result<(), EvalAltResult>
{ {
let mut engine = Engine::new(); let engine = Engine::new();
let result = engine.eval::<i64>("40 + 2")?; let result = engine.eval::<i64>("40 + 2")?;
@ -446,7 +446,7 @@ fn get_an_any() -> Dynamic {
fn main() -> Result<(), EvalAltResult> fn main() -> Result<(), EvalAltResult>
{ {
let mut engine = Engine::new(); let engine = Engine::new();
engine.register_fn("add", add); engine.register_fn("add", add);
@ -497,7 +497,7 @@ fn show_it<T: Display>(x: &mut T) -> () {
fn main() 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 i64)->());
engine.register_fn("print", show_it as fn(x: &mut bool)->()); engine.register_fn("print", show_it as fn(x: &mut bool)->());
@ -533,7 +533,7 @@ fn safe_divide(x: i64, y: i64) -> Result<i64, EvalAltResult> {
fn main() fn main()
{ {
let mut engine = Engine::new(); let engine = Engine::new();
// Fallible functions that return Result values must use register_result_fn() // Fallible functions that return Result values must use register_result_fn()
engine.register_result_fn("divide", safe_divide); engine.register_result_fn("divide", safe_divide);
@ -584,7 +584,7 @@ impl TestStruct {
fn main() -> Result<(), EvalAltResult> fn main() -> Result<(), EvalAltResult>
{ {
let mut engine = Engine::new(); let engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
@ -622,7 +622,7 @@ impl TestStruct {
} }
} }
let mut engine = Engine::new(); let engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
``` ```
@ -712,7 +712,7 @@ impl TestStruct {
} }
} }
let mut engine = Engine::new(); let engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
@ -748,7 +748,7 @@ use rhai::{Engine, Scope, EvalAltResult};
fn main() -> Result<(), EvalAltResult> fn main() -> Result<(), EvalAltResult>
{ {
let mut engine = Engine::new(); let engine = Engine::new();
// First create the state // First create the state
let mut scope = Scope::new(); 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_, 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 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 ```rust
let script = "x += 32"; let script = "x += 32";

View File

@ -1,7 +1,7 @@
use rhai::{Engine, EvalAltResult, INT}; use rhai::{Engine, EvalAltResult, INT};
fn main() -> Result<(), EvalAltResult> { fn main() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let result = engine.eval::<INT>("40 + 2")?; let result = engine.eval::<INT>("40 + 2")?;

View File

@ -3,7 +3,7 @@
use rhai::{Engine, EvalAltResult, INT}; use rhai::{Engine, EvalAltResult, INT};
fn main() -> Result<(), EvalAltResult> { fn main() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let result = engine.eval::<INT>("40 + 2")?; let result = engine.eval::<INT>("40 + 2")?;

View File

@ -1,7 +1,7 @@
use rhai::{Engine, EvalAltResult, Scope, INT}; use rhai::{Engine, EvalAltResult, Scope, INT};
fn main() -> Result<(), EvalAltResult> { fn main() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?; engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?;

View File

@ -334,7 +334,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// // Compile a script to an AST and store it for later evaluation /// // Compile a script to an AST and store it for later evaluation
/// let ast = engine.compile("40 + 2")?; /// let ast = engine.compile("40 + 2")?;
@ -413,7 +413,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// 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. /// // 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. /// // 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> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// // Compile a script to an AST and store it for later evaluation /// // Compile a script to an AST and store it for later evaluation
/// let ast = engine.compile_expression("40 + 2")?; /// let ast = engine.compile_expression("40 + 2")?;
@ -552,7 +552,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// 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. /// // Notice that a PathBuf is required which can easily be constructed from a string.
/// let result = engine.eval_file::<i64>("script.rhai".into())?; /// let result = engine.eval_file::<i64>("script.rhai".into())?;
@ -560,7 +560,7 @@ impl<'e> Engine<'e> {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
pub fn eval_file<T: Any + Clone>(&mut self, path: PathBuf) -> Result<T, EvalAltResult> { pub fn eval_file<T: Any + Clone>(&self, path: PathBuf) -> Result<T, EvalAltResult> {
Self::read_file(path).and_then(|contents| self.eval::<T>(&contents)) Self::read_file(path).and_then(|contents| self.eval::<T>(&contents))
} }
@ -572,7 +572,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope}; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// // Create initialized scope /// // Create initialized scope
/// let mut scope = Scope::new(); /// let mut scope = Scope::new();
@ -585,7 +585,7 @@ impl<'e> Engine<'e> {
/// ``` /// ```
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
pub fn eval_file_with_scope<T: Any + Clone>( pub fn eval_file_with_scope<T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
path: PathBuf, path: PathBuf,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
@ -600,13 +600,13 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// assert_eq!(engine.eval::<i64>("40 + 2")?, 42); /// assert_eq!(engine.eval::<i64>("40 + 2")?, 42);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> { pub fn eval<T: Any + Clone>(&self, input: &str) -> Result<T, EvalAltResult> {
self.eval_with_scope(&mut Scope::new(), input) self.eval_with_scope(&mut Scope::new(), input)
} }
@ -618,7 +618,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope}; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// // Create initialized scope /// // Create initialized scope
/// let mut scope = Scope::new(); /// let mut scope = Scope::new();
@ -633,7 +633,7 @@ impl<'e> Engine<'e> {
/// # } /// # }
/// ``` /// ```
pub fn eval_with_scope<T: Any + Clone>( pub fn eval_with_scope<T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
input: &str, input: &str,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
@ -649,13 +649,13 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// assert_eq!(engine.eval_expression::<i64>("40 + 2")?, 42); /// assert_eq!(engine.eval_expression::<i64>("40 + 2")?, 42);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn eval_expression<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> { pub fn eval_expression<T: Any + Clone>(&self, input: &str) -> Result<T, EvalAltResult> {
self.eval_expression_with_scope(&mut Scope::new(), input) self.eval_expression_with_scope(&mut Scope::new(), input)
} }
@ -667,7 +667,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope}; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// // Create initialized scope /// // Create initialized scope
/// let mut scope = Scope::new(); /// let mut scope = Scope::new();
@ -678,7 +678,7 @@ impl<'e> Engine<'e> {
/// # } /// # }
/// ``` /// ```
pub fn eval_expression_with_scope<T: Any + Clone>( pub fn eval_expression_with_scope<T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
input: &str, input: &str,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
@ -697,7 +697,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// // Compile a script to an AST and store it for later evaluation /// // Compile a script to an AST and store it for later evaluation
/// let ast = engine.compile("40 + 2")?; /// let ast = engine.compile("40 + 2")?;
@ -707,7 +707,7 @@ impl<'e> Engine<'e> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> { pub fn eval_ast<T: Any + Clone>(&self, ast: &AST) -> Result<T, EvalAltResult> {
self.eval_ast_with_scope(&mut Scope::new(), ast) self.eval_ast_with_scope(&mut Scope::new(), ast)
} }
@ -719,7 +719,7 @@ impl<'e> Engine<'e> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope}; /// 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 /// // Compile a script to an AST and store it for later evaluation
/// let ast = engine.compile("x + 2")?; /// let ast = engine.compile("x + 2")?;
@ -741,7 +741,7 @@ impl<'e> Engine<'e> {
/// # } /// # }
/// ``` /// ```
pub fn eval_ast_with_scope<T: Any + Clone>( pub fn eval_ast_with_scope<T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
@ -756,32 +756,25 @@ impl<'e> Engine<'e> {
} }
pub(crate) fn eval_ast_with_scope_raw( pub(crate) fn eval_ast_with_scope_raw(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<Dynamic, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
let statements = { ast.0
let AST(statements, functions) = ast;
self.fn_lib = Some(functions.clone());
statements
};
let result = statements
.iter() .iter()
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0)); .try_fold(().into_dynamic(), |_, stmt| {
self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0)
self.fn_lib = None; })
.or_else(|err| match err {
result.or_else(|err| match err { EvalAltResult::Return(out, _) => Ok(out),
EvalAltResult::Return(out, _) => Ok(out), _ => Err(err),
_ => Err(err), })
})
} }
/// Evaluate a file, but throw away the result and only return error (if any). /// 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. /// Useful for when you don't need the result, but still need to keep track of possible errors.
#[cfg(not(feature = "no_std"))] #[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)) 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. /// Useful for when you don't need the result, but still need to keep track of possible errors.
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
pub fn consume_file_with_scope( pub fn consume_file_with_scope(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
path: PathBuf, path: PathBuf,
) -> Result<(), EvalAltResult> { ) -> Result<(), EvalAltResult> {
@ -798,17 +791,13 @@ impl<'e> Engine<'e> {
/// Evaluate a string, but throw away the result and only return error (if any). /// 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. /// 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) 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). /// 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. /// Useful for when you don't need the result, but still need to keep track of possible errors.
pub fn consume_with_scope( pub fn consume_with_scope(&self, scope: &mut Scope, input: &str) -> Result<(), EvalAltResult> {
&mut self,
scope: &mut Scope,
input: &str,
) -> Result<(), EvalAltResult> {
let tokens_stream = lex(input); let tokens_stream = lex(input);
let ast = parse(&mut tokens_stream.peekable(), self, scope) 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). /// 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. /// 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) 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). /// 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. /// Useful for when you don't need the result, but still need to keep track of possible errors.
pub fn consume_ast_with_scope( pub fn consume_ast_with_scope(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<(), EvalAltResult> { ) -> Result<(), EvalAltResult> {
let statements = { ast.0
let AST(statements, functions) = ast;
self.fn_lib = Some(functions.clone());
statements
};
let result = statements
.iter() .iter()
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0)); .try_fold(().into_dynamic(), |_, stmt| {
self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0)
self.fn_lib = None; })
.map(|_| ())
result.map(|_| ()).or_else(|err| match err { .or_else(|err| match err {
EvalAltResult::Return(_, _) => Ok(()), EvalAltResult::Return(_, _) => Ok(()),
_ => Err(err), _ => Err(err),
}) })
} }
/// Call a script function defined in an `AST` with no argument. /// Call a script function defined in an `AST` with no argument.
@ -859,7 +842,7 @@ impl<'e> Engine<'e> {
/// # { /// # {
/// use rhai::{Engine, Scope}; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// let ast = engine.compile("fn num() { 42 + foo }")?; /// let ast = engine.compile("fn num() { 42 + foo }")?;
/// ///
@ -876,7 +859,7 @@ impl<'e> Engine<'e> {
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub fn call_fn0<T: Any + Clone>( pub fn call_fn0<T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
@ -895,7 +878,7 @@ impl<'e> Engine<'e> {
/// # { /// # {
/// use rhai::{Engine, Scope}; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// let ast = engine.compile("fn inc(x) { x + foo }")?; /// let ast = engine.compile("fn inc(x) { x + foo }")?;
/// ///
@ -912,7 +895,7 @@ impl<'e> Engine<'e> {
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub fn call_fn1<A: Any + Clone, T: Any + Clone>( pub fn call_fn1<A: Any + Clone, T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
@ -932,7 +915,7 @@ impl<'e> Engine<'e> {
/// # { /// # {
/// use rhai::{Engine, Scope}; /// 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 }")?; /// 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"))] #[cfg(not(feature = "no_function"))]
pub fn call_fn<A: FuncArgs, T: Any + Clone>( pub fn call_fn<A: FuncArgs, T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
@ -960,29 +943,24 @@ impl<'e> Engine<'e> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
fn call_fn_internal<T: Any + Clone>( fn call_fn_internal<T: Any + Clone>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
mut arg_values: Vec<Dynamic>, mut arg_values: Vec<Dynamic>,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
let mut args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect(); 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()); self.call_fn_raw(Some(scope), fn_lib, name, &mut args, None, pos, 0)?
let result = self
.call_fn_raw(Some(scope), name, &mut args, None, Position::none(), 0)?
.try_cast() .try_cast()
.map_err(|a| { .map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(), 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. /// 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. /// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
pub fn optimize_ast(&self, scope: &Scope, ast: AST) -> AST { pub fn optimize_ast(&self, scope: &Scope, ast: AST) -> AST {
optimize_into_ast( let fn_lib = ast.1.iter().map(|fn_def| fn_def.as_ref().clone()).collect();
self, optimize_into_ast(self, scope, ast.0, fn_lib)
scope,
ast.0,
ast.1.iter().map(|fn_def| fn_def.as_ref().clone()).collect(),
)
} }
/// Override default action of `print` (print to stdout using `println!`) /// Override default action of `print` (print to stdout using `println!`)
@ -1012,22 +986,24 @@ impl<'e> Engine<'e> {
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock;
/// use rhai::Engine; /// 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 /// // Override action of 'print' function
/// engine.on_print(|s| result.push_str(s)); /// engine.on_print(|s| result.write().unwrap().push_str(s));
/// engine.consume("print(40 + 2);")?; ///
/// engine.consume("print(40 + 2);")?;
/// } /// }
/// assert_eq!(result, "42"); /// assert_eq!(*result.read().unwrap(), "42");
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "sync")] #[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)); self.on_print = Some(Box::new(callback));
} }
/// Override default action of `print` (print to stdout using `println!`) /// Override default action of `print` (print to stdout using `println!`)
@ -1036,22 +1012,24 @@ impl<'e> Engine<'e> {
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock;
/// use rhai::Engine; /// 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 /// // Override action of 'print' function
/// engine.on_print(|s| result.push_str(s)); /// engine.on_print(|s| result.write().unwrap().push_str(s));
/// engine.consume("print(40 + 2);")?; ///
/// engine.consume("print(40 + 2);")?;
/// } /// }
/// assert_eq!(result, "42"); /// assert_eq!(*result.read().unwrap(), "42");
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "sync"))] #[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)); self.on_print = Some(Box::new(callback));
} }
@ -1061,22 +1039,24 @@ impl<'e> Engine<'e> {
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock;
/// use rhai::Engine; /// 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 /// // Override action of 'print' function
/// engine.on_debug(|s| result.push_str(s)); /// engine.on_debug(|s| result.write().unwrap().push_str(s));
/// engine.consume(r#"debug("hello");"#)?; ///
/// engine.consume(r#"debug("hello");"#)?;
/// } /// }
/// assert_eq!(result, "\"hello\""); /// assert_eq!(*result.read().unwrap(), r#""hello""#);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "sync")] #[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)); self.on_debug = Some(Box::new(callback));
} }
/// Override default action of `debug` (print to stdout using `println!`) /// Override default action of `debug` (print to stdout using `println!`)
@ -1085,22 +1065,24 @@ impl<'e> Engine<'e> {
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock;
/// use rhai::Engine; /// 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 /// // Override action of 'print' function
/// engine.on_debug(|s| result.push_str(s)); /// engine.on_debug(|s| result.write().unwrap().push_str(s));
/// engine.consume(r#"debug("hello");"#)?; ///
/// engine.consume(r#"debug("hello");"#)?;
/// } /// }
/// assert_eq!(result, "\"hello\""); /// assert_eq!(*result.read().unwrap(), r#""hello""#);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "sync"))] #[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)); self.on_debug = Some(Box::new(callback));
} }
} }

View File

@ -1,7 +1,8 @@
//! Main module defining the script evaluation `Engine`. //! Main module defining the script evaluation `Engine`.
use crate::any::{Any, AnyExt, Dynamic, Variant}; 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::result::EvalAltResult;
use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope};
@ -232,7 +233,7 @@ impl DerefMut for FunctionsLib {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// let result = engine.eval::<i64>("40 + 2")?; /// let result = engine.eval::<i64>("40 + 2")?;
/// ///
@ -246,13 +247,6 @@ pub struct Engine<'e> {
/// A hashmap containing all compiled functions known to the engine. /// A hashmap containing all compiled functions known to the engine.
pub(crate) functions: Option<HashMap<FnSpec<'e>, Box<FnAny>>>, pub(crate) functions: Option<HashMap<FnSpec<'e>, Box<FnAny>>>,
/// A hashmap containing all script-defined functions.
#[cfg(feature = "sync")]
pub(crate) fn_lib: Option<Arc<FunctionsLib>>,
/// A hashmap containing all script-defined functions.
#[cfg(not(feature = "sync"))]
pub(crate) fn_lib: Option<Rc<FunctionsLib>>,
/// A hashmap containing all iterators known to the engine. /// A hashmap containing all iterators known to the engine.
pub(crate) type_iterators: Option<HashMap<TypeId, Box<IteratorFn>>>, pub(crate) type_iterators: Option<HashMap<TypeId, Box<IteratorFn>>>,
/// A hashmap mapping type names to pretty-print names. /// A hashmap mapping type names to pretty-print names.
@ -260,17 +254,17 @@ pub struct Engine<'e> {
/// Closure for implementing the `print` command. /// Closure for implementing the `print` command.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub(crate) on_print: Option<Box<dyn FnMut(&str) + Send + Sync + 'e>>, pub(crate) on_print: Option<Box<dyn Fn(&str) + Send + Sync + 'e>>,
/// Closure for implementing the `print` command. /// Closure for implementing the `print` command.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub(crate) on_print: Option<Box<dyn FnMut(&str) + 'e>>, pub(crate) on_print: Option<Box<dyn Fn(&str) + 'e>>,
/// Closure for implementing the `debug` command. /// Closure for implementing the `debug` command.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub(crate) on_debug: Option<Box<dyn FnMut(&str) + Send + Sync + 'e>>, pub(crate) on_debug: Option<Box<dyn Fn(&str) + Send + Sync + 'e>>,
/// Closure for implementing the `debug` command. /// Closure for implementing the `debug` command.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub(crate) on_debug: Option<Box<dyn FnMut(&str) + 'e>>, pub(crate) on_debug: Option<Box<dyn Fn(&str) + 'e>>,
/// Optimize the AST after compilation. /// Optimize the AST after compilation.
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
@ -299,7 +293,6 @@ impl Default for Engine<'_> {
// Create the new scripting Engine // Create the new scripting Engine
let mut engine = Engine { let mut engine = Engine {
functions: None, functions: None,
fn_lib: None,
type_iterators: None, type_iterators: None,
type_names: Some(type_names), type_names: Some(type_names),
on_print: Some(Box::new(default_print)), // default print/debug implementations on_print: Some(Box::new(default_print)), // default print/debug implementations
@ -368,7 +361,6 @@ impl Engine<'_> {
pub fn new_raw() -> Self { pub fn new_raw() -> Self {
let mut engine = Engine { let mut engine = Engine {
functions: None, functions: None,
fn_lib: None,
type_iterators: None, type_iterators: None,
type_names: None, type_names: None,
on_print: None, on_print: None,
@ -435,8 +427,9 @@ impl Engine<'_> {
/// Universal method for calling functions either registered with the `Engine` or written in Rhai /// Universal method for calling functions either registered with the `Engine` or written in Rhai
pub(crate) fn call_fn_raw( pub(crate) fn call_fn_raw(
&mut self, &self,
scope: Option<&mut Scope>, scope: Option<&mut Scope>,
fn_lib: Option<&FunctionsLib>,
fn_name: &str, fn_name: &str,
args: &mut FnCallArgs, args: &mut FnCallArgs,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
@ -444,8 +437,8 @@ impl Engine<'_> {
level: usize, level: usize,
) -> Result<Dynamic, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
if let Some(fn_lib_arc) = &self.fn_lib { if let Some(lib) = fn_lib {
if let Some(fn_def) = fn_lib_arc.clone().get_function(fn_name, args.len()) { if let Some(fn_def) = lib.get_function(fn_name, args.len()) {
match scope { match scope {
// Extern scope passed in which is not empty // Extern scope passed in which is not empty
Some(scope) if scope.len() > 0 => { Some(scope) if scope.len() > 0 => {
@ -462,13 +455,13 @@ impl Engine<'_> {
); );
// Evaluate the function at one higher level of call depth // Evaluate the function at one higher level of call depth
let result = self.eval_stmt(scope, &fn_def.body, level + 1).or_else( let result = self
|err| match err { .eval_stmt(scope, fn_lib, &fn_def.body, level + 1)
.or_else(|err| match err {
// Convert return statement to return value // Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x), EvalAltResult::Return(x, _) => Ok(x),
err => Err(err.set_position(pos)), err => Err(err.set_position(pos)),
}, });
);
scope.rewind(scope_len); scope.rewind(scope_len);
@ -488,13 +481,13 @@ impl Engine<'_> {
); );
// Evaluate the function at one higher level of call depth // Evaluate the function at one higher level of call depth
return self.eval_stmt(&mut scope, &fn_def.body, level + 1).or_else( return self
|err| match err { .eval_stmt(&mut scope, fn_lib, &fn_def.body, level + 1)
.or_else(|err| match err {
// Convert return statement to return value // Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x), EvalAltResult::Return(x, _) => Ok(x),
err => Err(err.set_position(pos)), err => Err(err.set_position(pos)),
}, });
);
} }
} }
} }
@ -521,11 +514,11 @@ impl Engine<'_> {
// See if the function match print/debug (which requires special processing) // See if the function match print/debug (which requires special processing)
return Ok(match fn_name { return Ok(match fn_name {
KEYWORD_PRINT if self.on_print.is_some() => { 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() .into_dynamic()
} }
KEYWORD_DEBUG if self.on_debug.is_some() => { 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() .into_dynamic()
} }
KEYWORD_PRINT | KEYWORD_DEBUG => ().into_dynamic(), KEYWORD_PRINT | KEYWORD_DEBUG => ().into_dynamic(),
@ -590,8 +583,9 @@ impl Engine<'_> {
/// Chain-evaluate a dot setter. /// Chain-evaluate a dot setter.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn get_dot_val_helper( fn get_dot_val_helper(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
target: Target, target: Target,
dot_rhs: &Expr, dot_rhs: &Expr,
level: usize, level: usize,
@ -601,7 +595,7 @@ impl Engine<'_> {
Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => { Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => {
let mut values = arg_expr_list let mut values = arg_expr_list
.iter() .iter()
.map(|arg_expr| self.eval_expr(scope, arg_expr, level)) .map(|arg_expr| self.eval_expr(scope, fn_lib, arg_expr, level))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let this_ptr = target.get_mut(scope); let this_ptr = target.get_mut(scope);
@ -612,13 +606,13 @@ impl Engine<'_> {
let def_val = def_val.as_ref(); 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 // xxx.id
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let mut args = [target.get_mut(scope)]; 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] // xxx.idx_lhs[idx_expr]
@ -628,11 +622,11 @@ impl Engine<'_> {
// xxx.id[idx_expr] // xxx.id[idx_expr]
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let mut args = [target.get_mut(scope)]; 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] // xxx.???[???][idx_expr]
Expr::Index(_, _, _) => { 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 // 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) .map(|(val, _, _)| val)
} }
@ -652,9 +646,10 @@ impl Engine<'_> {
// xxx.id.rhs // xxx.id.rhs
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let mut args = [target.get_mut(scope)]; 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| { .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 // xxx.idx_lhs[idx_expr].rhs
@ -663,12 +658,13 @@ impl Engine<'_> {
let val = match idx_lhs.as_ref() { let val = match idx_lhs.as_ref() {
// xxx.id[idx_expr].rhs // xxx.id[idx_expr].rhs
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let fn_name = make_getter(id);
let mut args = [target.get_mut(scope)]; 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 // xxx.???[???][idx_expr].rhs
Expr::Index(_, _, _) => { 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 // 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, _, _)| { .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 // Syntax error
@ -702,8 +699,9 @@ impl Engine<'_> {
/// Evaluate a dot chain getter /// Evaluate a dot chain getter
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn get_dot_val( fn get_dot_val(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
dot_lhs: &Expr, dot_lhs: &Expr,
dot_rhs: &Expr, dot_rhs: &Expr,
level: usize, level: usize,
@ -718,16 +716,16 @@ impl Engine<'_> {
// This is a variable property access (potential function call). // This is a variable property access (potential function call).
// Use a direct index into `scope` to directly mutate the variable value. // 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].??? // idx_lhs[idx_expr].???
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, op_pos) => { Expr::Index(idx_lhs, idx_expr, op_pos) => {
let (idx_src_type, src, idx, mut val) = let (idx_src_type, src, idx, mut val) =
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)?;
let value = let target = Target::from(val.as_mut());
self.get_dot_val_helper(scope, Target::from(val.as_mut()), dot_rhs, level); 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. // In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
if let Some(src) = src { if let Some(src) = src {
@ -755,8 +753,8 @@ impl Engine<'_> {
// {expr}.??? // {expr}.???
expr => { expr => {
let mut val = self.eval_expr(scope, expr, level)?; let mut val = self.eval_expr(scope, fn_lib, expr, level)?;
self.get_dot_val_helper(scope, Target::from(val.as_mut()), dot_rhs, 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 /// Get the value at the indexed position of a base type
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
fn get_indexed_value( fn get_indexed_value(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
val: &Dynamic, val: &Dynamic,
idx_expr: &Expr, idx_expr: &Expr,
op_pos: Position, op_pos: Position,
@ -787,7 +786,7 @@ impl Engine<'_> {
// val_array[idx] // val_array[idx]
if let Some(arr) = val.downcast_ref::<Array>() { if let Some(arr) = val.downcast_ref::<Array>() {
let idx = self let idx = self
.eval_expr(scope, idx_expr, level)? .eval_expr(scope, fn_lib, idx_expr, level)?
.try_cast::<INT>() .try_cast::<INT>()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
@ -806,7 +805,7 @@ impl Engine<'_> {
// val_map[idx] // val_map[idx]
if let Some(map) = val.downcast_ref::<Map>() { if let Some(map) = val.downcast_ref::<Map>() {
let idx = self let idx = self
.eval_expr(scope, idx_expr, level)? .eval_expr(scope, fn_lib, idx_expr, level)?
.try_cast::<String>() .try_cast::<String>()
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?; .map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?;
@ -821,7 +820,7 @@ impl Engine<'_> {
// val_string[idx] // val_string[idx]
if let Some(s) = val.downcast_ref::<String>() { if let Some(s) = val.downcast_ref::<String>() {
let idx = self let idx = self
.eval_expr(scope, idx_expr, level)? .eval_expr(scope, fn_lib, idx_expr, level)?
.try_cast::<INT>() .try_cast::<INT>()
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
@ -857,8 +856,9 @@ impl Engine<'_> {
/// Evaluate an index expression /// Evaluate an index expression
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
fn eval_index_expr<'a>( fn eval_index_expr<'a>(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
lhs: &'a Expr, lhs: &'a Expr,
idx_expr: &Expr, idx_expr: &Expr,
op_pos: Position, op_pos: Position,
@ -885,7 +885,7 @@ impl Engine<'_> {
) = Self::search_scope(scope, &id, lhs.position())?; ) = Self::search_scope(scope, &id, lhs.position())?;
let (val, idx_src_type, idx) = 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(( Ok((
idx_src_type, idx_src_type,
@ -901,9 +901,9 @@ impl Engine<'_> {
// (expr)[idx_expr] // (expr)[idx_expr]
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)) .map(|(val, _, idx)| (IndexSourceType::Expression, None, idx, val))
} }
} }
@ -1002,8 +1002,9 @@ impl Engine<'_> {
/// Chain-evaluate a dot setter /// Chain-evaluate a dot setter
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn set_dot_val_helper( fn set_dot_val_helper(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
this_ptr: &mut Variant, this_ptr: &mut Variant,
dot_rhs: &Expr, dot_rhs: &Expr,
new_val: (&mut Dynamic, Position), new_val: (&mut Dynamic, Position),
@ -1013,7 +1014,7 @@ impl Engine<'_> {
// xxx.id // xxx.id
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let mut args = [this_ptr, new_val.0.as_mut()]; 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] // xxx.lhs[idx_expr]
@ -1021,18 +1022,21 @@ impl Engine<'_> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() { Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() {
// xxx.id[idx_expr] // xxx.id[idx_expr]
Expr::Property(id, pos) => self Expr::Property(id, pos) => {
.call_fn_raw(None, &make_getter(id), &mut [this_ptr], None, *pos, 0) let fn_name = make_getter(id);
.and_then(|val| { self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0)
let (_, _, idx) = .and_then(|val| {
self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)?; 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) Self::update_indexed_value(val, idx, new_val.0.clone(), new_val.1)
}) })
.and_then(|mut val| { .and_then(|mut val| {
let mut args = [this_ptr, val.as_mut()]; let fn_name = make_setter(id);
self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) 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 // All others - syntax error for setters chain
_ => Err(EvalAltResult::ErrorDotExpr( _ => Err(EvalAltResult::ErrorDotExpr(
@ -1045,14 +1049,17 @@ impl Engine<'_> {
Expr::Dot(lhs, rhs, _) => match lhs.as_ref() { Expr::Dot(lhs, rhs, _) => match lhs.as_ref() {
// xxx.id.rhs // xxx.id.rhs
Expr::Property(id, pos) => { 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| { .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 .map(|_| val) // Discard Ok return value
}) })
.and_then(|mut val| { .and_then(|mut val| {
let fn_name = make_setter(id);
let mut args = [this_ptr, val.as_mut()]; 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() { Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() {
// xxx.id[idx_expr].rhs // xxx.id[idx_expr].rhs
Expr::Property(id, pos) => { 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| { .and_then(|v| {
let (mut value, _, idx) = let (mut value, _, idx) = self.get_indexed_value(
self.get_indexed_value(scope, &v, idx_expr, *op_pos, level)?; scope, fn_lib, &v, idx_expr, *op_pos, level,
)?;
let val_pos = new_val.1; let val_pos = new_val.1;
let this_ptr = value.as_mut(); 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. // 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) Self::update_indexed_value(v, idx, value, val_pos)
}) })
.and_then(|mut v| { .and_then(|mut v| {
let fn_name = make_setter(id);
let mut args = [this_ptr, v.as_mut()]; 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 // Evaluate a dot chain setter
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn set_dot_val( fn set_dot_val(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
dot_lhs: &Expr, dot_lhs: &Expr,
dot_rhs: &Expr, dot_rhs: &Expr,
new_val: (&mut Dynamic, Position), new_val: (&mut Dynamic, Position),
@ -1127,8 +1140,8 @@ impl Engine<'_> {
// Avoid referencing scope which is used below as mut // Avoid referencing scope which is used below as mut
let entry = ScopeSource { name: id, ..entry }; let entry = ScopeSource { name: id, ..entry };
let this_ptr = target.as_mut(); let this_ptr = target.as_mut();
let value = let value = self
self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level); .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. // In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
*scope.get_mut(entry) = target; *scope.get_mut(entry) = target;
@ -1143,10 +1156,11 @@ impl Engine<'_> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, op_pos) => { Expr::Index(lhs, idx_expr, op_pos) => {
let (idx_src_type, src, idx, mut target) = 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 val_pos = new_val.1;
let this_ptr = target.as_mut(); 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. // In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
if let Some(src) = src { if let Some(src) = src {
@ -1182,14 +1196,15 @@ impl Engine<'_> {
// Evaluate an 'in' expression // Evaluate an 'in' expression
fn eval_in_expr( fn eval_in_expr(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
lhs: &Expr, lhs: &Expr,
rhs: &Expr, rhs: &Expr,
level: usize, level: usize,
) -> Result<Dynamic, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
let mut lhs_value = self.eval_expr(scope, lhs, level)?; let mut lhs_value = self.eval_expr(scope, fn_lib, lhs, level)?;
let rhs_value = self.eval_expr(scope, rhs, level)?; let rhs_value = self.eval_expr(scope, fn_lib, rhs, level)?;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
@ -1200,15 +1215,10 @@ impl Engine<'_> {
// Call the '==' operator to compare each value // Call the '==' operator to compare each value
for value in rhs_value.iter_mut() { 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 if self
.call_fn_raw( .call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)?
None,
"==",
&mut [lhs_value.as_mut(), value.as_mut()],
Some(&def_value),
rhs.position(),
level,
)?
.try_cast::<bool>() .try_cast::<bool>()
.unwrap_or(false) .unwrap_or(false)
{ {
@ -1261,8 +1271,9 @@ impl Engine<'_> {
/// Evaluate an expression /// Evaluate an expression
fn eval_expr( fn eval_expr(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
expr: &Expr, expr: &Expr,
level: usize, level: usize,
) -> Result<Dynamic, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
@ -1279,15 +1290,15 @@ impl Engine<'_> {
// lhs[idx_expr] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, op_pos) => self 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), .map(|(_, _, _, x)| x),
// Statement block // Statement block
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt, level), Expr::Stmt(stmt, _) => self.eval_stmt(scope, fn_lib, stmt, level),
// lhs = rhs // lhs = rhs
Expr::Assignment(lhs, rhs, op_pos) => { 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() { match lhs.as_ref() {
// name = rhs // name = rhs
@ -1324,7 +1335,7 @@ impl Engine<'_> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, op_pos) => { Expr::Index(idx_lhs, idx_expr, op_pos) => {
let (idx_src_type, src, idx, _) = 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 { if let Some(src) = src {
match src.typ { match src.typ {
@ -1351,14 +1362,10 @@ impl Engine<'_> {
// dot_lhs.dot_rhs = rhs // dot_lhs.dot_rhs = rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(dot_lhs, dot_rhs, _) => self.set_dot_val( Expr::Dot(dot_lhs, dot_rhs, _) => {
scope, let new_val = (&mut rhs_val, rhs.position());
dot_lhs, self.set_dot_val(scope, fn_lib, dot_lhs, dot_rhs, new_val, *op_pos, level)
dot_rhs, }
(&mut rhs_val, rhs.position()),
*op_pos,
level,
),
// Error assignment to constant // Error assignment to constant
expr if expr.is_constant() => Err(EvalAltResult::ErrorAssignmentToConstant( expr if expr.is_constant() => Err(EvalAltResult::ErrorAssignmentToConstant(
@ -1372,14 +1379,15 @@ impl Engine<'_> {
} }
#[cfg(not(feature = "no_object"))] #[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"))] #[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => { Expr::Array(contents, _) => {
let mut arr = Array::new(); let mut arr = Array::new();
contents.into_iter().try_for_each(|item| { 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()) Ok((arr).into_dynamic())
@ -1390,7 +1398,7 @@ impl Engine<'_> {
let mut map = Map::new(); let mut map = Map::new();
contents.into_iter().try_for_each(|item| { 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); map.insert(item.0.clone(), val);
}) })
})?; })?;
@ -1400,14 +1408,17 @@ impl Engine<'_> {
Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => { Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => {
// Has a system function an override? // Has a system function an override?
fn has_override(engine: &Engine, name: &str) -> bool { fn has_override(
(engine.functions.is_some() && { engine: &Engine,
engine.functions.as_ref().unwrap().contains_key(&FnSpec { fn_lib: Option<&FunctionsLib>,
name: &str,
) -> bool {
engine.functions.as_ref().map_or(false, |lib| {
lib.contains_key(&FnSpec {
name: name.into(), name: name.into(),
args: vec![TypeId::of::<String>()], args: vec![TypeId::of::<String>()],
}) })
}) || (engine.fn_lib.is_some() }) || fn_lib.map_or(false, |lib| lib.has_function(name, 1))
&& engine.fn_lib.as_ref().unwrap().has_function(name, 1))
} }
match fn_name.as_ref() { match fn_name.as_ref() {
@ -1429,14 +1440,15 @@ impl Engine<'_> {
// Redirect call to `print` // Redirect call to `print`
let mut args = [result.as_mut()]; 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 // type_of
KEYWORD_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 Ok(self
.map_type_name((*r).type_name()) .map_type_name((*r).type_name())
.to_string() .to_string()
@ -1445,10 +1457,11 @@ impl Engine<'_> {
// eval // eval
KEYWORD_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 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 // Get the script text by evaluating the expression
let script = let script =
@ -1462,81 +1475,59 @@ impl Engine<'_> {
})?; })?;
// Compile the script text // Compile the script text
#[cfg(not(feature = "no_optimize"))] let mut ast = self.compile(script).map_err(EvalAltResult::ErrorParsing)?;
let ast = {
let orig_optimization_level = self.optimization_level;
self.set_optimization_level(OptimizationLevel::None); // If new functions are defined within the eval string, it is an error
let ast = self.compile(script); if ast.1.len() > 0 {
self.set_optimization_level(orig_optimization_level); return Err(EvalAltResult::ErrorParsing(
ParseErrorType::WrongFnDefinition.into_err(pos),
));
}
ast.map_err(EvalAltResult::ErrorParsing)? if let Some(lib) = fn_lib {
}; #[cfg(feature = "sync")]
{
#[cfg(feature = "no_optimize")] ast.1 = Arc::new(lib.clone());
let ast = self.compile(script).map_err(EvalAltResult::ErrorParsing)?; }
#[cfg(not(feature = "sync"))]
// If new functions are defined, merge it into the current functions library {
let merged = AST( ast.1 = Rc::new(lib.clone());
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
},
);
// Evaluate the AST // Evaluate the AST
let result = self self.eval_ast_with_scope_raw(scope, &ast)
.eval_ast_with_scope_raw(scope, &merged) .map_err(|err| err.set_position(pos))
.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?)
} }
// Normal function call // Normal function call
_ => { _ => {
let mut values = args_expr_list let mut arg_values = args_expr_list
.iter() .iter()
.map(|expr| self.eval_expr(scope, expr, level)) .map(|expr| self.eval_expr(scope, fn_lib, expr, level))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let mut arg_values: Vec<_> = let mut args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect();
values.iter_mut().map(Dynamic::as_mut).collect();
let def_val = def_val.as_ref(); let def_val = def_val.as_ref();
self.call_fn_raw(None, fn_lib, fn_name, &mut args, def_val, *pos, level)
self.call_fn_raw(None, fn_name, &mut arg_values, 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( Expr::And(lhs, rhs, _) => Ok(Box::new(
self self
.eval_expr(scope, lhs.as_ref(), level)? .eval_expr(scope, fn_lib,lhs.as_ref(), level)?
.try_cast::<bool>() .try_cast::<bool>()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position()) EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
})? })?
&& // Short-circuit using && && // Short-circuit using &&
self self
.eval_expr(scope, rhs.as_ref(), level)? .eval_expr(scope, fn_lib,rhs.as_ref(), level)?
.try_cast::<bool>() .try_cast::<bool>()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position()) EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
@ -1545,14 +1536,14 @@ impl Engine<'_> {
Expr::Or(lhs, rhs, _) => Ok(Box::new( Expr::Or(lhs, rhs, _) => Ok(Box::new(
self self
.eval_expr(scope, lhs.as_ref(), level)? .eval_expr(scope,fn_lib, lhs.as_ref(), level)?
.try_cast::<bool>() .try_cast::<bool>()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position()) EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
})? })?
|| // Short-circuit using || || // Short-circuit using ||
self self
.eval_expr(scope, rhs.as_ref(), level)? .eval_expr(scope,fn_lib, rhs.as_ref(), level)?
.try_cast::<bool>() .try_cast::<bool>()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
@ -1567,8 +1558,9 @@ impl Engine<'_> {
/// Evaluate a statement /// Evaluate a statement
pub(crate) fn eval_stmt( pub(crate) fn eval_stmt(
&mut self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: Option<&FunctionsLib>,
stmt: &Stmt, stmt: &Stmt,
level: usize, level: usize,
) -> Result<Dynamic, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
@ -1578,7 +1570,7 @@ impl Engine<'_> {
// Expression as statement // Expression as statement
Stmt::Expr(expr) => { 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(_, _, _)) { Ok(if !matches!(expr.as_ref(), Expr::Assignment(_, _, _)) {
result result
@ -1593,7 +1585,7 @@ impl Engine<'_> {
let prev_len = scope.len(); let prev_len = scope.len();
let result = block.iter().try_fold(().into_dynamic(), |_, stmt| { 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); scope.rewind(prev_len);
@ -1603,14 +1595,14 @@ impl Engine<'_> {
// If-else statement // If-else statement
Stmt::IfThenElse(guard, if_body, else_body) => self Stmt::IfThenElse(guard, if_body, else_body) => self
.eval_expr(scope, guard, level)? .eval_expr(scope, fn_lib, guard, level)?
.try_cast::<bool>() .try_cast::<bool>()
.map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position())) .map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position()))
.and_then(|guard_val| { .and_then(|guard_val| {
if 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 { } 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 { } else {
Ok(().into_dynamic()) Ok(().into_dynamic())
} }
@ -1618,12 +1610,19 @@ impl Engine<'_> {
// While loop // While loop
Stmt::While(guard, body) => loop { Stmt::While(guard, body) => loop {
match self.eval_expr(scope, guard, level)?.try_cast::<bool>() { match self
Ok(guard_val) if guard_val => match self.eval_stmt(scope, body, level) { .eval_expr(scope, fn_lib, guard, level)?
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (), .try_cast::<bool>()
Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()), {
Err(x) => return Err(x), 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()), Ok(_) => return Ok(().into_dynamic()),
Err(_) => return Err(EvalAltResult::ErrorLogicGuard(guard.position())), Err(_) => return Err(EvalAltResult::ErrorLogicGuard(guard.position())),
} }
@ -1631,7 +1630,7 @@ impl Engine<'_> {
// Loop statement // Loop statement
Stmt::Loop(body) => loop { 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, _)) => (), Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()), Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()),
Err(x) => return Err(x), Err(x) => return Err(x),
@ -1640,7 +1639,7 @@ impl Engine<'_> {
// For loop // For loop
Stmt::For(name, expr, body) => { 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()); let tid = Any::type_id(arr.as_ref());
if let Some(type_iterators) = &self.type_iterators { if let Some(type_iterators) = &self.type_iterators {
@ -1658,7 +1657,7 @@ impl Engine<'_> {
for a in iter_fn(&arr) { for a in iter_fn(&arr) {
*scope.get_mut(entry) = a; *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, _)) => (), Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
Err(EvalAltResult::ErrorLoopBreak(true, _)) => break, Err(EvalAltResult::ErrorLoopBreak(true, _)) => break,
Err(x) => return Err(x), Err(x) => return Err(x),
@ -1688,7 +1687,7 @@ impl Engine<'_> {
// Return value // Return value
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(EvalAltResult::Return( 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, *pos,
)), )),
@ -1699,7 +1698,7 @@ impl Engine<'_> {
// Throw value // Throw value
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => { 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( Err(EvalAltResult::ErrorRuntime(
val.try_cast::<String>().unwrap_or_else(|_| "".to_string()), val.try_cast::<String>().unwrap_or_else(|_| "".to_string()),
*pos, *pos,
@ -1708,7 +1707,7 @@ impl Engine<'_> {
// Let statement // Let statement
Stmt::Let(name, Some(expr), _) => { 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? // TODO - avoid copying variable name in inner block?
scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false); scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false);
Ok(().into_dynamic()) Ok(().into_dynamic())
@ -1722,7 +1721,7 @@ impl Engine<'_> {
// Const statement // Const statement
Stmt::Const(name, expr, _) if expr.is_constant() => { 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? // TODO - avoid copying variable name in inner block?
scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true); scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true);
Ok(().into_dynamic()) Ok(().into_dynamic())
@ -1745,11 +1744,6 @@ impl Engine<'_> {
.unwrap_or(name) .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 /// Print/debug to stdout

View File

@ -29,7 +29,7 @@
//! //!
//! engine.register_fn("compute_something", compute_something); //! engine.register_fn("compute_something", compute_something);
//! //!
//! # #[cfg(not(feature = "no_std"))] //! # #[cfg(not(feature = "no_std"))]
//! assert_eq!(engine.eval_file::<bool>("my_script.rhai".into())?, true); //! assert_eq!(engine.eval_file::<bool>("my_script.rhai".into())?, true);
//! //!
//! Ok(()) //! Ok(())

View File

@ -39,15 +39,18 @@ struct State<'a> {
constants: Vec<(String, Expr)>, constants: Vec<(String, Expr)>,
/// An `Engine` instance for eager function evaluation. /// An `Engine` instance for eager function evaluation.
engine: &'a Engine<'a>, engine: &'a Engine<'a>,
/// Library of script-defined functions.
fn_lib: &'a [(&'a str, usize)],
} }
impl<'a> State<'a> { impl<'a> State<'a> {
/// Create a new State. /// 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 { Self {
changed: false, changed: false,
constants: vec![], constants: vec![],
engine, engine,
fn_lib,
} }
} }
/// Reset the state from dirty to clean. /// 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 && args.iter().all(|expr| expr.is_constant()) // all arguments are constants
=> { => {
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
if let Some(fn_lib_arc) = &state.engine.fn_lib { if state.fn_lib.iter().find(|(name, len)| name == &id && *len == args.len()).is_some() {
if fn_lib_arc.has_function(&id, args.len()) { // A script-defined function overrides the built-in function - do not make the call
// 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);
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(); 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<Stmt>, engine: &Engine<'a>, scope: &Scope) -> Vec<Stmt> { pub(crate) fn optimize<'a>(
statements: Vec<Stmt>,
engine: &Engine<'a>,
scope: &Scope,
fn_lib: &'a [(&'a str, usize)],
) -> Vec<Stmt> {
// If optimization level is None then skip optimizing // If optimization level is None then skip optimizing
if engine.optimization_level == OptimizationLevel::None { if engine.optimization_level == OptimizationLevel::None {
return statements; return statements;
} }
// Set up the state // 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 // Add constants from the scope into the state
scope scope
@ -635,7 +641,12 @@ pub fn optimize_into_ast(
statements: Vec<Stmt>, statements: Vec<Stmt>,
functions: Vec<FnDef>, functions: Vec<FnDef>,
) -> AST { ) -> 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 functions
.iter() .iter()
.cloned() .cloned()
@ -644,7 +655,7 @@ pub fn optimize_into_ast(
let pos = fn_def.body.position(); let pos = fn_def.body.position();
// Optimize the function body // 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 // {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { 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 { match engine.optimization_level {
OptimizationLevel::None => statements, OptimizationLevel::None => statements,
OptimizationLevel::Simple | OptimizationLevel::Full => { OptimizationLevel::Simple | OptimizationLevel::Full => {
optimize(statements, engine, &scope) optimize(statements, engine, &scope, &fn_lib)
} }
}, },
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
Arc::new(fn_lib), Arc::new(lib),
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
Rc::new(fn_lib), Rc::new(lib),
) )
} }

View File

@ -199,7 +199,7 @@ impl AST {
/// # { /// # {
/// use rhai::Engine; /// 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 ast1 = engine.compile(r#"fn foo(x) { 42 + x } foo(1)"#)?;
/// let ast2 = engine.compile(r#"fn foo(n) { "hello" + n } foo("!")"#)?; /// let ast2 = engine.compile(r#"fn foo(n) { "hello" + n } foo("!")"#)?;

View File

@ -16,6 +16,8 @@ use crate::stdlib::path::PathBuf;
/// Evaluation result. /// 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.
///
/// Currently, `EvalAltResult` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
#[derive(Debug)] #[derive(Debug)]
pub enum EvalAltResult { pub enum EvalAltResult {
/// Syntax error. /// Syntax error.

View File

@ -49,7 +49,7 @@ pub(crate) struct EntryRef<'a> {
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope}; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// let engine = Engine::new();
/// let mut my_scope = Scope::new(); /// let mut my_scope = Scope::new();
/// ///
/// my_scope.push("z", 40_i64); /// my_scope.push("z", 40_i64);

View File

@ -3,7 +3,7 @@ use rhai::{Array, Engine, EvalAltResult, RegisterFn, INT};
#[test] #[test]
fn test_arrays() -> Result<(), EvalAltResult> { fn test_arrays() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = [1, 2, 3]; x[1]")?, 2); assert_eq!(engine.eval::<INT>("let x = [1, 2, 3]; x[1]")?, 2);
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5); assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5);

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_binary_ops() -> Result<(), EvalAltResult> { fn test_binary_ops() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("10 % 4")?, 2); assert_eq!(engine.eval::<INT>("10 % 4")?, 2);
assert_eq!(engine.eval::<INT>("10 << 4")?, 160); assert_eq!(engine.eval::<INT>("10 << 4")?, 160);

View File

@ -2,14 +2,14 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_left_shift() -> Result<(), EvalAltResult> { fn test_left_shift() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("4 << 2")?, 16); assert_eq!(engine.eval::<INT>("4 << 2")?, 16);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_right_shift() -> Result<(), EvalAltResult> { fn test_right_shift() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("9 >> 1")?, 4); assert_eq!(engine.eval::<INT>("9 >> 1")?, 4);
Ok(()) Ok(())
} }

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult};
#[test] #[test]
fn test_bool_op1() -> Result<(), EvalAltResult> { fn test_bool_op1() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<bool>("true && (false || true)")?, true); assert_eq!(engine.eval::<bool>("true && (false || true)")?, true);
assert_eq!(engine.eval::<bool>("true & (false | true)")?, true); assert_eq!(engine.eval::<bool>("true & (false | true)")?, true);
@ -12,7 +12,7 @@ fn test_bool_op1() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_bool_op2() -> Result<(), EvalAltResult> { fn test_bool_op2() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<bool>("false && (false || true)")?, false); assert_eq!(engine.eval::<bool>("false && (false || true)")?, false);
assert_eq!(engine.eval::<bool>("false & (false | true)")?, false); assert_eq!(engine.eval::<bool>("false & (false | true)")?, false);
@ -22,7 +22,7 @@ fn test_bool_op2() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_bool_op3() -> Result<(), EvalAltResult> { fn test_bool_op3() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert!(engine.eval::<bool>("true && (false || 123)").is_err()); assert!(engine.eval::<bool>("true && (false || 123)").is_err());
assert_eq!(engine.eval::<bool>("true && (true || 123)")?, true); assert_eq!(engine.eval::<bool>("true && (true || 123)")?, true);
@ -34,7 +34,7 @@ fn test_bool_op3() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> { fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<bool>( engine.eval::<bool>(
@ -63,7 +63,7 @@ fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_bool_op_no_short_circuit1() { fn test_bool_op_no_short_circuit1() {
let mut engine = Engine::new(); let engine = Engine::new();
assert!(engine assert!(engine
.eval::<bool>( .eval::<bool>(
@ -78,7 +78,7 @@ fn test_bool_op_no_short_circuit1() {
#[test] #[test]
fn test_bool_op_no_short_circuit2() { fn test_bool_op_no_short_circuit2() {
let mut engine = Engine::new(); let engine = Engine::new();
assert!(engine assert!(engine
.eval::<bool>( .eval::<bool>(

View File

@ -20,7 +20,7 @@ fn test_fn() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_call_fn() -> Result<(), EvalAltResult> { fn test_call_fn() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
scope.push("foo", 42 as INT); scope.push("foo", 42 as INT);

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult};
#[test] #[test]
fn test_chars() -> Result<(), EvalAltResult> { fn test_chars() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<char>("'y'")?, 'y'); assert_eq!(engine.eval::<char>("'y'")?, 'y');
assert_eq!(engine.eval::<char>("'\\u2764'")?, '❤'); assert_eq!(engine.eval::<char>("'\\u2764'")?, '❤');

View File

@ -2,13 +2,13 @@ use rhai::{Engine, INT};
#[test] #[test]
fn test_comments() { fn test_comments() {
let mut engine = Engine::new(); let engine = Engine::new();
assert!(engine assert!(engine
.eval::<INT>("let x = 5; x // I am a single line comment, yay!") .eval::<INT>("let x = 5; x // I am a single line comment, yay!")
.is_ok()); .is_ok());
assert!(engine assert!(engine
.eval::<INT>("let /* I am a multiline comment, yay! */ x = 5; x") .eval::<INT>("let /* I am a multi-line comment, yay! */ x = 5; x")
.is_ok()); .is_ok());
} }

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_or_equals() -> Result<(), EvalAltResult> { fn test_or_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 16; x |= 74; x")?, 90); assert_eq!(engine.eval::<INT>("let x = 16; x |= 74; x")?, 90);
assert_eq!(engine.eval::<bool>("let x = true; x |= false; x")?, true); assert_eq!(engine.eval::<bool>("let x = true; x |= false; x")?, true);
@ -13,7 +13,7 @@ fn test_or_equals() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_and_equals() -> Result<(), EvalAltResult> { fn test_and_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 16; x &= 31; x")?, 16); assert_eq!(engine.eval::<INT>("let x = 16; x &= 31; x")?, 16);
assert_eq!(engine.eval::<bool>("let x = true; x &= false; x")?, false); assert_eq!(engine.eval::<bool>("let x = true; x &= false; x")?, false);
@ -25,42 +25,42 @@ fn test_and_equals() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_xor_equals() -> Result<(), EvalAltResult> { fn test_xor_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 90; x ^= 12; x")?, 86); assert_eq!(engine.eval::<INT>("let x = 90; x ^= 12; x")?, 86);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_multiply_equals() -> Result<(), EvalAltResult> { fn test_multiply_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 2; x *= 3; x")?, 6); assert_eq!(engine.eval::<INT>("let x = 2; x *= 3; x")?, 6);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_divide_equals() -> Result<(), EvalAltResult> { fn test_divide_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 6; x /= 2; x")?, 3); assert_eq!(engine.eval::<INT>("let x = 6; x /= 2; x")?, 3);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_left_shift_equals() -> Result<(), EvalAltResult> { fn test_left_shift_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 9; x >>=1; x")?, 4); assert_eq!(engine.eval::<INT>("let x = 9; x >>=1; x")?, 4);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_right_shift_equals() -> Result<(), EvalAltResult> { fn test_right_shift_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 4; x<<= 2; x")?, 16); assert_eq!(engine.eval::<INT>("let x = 4; x<<= 2; x")?, 16);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_modulo_equals() -> Result<(), EvalAltResult> { fn test_modulo_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 10; x %= 4; x")?, 2); assert_eq!(engine.eval::<INT>("let x = 10; x %= 4; x")?, 2);
Ok(()) Ok(())
} }

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_constant() -> Result<(), EvalAltResult> { fn test_constant() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("const x = 123; x")?, 123); assert_eq!(engine.eval::<INT>("const x = 123; x")?, 123);

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_decrement() -> Result<(), EvalAltResult> { fn test_decrement() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3); assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3);

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, Scope, INT};
#[test] #[test]
fn test_eval() -> Result<(), EvalAltResult> { fn test_eval() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
@ -19,7 +19,7 @@ fn test_eval() -> Result<(), EvalAltResult> {
#[test] #[test]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
fn test_eval_function() -> Result<(), EvalAltResult> { fn test_eval_function() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
assert_eq!( assert_eq!(
@ -62,7 +62,7 @@ fn test_eval_function() -> Result<(), EvalAltResult> {
#[test] #[test]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
fn test_eval_override() -> Result<(), EvalAltResult> { fn test_eval_override() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<String>( engine.eval::<String>(

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, Scope, INT};
#[test] #[test]
fn test_expressions() -> Result<(), EvalAltResult> { fn test_expressions() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
scope.push("x", 10 as INT); scope.push("x", 10 as INT);

View File

@ -5,7 +5,7 @@ const EPSILON: FLOAT = 0.000_000_000_1;
#[test] #[test]
fn test_float() -> Result<(), EvalAltResult> { fn test_float() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<bool>("let x = 0.0; let y = 1.0; x < y")?, engine.eval::<bool>("let x = 0.0; let y = 1.0; x < y")?,

View File

@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
#[test] #[test]
fn test_for_array() -> Result<(), EvalAltResult> { fn test_for_array() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let script = r" let script = r"
let sum1 = 0; let sum1 = 0;
@ -33,7 +33,7 @@ fn test_for_array() -> Result<(), EvalAltResult> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[test] #[test]
fn test_for_object() -> Result<(), EvalAltResult> { fn test_for_object() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let script = r#" let script = r#"
let sum = 0; let sum = 0;

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_if() -> Result<(), EvalAltResult> { fn test_if() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("if true { 55 }")?, 55); assert_eq!(engine.eval::<INT>("if true { 55 }")?, 55);
assert_eq!(engine.eval::<INT>("if false { 55 } else { 44 }")?, 44); assert_eq!(engine.eval::<INT>("if false { 55 } else { 44 }")?, 44);
@ -30,7 +30,7 @@ fn test_if() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_if_expr() -> Result<(), EvalAltResult> { fn test_if_expr() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_increment() -> Result<(), EvalAltResult> { fn test_increment() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 1; x += 2; x")?, 3); assert_eq!(engine.eval::<INT>("let x = 1; x += 2; x")?, 3);
assert_eq!( assert_eq!(

View File

@ -4,9 +4,12 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_internal_fn() -> Result<(), EvalAltResult> { fn test_internal_fn() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("fn addme(a, b) { a+b } addme(3, 4)")?, 7); assert_eq!(
engine.eval::<INT>("fn add_me(a, b) { a+b } add_me(3, 4)")?,
7
);
assert_eq!(engine.eval::<INT>("fn bob() { return 4; 5 } bob()")?, 4); assert_eq!(engine.eval::<INT>("fn bob() { return 4; 5 } bob()")?, 4);
Ok(()) Ok(())
@ -14,15 +17,15 @@ fn test_internal_fn() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_big_internal_fn() -> Result<(), EvalAltResult> { fn test_big_internal_fn() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" r"
fn mathme(a, b, c, d, e, f) { fn math_me(a, b, c, d, e, f) {
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 112
@ -33,7 +36,7 @@ fn test_big_internal_fn() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_internal_fn_overloading() -> Result<(), EvalAltResult> { fn test_internal_fn_overloading() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_loop() -> Result<(), EvalAltResult> { fn test_loop() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(

View File

@ -4,7 +4,7 @@ use rhai::{AnyExt, Engine, EvalAltResult, Map, INT};
#[test] #[test]
fn test_map_indexing() -> Result<(), EvalAltResult> { fn test_map_indexing() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
@ -75,7 +75,7 @@ fn test_map_indexing() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_map_assign() -> Result<(), EvalAltResult> { fn test_map_assign() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let x = engine.eval::<Map>(r#"let x = #{a: 1, b: true, "c$": "hello"}; x"#)?; let x = engine.eval::<Map>(r#"let x = #{a: 1, b: true, "c$": "hello"}; x"#)?;
let a = x.get("a").cloned().expect("should have property a"); let a = x.get("a").cloned().expect("should have property a");
@ -91,7 +91,7 @@ fn test_map_assign() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_map_return() -> Result<(), EvalAltResult> { fn test_map_return() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let x = engine.eval::<Map>(r#"#{a: 1, b: true, "c$": "hello"}"#)?; let x = engine.eval::<Map>(r#"#{a: 1, b: true, "c$": "hello"}"#)?;
let a = x.get("a").cloned().expect("should have property a"); let a = x.get("a").cloned().expect("should have property a");
@ -107,7 +107,7 @@ fn test_map_return() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_map_for() -> Result<(), EvalAltResult> { fn test_map_for() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_math() -> Result<(), EvalAltResult> { fn test_math() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("1 + 2")?, 3); assert_eq!(engine.eval::<INT>("1 + 2")?, 3);
assert_eq!(engine.eval::<INT>("1 - 2")?, -1); assert_eq!(engine.eval::<INT>("1 - 2")?, -1);

View File

@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, RegisterFn, INT};
#[test] #[test]
#[cfg(not(feature = "no_stdlib"))] #[cfg(not(feature = "no_stdlib"))]
fn test_mismatched_op() { fn test_mismatched_op() {
let mut engine = Engine::new(); let engine = Engine::new();
assert!( assert!(
matches!(engine.eval::<INT>(r#"60 + "hello""#).expect_err("expects error"), matches!(engine.eval::<INT>(r#"60 + "hello""#).expect_err("expects error"),

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult};
#[test] #[test]
fn test_not() -> Result<(), EvalAltResult> { fn test_not() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<bool>("let not_true = !true; not_true")?, engine.eval::<bool>("let not_true = !true; not_true")?,

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_number_literal() -> Result<(), EvalAltResult> { fn test_number_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("65")?, 65); assert_eq!(engine.eval::<INT>("65")?, 65);
@ -11,7 +11,7 @@ fn test_number_literal() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_hex_literal() -> Result<(), EvalAltResult> { fn test_hex_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 0xf; x")?, 15); assert_eq!(engine.eval::<INT>("let x = 0xf; x")?, 15);
assert_eq!(engine.eval::<INT>("let x = 0xff; x")?, 255); assert_eq!(engine.eval::<INT>("let x = 0xff; x")?, 255);
@ -21,7 +21,7 @@ fn test_hex_literal() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_octal_literal() -> Result<(), EvalAltResult> { fn test_octal_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 0o77; x")?, 63); assert_eq!(engine.eval::<INT>("let x = 0o77; x")?, 63);
assert_eq!(engine.eval::<INT>("let x = 0o1234; x")?, 668); assert_eq!(engine.eval::<INT>("let x = 0o1234; x")?, 668);
@ -31,7 +31,7 @@ fn test_octal_literal() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_binary_literal() -> Result<(), EvalAltResult> { fn test_binary_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 0b1111; x")?, 15); assert_eq!(engine.eval::<INT>("let x = 0b1111; x")?, 15);
assert_eq!( assert_eq!(

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_ops() -> Result<(), EvalAltResult> { fn test_ops() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("60 + 5")?, 65); assert_eq!(engine.eval::<INT>("60 + 5")?, 65);
assert_eq!(engine.eval::<INT>("(1 + 2) * (6 - 4) / 2")?, 3); assert_eq!(engine.eval::<INT>("(1 + 2) * (6 - 4) / 2")?, 3);
@ -11,8 +11,8 @@ fn test_ops() -> Result<(), EvalAltResult> {
} }
#[test] #[test]
fn test_op_prec() -> Result<(), EvalAltResult> { fn test_op_precedence() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>("let x = 0; if x == 10 || true { x = 1} x")?, engine.eval::<INT>("let x = 0; if x == 10 || true { x = 1} x")?,

View File

@ -8,7 +8,7 @@ const EPSILON: FLOAT = 0.000_000_000_1;
#[test] #[test]
fn test_power_of() -> Result<(), EvalAltResult> { fn test_power_of() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("2 ~ 3")?, 8); assert_eq!(engine.eval::<INT>("2 ~ 3")?, 8);
assert_eq!(engine.eval::<INT>("(-2 ~ 3)")?, -8); assert_eq!(engine.eval::<INT>("(-2 ~ 3)")?, -8);
@ -29,7 +29,7 @@ fn test_power_of() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_power_of_equals() -> Result<(), EvalAltResult> { fn test_power_of_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 2; x ~= 3; x")?, 8); assert_eq!(engine.eval::<INT>("let x = 2; x ~= 3; x")?, 8);
assert_eq!(engine.eval::<INT>("let x = -2; x ~= 3; x")?, -8); assert_eq!(engine.eval::<INT>("let x = -2; x ~= 3; x")?, -8);

View File

@ -1,5 +1,3 @@
#![cfg(not(feature = "no_object"))]
///! This test simulates an external command object that is driven by a script. ///! This test simulates an external command object that is driven by a script.
use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT}; use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -40,8 +38,9 @@ impl CommandWrapper {
} }
} }
#[cfg(not(feature = "no_object"))]
#[test] #[test]
fn test_side_effects() -> Result<(), EvalAltResult> { fn test_side_effects_command() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
@ -79,3 +78,22 @@ fn test_side_effects() -> Result<(), EvalAltResult> {
Ok(()) 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(())
}

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult};
#[test] #[test]
fn test_string() -> Result<(), EvalAltResult> { fn test_string() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<String>(r#""Test string: \u2764""#)?, engine.eval::<String>(r#""Test string: \u2764""#)?,

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult};
#[test] #[test]
fn test_throw() { fn test_throw() {
let mut engine = Engine::new(); let engine = Engine::new();
assert!(matches!( assert!(matches!(
engine.eval::<()>(r#"if true { throw "hello" }"#).expect_err("expects error"), engine.eval::<()>(r#"if true { throw "hello" }"#).expect_err("expects error"),

View File

@ -4,7 +4,7 @@ use rhai::{Engine, EvalAltResult, INT};
// TODO also add test case for unary after compound // TODO also add test case for unary after compound
// Hah, turns out unary + has a good use after all! // Hah, turns out unary + has a good use after all!
fn test_unary_after_binary() -> Result<(), EvalAltResult> { fn test_unary_after_binary() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("10 % +4")?, 2); assert_eq!(engine.eval::<INT>("10 % +4")?, 2);
assert_eq!(engine.eval::<INT>("10 << +4")?, 160); assert_eq!(engine.eval::<INT>("10 << +4")?, 160);

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_unary_minus() -> Result<(), EvalAltResult> { fn test_unary_minus() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = -5; x")?, -5); assert_eq!(engine.eval::<INT>("let x = -5; x")?, -5);

View File

@ -2,21 +2,21 @@ use rhai::{Engine, EvalAltResult};
#[test] #[test]
fn test_unit() -> Result<(), EvalAltResult> { fn test_unit() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
engine.eval::<()>("let x = (); x")?; engine.eval::<()>("let x = (); x")?;
Ok(()) Ok(())
} }
#[test] #[test]
fn test_unit_eq() -> Result<(), EvalAltResult> { fn test_unit_eq() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<bool>("let x = (); let y = (); x == y")?, true); assert_eq!(engine.eval::<bool>("let x = (); let y = (); x == y")?, true);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_unit_with_spaces() -> Result<(), EvalAltResult> { fn test_unit_with_spaces() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
engine.eval::<()>("let x = ( ); x")?; engine.eval::<()>("let x = ( ); x")?;
Ok(()) Ok(())
} }

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, Scope, INT};
#[test] #[test]
fn test_var_scope() -> Result<(), EvalAltResult> { fn test_var_scope() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?; engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?;
@ -21,7 +21,7 @@ fn test_var_scope() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_scope_eval() -> Result<(), EvalAltResult> { fn test_scope_eval() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
// First create the state // First create the state
let mut scope = Scope::new(); let mut scope = Scope::new();

View File

@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_while() -> Result<(), EvalAltResult> { fn test_while() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(