From 560da5fdc6baf0f47c61cce21001bb57f1776162 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 12 Mar 2020 14:54:14 +0800 Subject: [PATCH] Allow call_fn with only one parameter; consume can retain functions, eval cannot. --- README.md | 31 +++++--- examples/reuse_scope.rs | 4 +- examples/rhai_runner.rs | 2 +- src/api.rs | 168 ++++++++++++++++++---------------------- src/call.rs | 37 +++++++-- src/engine.rs | 31 ++++---- src/optimize.rs | 12 +-- src/parser.rs | 12 +-- src/scope.rs | 4 +- tests/engine.rs | 12 ++- tests/var_scope.rs | 19 ++--- 11 files changed, 176 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index 0a91272f..ffb34bf3 100644 --- a/README.md +++ b/README.md @@ -167,28 +167,35 @@ let ast = engine.compile_file("hello_world.rhai")?; ``` Rhai also allows you to work _backwards_ from the other direction - i.e. calling a Rhai-scripted function from Rust. -You do this via `call_fn`, which takes a compiled AST (output from `compile`) and the -function call arguments: +You do this via `call_fn`: ```rust use rhai::Engine; let mut engine = Engine::new(); -// Define a function in a script and compile to AST -let ast = engine.compile( +// Define a function in a script and load it into the Engine. +engine.consume( r" - fn hello(x, y) { // a function with two arguments: String and i64 - x.len() + y // returning i64 - } - ")?; + fn hello(x, y) { // a function with two parameters: String and i64 + x.len() + y // returning i64 + } -// Evaluate the function in the AST, passing arguments into the script as a tuple. + fn hello(x) { // script-functions can be overloaded: this one takes only one parameter + x * 2 // returning i64 + } + ", true)?; // pass true to 'retain_functions' otherwise these functions will be cleared + // at the end of consume() + +// Evaluate the function in the AST, passing arguments into the script as a tuple if there are more than one. // Beware, arguments must be of the correct types because Rhai does not have built-in type conversions. // If you pass in arguments of the wrong type, the Engine will not find the function. let result: i64 = engine.call_fn("hello", &ast, ( String::from("abc"), 123_i64 ) )?; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ put arguments in a tuple + +let result: i64 = engine.call_fn("hello", 123_i64)? +// ^^^^^^^ this one calls the 'hello' function with one parameter (so need for tuple) ``` Values and types @@ -318,7 +325,7 @@ fn main() } ``` -You can also see in this example how you can register multiple functions (or in this case multiple instances of the same function) to the same name in script. This gives you a way to overload functions the correct one, based on the types of the arguments, from your script. +You can also see in this example how you can register multiple functions (or in this case multiple instances of the same function) to the same name in script. This gives you a way to overload functions the correct one, based on the types of the parameters, from your script. Fallible functions ------------------ @@ -975,8 +982,8 @@ fn add(x, y) { print(add(2, 3)); ``` -Functions defined in script always take `Dynamic` arguments (i.e. the arguments can be of any type). -It is important to remember that all arguments are passed by _value_, so all functions are _pure_ (i.e. they never modify their arguments). +Functions defined in script always take `Dynamic` parameters (i.e. the parameter can be of any type). +It is important to remember that all parameters are passed by _value_, so all functions are _pure_ (i.e. they never modify their parameters). Any update to an argument will **not** be reflected back to the caller. This can introduce subtle bugs, if you are not careful. ```rust diff --git a/examples/reuse_scope.rs b/examples/reuse_scope.rs index c83d0a72..a6509ffe 100644 --- a/examples/reuse_scope.rs +++ b/examples/reuse_scope.rs @@ -4,9 +4,9 @@ fn main() -> Result<(), EvalAltResult> { let mut engine = Engine::new(); let mut scope = Scope::new(); - engine.eval_with_scope::<()>(&mut scope, false, "let x = 4 + 5")?; + engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?; - let result = engine.eval_with_scope::(&mut scope, false, "x")?; + let result = engine.eval_with_scope::(&mut scope, "x")?; println!("result: {}", result); diff --git a/examples/rhai_runner.rs b/examples/rhai_runner.rs index d535f078..2955c76e 100644 --- a/examples/rhai_runner.rs +++ b/examples/rhai_runner.rs @@ -67,7 +67,7 @@ fn main() { _ => (), } - if let Err(err) = engine.consume(&contents) { + if let Err(err) = engine.consume(&contents, false) { eprintln!("{}", padding("=", filename.len())); eprintln!("{}", filename); eprintln!("{}", padding("=", filename.len())); diff --git a/src/api.rs b/src/api.rs index 31416fbb..f199643d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -2,16 +2,17 @@ use crate::any::{Any, AnyExt, Dynamic}; use crate::call::FuncArgs; -use crate::engine::{Engine, FnAny, FnCallArgs, FnSpec}; +use crate::engine::{Engine, FnAny, FnSpec}; use crate::error::ParseError; use crate::fn_register::RegisterFn; -use crate::parser::{lex, parse, Position, AST}; +use crate::parser::{lex, parse, FnDef, Position, AST}; use crate::result::EvalAltResult; use crate::scope::Scope; use std::{ any::{type_name, TypeId}, fs::File, io::prelude::*, + sync::Arc, }; impl<'e> Engine<'e> { @@ -99,8 +100,8 @@ impl<'e> Engine<'e> { /// Compile a string into an AST. pub fn compile(&self, input: &str) -> Result { - let tokens = lex(input); - parse(&mut tokens.peekable(), self.optimize) + let tokens_stream = lex(input); + parse(&mut tokens_stream.peekable(), self.optimize) } fn read_file(filename: &str) -> Result { @@ -128,62 +129,45 @@ impl<'e> Engine<'e> { /// Evaluate a string. pub fn eval(&mut self, input: &str) -> Result { let mut scope = Scope::new(); - self.eval_with_scope(&mut scope, false, input) + self.eval_with_scope(&mut scope, input) } /// Evaluate a string with own scope. - /// - /// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ - /// and not cleared from run to run. pub fn eval_with_scope( &mut self, scope: &mut Scope, - retain_functions: bool, input: &str, ) -> Result { let ast = self.compile(input).map_err(EvalAltResult::ErrorParsing)?; - self.eval_ast_with_scope(scope, retain_functions, &ast) + self.eval_ast_with_scope(scope, &ast) } /// Evaluate an AST. pub fn eval_ast(&mut self, ast: &AST) -> Result { let mut scope = Scope::new(); - self.eval_ast_with_scope(&mut scope, false, ast) + self.eval_ast_with_scope(&mut scope, ast) } /// Evaluate an AST with own scope. - /// - /// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ - /// and not cleared from run to run. pub fn eval_ast_with_scope( &mut self, scope: &mut Scope, - retain_functions: bool, ast: &AST, ) -> Result { fn eval_ast_internal( engine: &mut Engine, scope: &mut Scope, - retain_functions: bool, ast: &AST, ) -> Result { + engine.clear_functions(); + #[cfg(feature = "no_function")] let AST(statements) = ast; #[cfg(not(feature = "no_function"))] let statements = { let AST(statements, functions) = ast; - - for f in functions { - match engine - .script_functions - .binary_search_by(|fn_def| fn_def.compare(&f.name, f.params.len())) - { - Ok(n) => engine.script_functions[n] = f.clone(), - Err(n) => engine.script_functions.insert(n, f.clone()), - } - } - + engine.load_script_functions(functions); statements }; @@ -191,14 +175,12 @@ impl<'e> Engine<'e> { .iter() .try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt)); - if !retain_functions { - engine.clear_functions(); - } + engine.clear_functions(); result } - match eval_ast_internal(self, scope, retain_functions, ast) { + match eval_ast_internal(self, scope, ast) { Err(EvalAltResult::Return(out, pos)) => out.downcast::().map(|v| *v).map_err(|a| { EvalAltResult::ErrorMismatchOutputType( self.map_type_name((*a).type_name()).to_string(), @@ -219,7 +201,14 @@ impl<'e> Engine<'e> { /// 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. - pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> { + /// + /// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ + /// and not cleared from run to run. + pub fn consume_file( + &mut self, + filename: &str, + retain_functions: bool, + ) -> Result<(), EvalAltResult> { let mut f = File::open(filename) .map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?; @@ -227,13 +216,16 @@ impl<'e> Engine<'e> { f.read_to_string(&mut contents) .map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err)) - .and_then(|_| self.consume(&contents)) + .and_then(|_| self.consume(&contents, retain_functions)) } /// Evaluate a string, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. - pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> { - self.consume_with_scope(&mut Scope::new(), false, input) + /// + /// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ + /// and not cleared from run to run. + pub fn consume(&mut self, input: &str, retain_functions: bool) -> Result<(), EvalAltResult> { + self.consume_with_scope(&mut Scope::new(), retain_functions, input) } /// Evaluate a string, but throw away the result and only return error (if any). @@ -247,45 +239,54 @@ impl<'e> Engine<'e> { retain_functions: bool, input: &str, ) -> Result<(), EvalAltResult> { - let tokens = lex(input); + let tokens_stream = lex(input); - parse(&mut tokens.peekable(), self.optimize) - .map_err(|err| EvalAltResult::ErrorParsing(err)) - .and_then(|ast| { - #[cfg(feature = "no_function")] - let AST(statements) = ast; + let ast = parse(&mut tokens_stream.peekable(), self.optimize) + .map_err(EvalAltResult::ErrorParsing)?; - #[cfg(not(feature = "no_function"))] - let statements = { - let AST(ref statements, ref functions) = ast; + if !retain_functions { + self.clear_functions(); + } - for f in functions { - match self - .script_functions - .binary_search_by(|fn_def| fn_def.compare(&f.name, f.params.len())) - { - Ok(n) => self.script_functions[n] = f.clone(), - Err(n) => self.script_functions.insert(n, f.clone()), - } - } + #[cfg(feature = "no_function")] + let AST(statements) = ast; - statements - }; + #[cfg(not(feature = "no_function"))] + let statements = { + let AST(ref statements, ref functions) = ast; + self.load_script_functions(functions); + statements + }; - let val = statements - .iter() - .try_fold(().into_dynamic(), |_, o| self.eval_stmt(scope, o)) - .map(|_| ()); + let result = statements + .iter() + .try_fold(().into_dynamic(), |_, o| self.eval_stmt(scope, o)) + .map(|_| ()); - if !retain_functions { - self.clear_functions(); - } + if !retain_functions { + self.clear_functions(); + } - val - }) + result } - /// Call a script function defined in a compiled AST. + /// Load a list of functions into the Engine. + pub(crate) fn load_script_functions<'a>( + &mut self, + functions: impl IntoIterator>, + ) { + for f in functions.into_iter() { + match self + .script_functions + .binary_search_by(|fn_def| fn_def.compare(&f.name, f.params.len())) + { + Ok(n) => self.script_functions[n] = f.clone(), + Err(n) => self.script_functions.insert(n, f.clone()), + } + } + } + + /// Call a script function retained inside the Engine. /// /// # Example /// @@ -298,9 +299,9 @@ impl<'e> Engine<'e> { /// /// let mut engine = Engine::new(); /// - /// let ast = engine.compile("fn add(x, y) { x.len() + y }")?; + /// engine.consume("fn add(x, y) { x.len() + y }", true)?; /// - /// let result: i64 = engine.call_fn("add", &ast, (String::from("abc"), 123_i64))?; + /// let result: i64 = engine.call_fn("add", (String::from("abc"), 123_i64))?; /// /// assert_eq!(result, 126); /// # } @@ -311,41 +312,22 @@ impl<'e> Engine<'e> { pub fn call_fn( &mut self, name: &str, - ast: &AST, args: A, ) -> Result { + // Split out non-generic portion to avoid exploding code size fn call_fn_internal( engine: &mut Engine, name: &str, - ast: &AST, - args: FnCallArgs, + mut values: Vec, ) -> Result { - for f in &ast.1 { - match engine - .script_functions - .binary_search_by(|fn_def| fn_def.compare(&f.name, f.params.len())) - { - Ok(n) => engine.script_functions[n] = f.clone(), - Err(n) => engine.script_functions.insert(n, f.clone()), - } - } + let values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect(); - let result = engine.call_fn_raw(name, args, None, Position::none()); - - engine.clear_functions(); + let result = engine.call_fn_raw(name, values, None, Position::none()); result } - let mut arg_values = args.into_vec(); - - call_fn_internal( - self, - name, - ast, - arg_values.iter_mut().map(|v| v.as_mut()).collect(), - ) - .and_then(|b| { + call_fn_internal(self, name, args.into_vec()).and_then(|b| { b.downcast().map(|b| *b).map_err(|a| { EvalAltResult::ErrorMismatchOutputType( self.map_type_name((*a).type_name()).into(), @@ -369,7 +351,7 @@ impl<'e> Engine<'e> { /// /// // Override action of 'print' function /// engine.on_print(|s| result.push_str(s)); - /// engine.consume("print(40 + 2);")?; + /// engine.consume("print(40 + 2);", false)?; /// } /// assert_eq!(result, "42"); /// # Ok(()) @@ -393,7 +375,7 @@ impl<'e> Engine<'e> { /// /// // Override action of 'debug' function /// engine.on_debug(|s| result.push_str(s)); - /// engine.consume(r#"debug("hello");"#)?; + /// engine.consume(r#"debug("hello");"#, false)?; /// } /// assert_eq!(result, "\"hello\""); /// # Ok(()) diff --git a/src/call.rs b/src/call.rs index f9faf563..9a2252c0 100644 --- a/src/call.rs +++ b/src/call.rs @@ -1,6 +1,7 @@ //! Helper module which defines `FnArgs` to make function calling easier. use crate::any::{Any, Dynamic}; +use crate::engine::Array; /// Trait that represent arguments to a function call. pub trait FuncArgs { @@ -8,12 +9,36 @@ pub trait FuncArgs { fn into_vec(self) -> Vec; } -impl FuncArgs for &mut Vec { - fn into_vec(self) -> Vec { - self.iter_mut().map(|x| x.into_dynamic()).collect() - } +macro_rules! impl_std_args { + ($($p:ty),*) => { + $( + impl FuncArgs for $p { + fn into_vec(self) -> Vec { + vec![self.into_dynamic()] + } + } + )* + }; } +impl_std_args!(String, char, bool); + +#[cfg(not(feature = "no_index"))] +impl_std_args!(Array); + +#[cfg(not(feature = "only_i32"))] +#[cfg(not(feature = "only_i64"))] +impl_std_args!(u8, i8, u16, i16, u32, i32, u64, i64); + +#[cfg(feature = "only_i32")] +impl_std_args!(i32); + +#[cfg(feature = "only_i64")] +impl_std_args!(i64); + +#[cfg(not(feature = "no_float"))] +impl_std_args!(f32, f64); + macro_rules! impl_args { ($($p:ident),*) => { impl<$($p: Any + Clone),*> FuncArgs for ($($p,)*) @@ -33,7 +58,9 @@ macro_rules! impl_args { }; (@pop) => { }; - (@pop $head:ident $(, $tail:ident)*) => { + (@pop $head:ident) => { + }; + (@pop $head:ident $(, $tail:ident)+) => { impl_args!($($tail),*); }; } diff --git a/src/engine.rs b/src/engine.rs index 8bfc4eb9..f3fa3ad4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -139,19 +139,20 @@ impl Engine<'_> { .script_functions .binary_search_by(|f| f.compare(fn_name, args.len())) { - let func = self.script_functions[n].clone(); - let mut scope = Scope::new(); + let fn_def = self.script_functions[n].clone(); + scope.extend( // Put arguments into scope as variables - func.params + fn_def + .params .iter() .zip(args.iter().map(|x| (*x).into_dynamic())), ); // Evaluate - return match self.eval_stmt(&mut scope, &func.body) { + return match self.eval_stmt(&mut scope, &fn_def.body) { // Convert return statement to return value Err(EvalAltResult::Return(x, _)) => Ok(x), other => other, @@ -240,14 +241,14 @@ impl Engine<'_> { ) -> Result { match dot_rhs { // xxx.fn_name(args) - Expr::FunctionCall(fn_name, args, def_val, pos) => { - let mut args = args + Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => { + let mut values = arg_expr_list .iter() - .map(|arg| self.eval_expr(scope, arg)) + .map(|arg_expr| self.eval_expr(scope, arg_expr)) .collect::, _>>()?; let args = once(this_ptr) - .chain(args.iter_mut().map(|b| b.as_mut())) + .chain(values.iter_mut().map(|b| b.as_mut())) .collect(); self.call_fn_raw(fn_name, args, def_val.as_ref(), *pos) @@ -837,15 +838,15 @@ impl Engine<'_> { Expr::Array(_, _) => panic!("encountered an array during no_index!"), // Dump AST - Expr::FunctionCall(fn_name, args, _, pos) if fn_name == KEYWORD_DUMP_AST => { - let pos = if args.len() == 0 { + Expr::FunctionCall(fn_name, args_expr_list, _, pos) if fn_name == KEYWORD_DUMP_AST => { + let pos = if args_expr_list.len() == 0 { *pos } else { - args[0].position() + args_expr_list[0].position() }; // Change the argument to a debug dump of the expressions - let result = args + let result = args_expr_list .into_iter() .map(|expr| format!("{:#?}", expr)) .collect::>() @@ -861,15 +862,15 @@ impl Engine<'_> { } // Normal function call - Expr::FunctionCall(fn_name, args, def_val, pos) => { - let mut args = args + Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => { + let mut values = args_expr_list .iter() .map(|expr| self.eval_expr(scope, expr)) .collect::, _>>()?; self.call_fn_raw( fn_name, - args.iter_mut().map(|b| b.as_mut()).collect(), + values.iter_mut().map(|b| b.as_mut()).collect(), def_val.as_ref(), *pos, ) diff --git a/src/optimize.rs b/src/optimize.rs index 20975d29..ab8d863b 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -50,7 +50,7 @@ fn optimize_stmt(stmt: Stmt, changed: &mut bool, preserve_result: bool) -> Stmt Stmt::Let(_, None, _) => stmt, Stmt::Block(statements, pos) => { - let original_len = statements.len(); + let orig_len = statements.len(); let mut result: Vec<_> = statements .into_iter() // For each statement @@ -105,7 +105,7 @@ fn optimize_stmt(stmt: Stmt, changed: &mut bool, preserve_result: bool) -> Stmt .collect(); } - *changed = *changed || original_len != result.len(); + *changed = *changed || orig_len != result.len(); match result[..] { // No statements in block - change to No-op @@ -177,14 +177,14 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr { #[cfg(not(feature = "no_index"))] Expr::Array(items, pos) => { - let original_len = items.len(); + let orig_len = items.len(); let items: Vec<_> = items .into_iter() .map(|expr| optimize_expr(expr, changed)) .collect(); - *changed = *changed || original_len != items.len(); + *changed = *changed || orig_len != items.len(); Expr::Array(items, pos) } @@ -233,14 +233,14 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr { Expr::FunctionCall(id, args, def_value, pos) } Expr::FunctionCall(id, args, def_value, pos) => { - let original_len = args.len(); + let orig_len = args.len(); let args: Vec<_> = args .into_iter() .map(|a| optimize_expr(a, changed)) .collect(); - *changed = *changed || original_len != args.len(); + *changed = *changed || orig_len != args.len(); Expr::FunctionCall(id, args, def_value, pos) } diff --git a/src/parser.rs b/src/parser.rs index 613d205a..db2da373 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1212,26 +1212,26 @@ fn parse_call_expr<'a>( input: &mut Peekable>, begin: Position, ) -> Result { - let mut args = Vec::new(); + let mut args_expr_list = Vec::new(); if let Some(&(Token::RightParen, _)) = input.peek() { input.next(); - return Ok(Expr::FunctionCall(id, args, None, begin)); + return Ok(Expr::FunctionCall(id, args_expr_list, None, begin)); } loop { - args.push(parse_expr(input)?); + args_expr_list.push(parse_expr(input)?); match input.peek() { Some(&(Token::RightParen, _)) => { input.next(); - return Ok(Expr::FunctionCall(id, args, None, begin)); + return Ok(Expr::FunctionCall(id, args_expr_list, None, begin)); } Some(&(Token::Comma, _)) => (), Some(&(_, pos)) => { return Err(ParseError::new( PERR::MissingRightParen(format!( - "closing the arguments list to function call of '{}'", + "closing the parameters list to function call of '{}'", id )), pos, @@ -1240,7 +1240,7 @@ fn parse_call_expr<'a>( None => { return Err(ParseError::new( PERR::MissingRightParen(format!( - "closing the arguments list to function call of '{}'", + "closing the parameters list to function call of '{}'", id )), Position::eof(), diff --git a/src/scope.rs b/src/scope.rs index ca3ec4d9..cf726d39 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -16,9 +16,9 @@ use std::borrow::Cow; /// let mut engine = Engine::new(); /// let mut my_scope = Scope::new(); /// -/// engine.eval_with_scope::<()>(&mut my_scope, false, "let x = 5;")?; +/// engine.eval_with_scope::<()>(&mut my_scope, "let x = 5;")?; /// -/// assert_eq!(engine.eval_with_scope::(&mut my_scope, false, "x + 1")?, 6); +/// assert_eq!(engine.eval_with_scope::(&mut my_scope, "x + 1")?, 6); /// # Ok(()) /// # } /// ``` diff --git a/tests/engine.rs b/tests/engine.rs index d795b35b..cd9f7fb0 100644 --- a/tests/engine.rs +++ b/tests/engine.rs @@ -6,17 +6,23 @@ use rhai::{Engine, EvalAltResult, INT}; fn test_engine_call_fn() -> Result<(), EvalAltResult> { let mut engine = Engine::new(); - let ast = engine.compile( + engine.consume( r" fn hello(x, y) { x.len() + y + } + fn hello(x) { + x * 2 } ", + true, )?; - let result: INT = engine.call_fn("hello", &ast, (String::from("abc"), 123 as INT))?; + let r: i64 = engine.call_fn("hello", (String::from("abc"), 123 as INT))?; + assert_eq!(r, 126); - assert_eq!(result, 126); + let r: i64 = engine.call_fn("hello", 123 as INT)?; + assert_eq!(r, 246); Ok(()) } diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 518e3b76..bdb4674e 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -5,15 +5,12 @@ fn test_var_scope() -> Result<(), EvalAltResult> { let mut engine = Engine::new(); let mut scope = Scope::new(); - engine.eval_with_scope::<()>(&mut scope, false, "let x = 4 + 5")?; - assert_eq!(engine.eval_with_scope::(&mut scope, false, "x")?, 9); - engine.eval_with_scope::<()>(&mut scope, false, "x = x + 1; x = x + 2;")?; - assert_eq!(engine.eval_with_scope::(&mut scope, false, "x")?, 12); - assert_eq!( - engine.eval_with_scope::<()>(&mut scope, false, "{let x = 3}")?, - () - ); - assert_eq!(engine.eval_with_scope::(&mut scope, false, "x")?, 12); + engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?; + assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 9); + engine.eval_with_scope::<()>(&mut scope, "x = x + 1; x = x + 2;")?; + assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 12); + assert_eq!(engine.eval_with_scope::<()>(&mut scope, "{let x = 3}")?, ()); + assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 12); Ok(()) } @@ -33,11 +30,11 @@ fn test_scope_eval() -> Result<(), EvalAltResult> { // First invocation engine - .eval_with_scope::<()>(&mut scope, false, " let x = 4 + 5 - y + z; y = 1;") + .eval_with_scope::<()>(&mut scope, " let x = 4 + 5 - y + z; y = 1;") .expect("y and z not found?"); // Second invocation using the same state - let result = engine.eval_with_scope::(&mut scope, false, "x")?; + let result = engine.eval_with_scope::(&mut scope, "x")?; println!("result: {}", result); // should print 966