diff --git a/README.md b/README.md index dbf37052..97e27192 100644 --- a/README.md +++ b/README.md @@ -232,18 +232,22 @@ let ast = engine.compile(true, } ")?; +// A custom scope can also contain any variables/constants available to the functions +let mut scope = Scope::new(); + // Evaluate a function defined in the script, 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 arguments of the wrong types are passed, // the Engine will not find the function. -let result: i64 = engine.call_fn(&ast, "hello", ( String::from("abc"), 123_i64 ) )?; -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ put arguments in a tuple +let result: i64 = engine.call_fn(&mut scope, &ast, "hello", ( String::from("abc"), 123_i64 ) )?; +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// put arguments in a tuple -let result: i64 = engine.call_fn1(&ast, "hello", 123_i64)? +let result: i64 = engine.call_fn1(&mut scope, &ast, "hello", 123_i64)? // ^^^^^^^^ use 'call_fn1' for one argument -let result: i64 = engine.call_fn0(&ast, "hello")? +let result: i64 = engine.call_fn0(&mut scope, &ast, "hello")? // ^^^^^^^^ use 'call_fn0' for no arguments ``` diff --git a/src/api.rs b/src/api.rs index ed14ada3..018ac400 100644 --- a/src/api.rs +++ b/src/api.rs @@ -857,23 +857,31 @@ impl<'e> Engine<'e> { /// # #[cfg(not(feature = "no_stdlib"))] /// # #[cfg(not(feature = "no_function"))] /// # { - /// use rhai::Engine; + /// use rhai::{Engine, Scope}; /// /// let mut engine = Engine::new(); /// - /// let ast = engine.compile("fn num() { 42 }")?; + /// let ast = engine.compile("fn num() { 42 + foo }")?; + /// + /// let mut scope = Scope::new(); + /// scope.push("foo", 42_i64); /// /// // Call the script-defined function - /// let result: i64 = engine.call_fn0(&ast, "num")?; + /// let result: i64 = engine.call_fn0(&mut scope, &ast, "num")?; /// - /// assert_eq!(result, 42); + /// assert_eq!(result, 84); /// # } /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "no_function"))] - pub fn call_fn0(&mut self, ast: &AST, name: &str) -> Result { - self.call_fn_internal(ast, name, vec![]) + pub fn call_fn0( + &mut self, + scope: &mut Scope, + ast: &AST, + name: &str, + ) -> Result { + self.call_fn_internal(scope, ast, name, vec![]) } /// Call a script function defined in an `AST` with one argument. @@ -885,16 +893,19 @@ impl<'e> Engine<'e> { /// # #[cfg(not(feature = "no_stdlib"))] /// # #[cfg(not(feature = "no_function"))] /// # { - /// use rhai::Engine; + /// use rhai::{Engine, Scope}; /// /// let mut engine = Engine::new(); /// - /// let ast = engine.compile("fn inc(x) { x + 1 }")?; + /// let ast = engine.compile("fn inc(x) { x + foo }")?; + /// + /// let mut scope = Scope::new(); + /// scope.push("foo", 42_i64); /// /// // Call the script-defined function - /// let result: i64 = engine.call_fn1(&ast, "inc", 123_i64)?; + /// let result: i64 = engine.call_fn1(&mut scope, &ast, "inc", 123_i64)?; /// - /// assert_eq!(result, 124); + /// assert_eq!(result, 165); /// # } /// # Ok(()) /// # } @@ -902,11 +913,12 @@ impl<'e> Engine<'e> { #[cfg(not(feature = "no_function"))] pub fn call_fn1( &mut self, + scope: &mut Scope, ast: &AST, name: &str, arg: A, ) -> Result { - self.call_fn_internal(ast, name, vec![arg.into_dynamic()]) + self.call_fn_internal(scope, ast, name, vec![arg.into_dynamic()]) } /// Call a script function defined in an `AST` with multiple arguments. @@ -918,16 +930,19 @@ impl<'e> Engine<'e> { /// # #[cfg(not(feature = "no_stdlib"))] /// # #[cfg(not(feature = "no_function"))] /// # { - /// use rhai::Engine; + /// use rhai::{Engine, Scope}; /// /// let mut engine = Engine::new(); /// - /// let ast = engine.compile("fn add(x, y) { len(x) + y }")?; + /// let ast = engine.compile("fn add(x, y) { len(x) + y + foo }")?; + /// + /// let mut scope = Scope::new(); + /// scope.push("foo", 42_i64); /// /// // Call the script-defined function - /// let result: i64 = engine.call_fn(&ast, "add", (String::from("abc"), 123_i64))?; + /// let result: i64 = engine.call_fn(&mut scope, &ast, "add", (String::from("abc"), 123_i64))?; /// - /// assert_eq!(result, 126); + /// assert_eq!(result, 168); /// # } /// # Ok(()) /// # } @@ -935,26 +950,28 @@ impl<'e> Engine<'e> { #[cfg(not(feature = "no_function"))] pub fn call_fn( &mut self, + scope: &mut Scope, ast: &AST, name: &str, args: A, ) -> Result { - self.call_fn_internal(ast, name, args.into_vec()) + self.call_fn_internal(scope, ast, name, args.into_vec()) } #[cfg(not(feature = "no_function"))] fn call_fn_internal( &mut self, + scope: &mut Scope, ast: &AST, name: &str, - mut values: Vec, + mut arg_values: Vec, ) -> Result { - let mut arg_values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect(); + let mut args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect(); self.fn_lib = Some(ast.1.clone()); let result = self - .call_fn_raw(name, &mut arg_values, None, Position::none(), 0)? + .call_fn_raw(Some(scope), name, &mut args, None, Position::none(), 0)? .try_cast() .map_err(|a| { EvalAltResult::ErrorMismatchOutputType( diff --git a/src/engine.rs b/src/engine.rs index e1ba5401..8bd55259 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -435,6 +435,7 @@ impl Engine<'_> { /// Universal method for calling functions either registered with the `Engine` or written in Rhai pub(crate) fn call_fn_raw( &mut self, + scope: Option<&mut Scope>, fn_name: &str, args: &mut FnCallArgs, def_val: Option<&Dynamic>, @@ -444,25 +445,56 @@ impl Engine<'_> { // First search in script-defined functions (can override built-in) if let Some(ref fn_lib_arc) = self.fn_lib { if let Some(fn_def) = fn_lib_arc.clone().get_function(fn_name, args.len()) { - let mut scope = Scope::new(); + match scope { + // Extern scope passed in which is not empty + Some(scope) if scope.len() > 0 => { + let scope_len = scope.len(); - scope.extend( - // Put arguments into scope as variables - fn_def - .params - .iter() - .zip(args.iter().map(|x| (*x).into_dynamic())) - .map(|(name, value)| (name, ScopeEntryType::Normal, value)), - ); + scope.extend( + // Put arguments into scope as variables - variable name is copied + fn_def + .params + .iter() + .zip(args.iter().map(|x| (*x).into_dynamic())) + .map(|(name, value)| (name.clone(), ScopeEntryType::Normal, value)), + ); - // Evaluate the function at one higher level of call depth - return self.eval_stmt(&mut scope, &fn_def.body, level + 1).or_else( - |err| match err { - // Convert return statement to return value - EvalAltResult::Return(x, _) => Ok(x), - err => Err(err.set_position(pos)), - }, - ); + // Evaluate the function at one higher level of call depth + let result = self.eval_stmt(scope, &fn_def.body, level + 1).or_else( + |err| match err { + // Convert return statement to return value + EvalAltResult::Return(x, _) => Ok(x), + err => Err(err.set_position(pos)), + }, + ); + + scope.rewind(scope_len); + + return result; + } + // No new scope - create internal scope + _ => { + let mut scope = Scope::new(); + + scope.extend( + // Put arguments into scope as variables + fn_def + .params + .iter() + .zip(args.iter().map(|x| (*x).into_dynamic())) + .map(|(name, value)| (name, ScopeEntryType::Normal, value)), + ); + + // Evaluate the function at one higher level of call depth + return self.eval_stmt(&mut scope, &fn_def.body, level + 1).or_else( + |err| match err { + // Convert return statement to return value + EvalAltResult::Return(x, _) => Ok(x), + err => Err(err.set_position(pos)), + }, + ); + } + } } } @@ -563,7 +595,7 @@ impl Engine<'_> { level: usize, ) -> Result { match dot_rhs { - // xxx.fn_name(args) + // xxx.fn_name(arg_expr_list) Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => { let mut values = arg_expr_list .iter() @@ -572,17 +604,19 @@ impl Engine<'_> { let this_ptr = target.get_mut(scope); - let mut arg_values: Vec<_> = once(this_ptr) + let mut args: Vec<_> = once(this_ptr) .chain(values.iter_mut().map(Dynamic::as_mut)) .collect(); - self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos, 0) + let def_val = def_val.as_ref(); + + self.call_fn_raw(None, fn_name, &mut args, def_val, *pos, 0) } // xxx.id Expr::Property(id, pos) => { - let this_ptr = target.get_mut(scope); - self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) + let mut args = [target.get_mut(scope)]; + self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0) } // xxx.idx_lhs[idx_expr] @@ -591,8 +625,8 @@ impl Engine<'_> { let value = match idx_lhs.as_ref() { // xxx.id[idx_expr] Expr::Property(id, pos) => { - let this_ptr = target.get_mut(scope); - self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0)? + let mut args = [target.get_mut(scope)]; + self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0)? } // xxx.???[???][idx_expr] Expr::Index(_, _, _) => { @@ -615,8 +649,8 @@ impl Engine<'_> { Expr::Dot(dot_lhs, rhs, _) => match dot_lhs.as_ref() { // xxx.id.rhs Expr::Property(id, pos) => { - let this_ptr = target.get_mut(scope); - self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) + let mut args = [target.get_mut(scope)]; + self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0) .and_then(|mut val| { self.get_dot_val_helper(scope, Target::from(val.as_mut()), rhs, level) }) @@ -627,8 +661,8 @@ impl Engine<'_> { let val = match idx_lhs.as_ref() { // xxx.id[idx_expr].rhs Expr::Property(id, pos) => { - let this_ptr = target.get_mut(scope); - self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0)? + let mut args = [target.get_mut(scope)]; + self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0)? } // xxx.???[???][idx_expr].rhs Expr::Index(_, _, _) => { @@ -977,7 +1011,7 @@ impl Engine<'_> { // xxx.id Expr::Property(id, pos) => { let mut args = [this_ptr, new_val.0.as_mut()]; - self.call_fn_raw(&make_setter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) } // xxx.lhs[idx_expr] @@ -986,7 +1020,7 @@ impl Engine<'_> { Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() { // xxx.id[idx_expr] Expr::Property(id, pos) => self - .call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) + .call_fn_raw(None, &make_getter(id), &mut [this_ptr], None, *pos, 0) .and_then(|val| { let (_, _, idx) = self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)?; @@ -995,7 +1029,7 @@ impl Engine<'_> { }) .and_then(|mut val| { let mut args = [this_ptr, val.as_mut()]; - self.call_fn_raw(&make_setter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) }), // All others - syntax error for setters chain @@ -1009,14 +1043,14 @@ impl Engine<'_> { Expr::Dot(lhs, rhs, _) => match lhs.as_ref() { // xxx.id.rhs Expr::Property(id, pos) => { - self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) + self.call_fn_raw(None, &make_getter(id), &mut [this_ptr], None, *pos, 0) .and_then(|mut val| { self.set_dot_val_helper(scope, val.as_mut(), rhs, new_val, level) .map(|_| val) // Discard Ok return value }) .and_then(|mut val| { let mut args = [this_ptr, val.as_mut()]; - self.call_fn_raw(&make_setter(id), &mut args, None, *pos, 0) + self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) }) } @@ -1026,7 +1060,7 @@ impl Engine<'_> { Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() { // xxx.id[idx_expr].rhs Expr::Property(id, pos) => { - self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) + self.call_fn_raw(None, &make_getter(id), &mut [this_ptr], None, *pos, 0) .and_then(|v| { let (mut value, _, idx) = self.get_indexed_value(scope, &v, idx_expr, *op_pos, level)?; @@ -1039,13 +1073,8 @@ impl Engine<'_> { Self::update_indexed_value(v, idx, value, val_pos) }) .and_then(|mut v| { - self.call_fn_raw( - &make_setter(id), - &mut [this_ptr, v.as_mut()], - None, - *pos, - 0, - ) + let mut args = [this_ptr, v.as_mut()]; + self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0) }) } @@ -1315,7 +1344,8 @@ impl Engine<'_> { .into_dynamic(); // Redirect call to `print` - self.call_fn_raw(KEYWORD_PRINT, &mut [result.as_mut()], None, pos, level) + let mut args = [result.as_mut()]; + self.call_fn_raw(None, KEYWORD_PRINT, &mut args, None, pos, level) } // type_of @@ -1349,7 +1379,7 @@ impl Engine<'_> { // Compile the script text #[cfg(not(feature = "no_optimize"))] - let mut ast = { + let ast = { let orig_optimization_level = self.optimization_level; self.set_optimization_level(OptimizationLevel::None); @@ -1360,7 +1390,7 @@ impl Engine<'_> { }; #[cfg(feature = "no_optimize")] - let mut ast = self.compile(script).map_err(EvalAltResult::ErrorParsing)?; + let ast = self.compile(script).map_err(EvalAltResult::ErrorParsing)?; // If new functions are defined, merge it into the current functions library let merged = AST( @@ -1404,7 +1434,9 @@ impl Engine<'_> { let mut arg_values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect(); - self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos, level) + let def_val = def_val.as_ref(); + + self.call_fn_raw(None, fn_name, &mut arg_values, def_val, *pos, level) } } } @@ -1527,6 +1559,7 @@ impl Engine<'_> { if let Some(ref type_iterators) = self.type_iterators { if let Some(iter_fn) = type_iterators.get(&tid) { + // Add the loop variable - variable name is copied scope.push(name.clone(), ()); let entry = ScopeSource { diff --git a/src/scope.rs b/src/scope.rs index b76e3904..d3740f4c 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -63,6 +63,7 @@ pub(crate) struct EntryRef<'a> { /// allowing for automatic _shadowing_. /// /// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. +#[derive(Debug, Clone)] pub struct Scope<'a>(Vec>); impl<'a> Scope<'a> { diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 555219f2..1535f504 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_function"))] -use rhai::{Engine, EvalAltResult, ParseErrorType, INT}; +use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT}; #[test] fn test_fn() -> Result<(), EvalAltResult> { @@ -21,6 +21,9 @@ fn test_fn() -> Result<(), EvalAltResult> { #[test] fn test_call_fn() -> Result<(), EvalAltResult> { let mut engine = Engine::new(); + let mut scope = Scope::new(); + + scope.push("foo", 42 as INT); let ast = engine.compile( r" @@ -28,22 +31,22 @@ fn test_call_fn() -> Result<(), EvalAltResult> { x + y } fn hello(x) { - x * 2 + x * foo } fn hello() { - 42 + 42 + foo } ", )?; - let r: i64 = engine.call_fn(&ast, "hello", (42 as INT, 123 as INT))?; + let r: i64 = engine.call_fn(&mut scope, &ast, "hello", (42 as INT, 123 as INT))?; assert_eq!(r, 165); - let r: i64 = engine.call_fn1(&ast, "hello", 123 as INT)?; - assert_eq!(r, 246); + let r: i64 = engine.call_fn1(&mut scope, &ast, "hello", 123 as INT)?; + assert_eq!(r, 5166); - let r: i64 = engine.call_fn0(&ast, "hello")?; - assert_eq!(r, 42); + let r: i64 = engine.call_fn0(&mut scope, &ast, "hello")?; + assert_eq!(r, 84); Ok(()) }