Allow passing in custom Scope to call_fn.

This commit is contained in:
Stephen Chung 2020-04-05 12:17:31 +08:00
parent ae9a975576
commit 3f247fd695
5 changed files with 134 additions and 76 deletions

View File

@ -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 // 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 // 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, // Rhai does not have built-in type conversions. If arguments of the wrong types are passed,
// the Engine will not find the function. // the Engine will not find the function.
let result: i64 = engine.call_fn(&ast, "hello", ( String::from("abc"), 123_i64 ) )?; let result: i64 = engine.call_fn(&mut scope, &ast, "hello", ( String::from("abc"), 123_i64 ) )?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ put arguments in a tuple // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// 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 // ^^^^^^^^ 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 // ^^^^^^^^ use 'call_fn0' for no arguments
``` ```

View File

@ -857,23 +857,31 @@ impl<'e> Engine<'e> {
/// # #[cfg(not(feature = "no_stdlib"))] /// # #[cfg(not(feature = "no_stdlib"))]
/// # #[cfg(not(feature = "no_function"))] /// # #[cfg(not(feature = "no_function"))]
/// # { /// # {
/// use rhai::Engine; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// 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 /// // 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(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub fn call_fn0<T: Any + Clone>(&mut self, ast: &AST, name: &str) -> Result<T, EvalAltResult> { pub fn call_fn0<T: Any + Clone>(
self.call_fn_internal(ast, name, vec![]) &mut self,
scope: &mut Scope,
ast: &AST,
name: &str,
) -> Result<T, EvalAltResult> {
self.call_fn_internal(scope, ast, name, vec![])
} }
/// Call a script function defined in an `AST` with one argument. /// 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_stdlib"))]
/// # #[cfg(not(feature = "no_function"))] /// # #[cfg(not(feature = "no_function"))]
/// # { /// # {
/// use rhai::Engine; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// 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 /// // 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(()) /// # Ok(())
/// # } /// # }
@ -902,11 +913,12 @@ 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, &mut self,
scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
arg: A, arg: A,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
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. /// 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_stdlib"))]
/// # #[cfg(not(feature = "no_function"))] /// # #[cfg(not(feature = "no_function"))]
/// # { /// # {
/// use rhai::Engine; /// use rhai::{Engine, Scope};
/// ///
/// let mut engine = Engine::new(); /// 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 /// // 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(()) /// # Ok(())
/// # } /// # }
@ -935,26 +950,28 @@ 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, &mut self,
scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
args: A, args: A,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
self.call_fn_internal(ast, name, args.into_vec()) self.call_fn_internal(scope, ast, name, args.into_vec())
} }
#[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, &mut self,
scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: &str,
mut values: Vec<Dynamic>, mut arg_values: Vec<Dynamic>,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
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()); self.fn_lib = Some(ast.1.clone());
let result = self 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() .try_cast()
.map_err(|a| { .map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(

View File

@ -435,6 +435,7 @@ 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, &mut self,
scope: Option<&mut Scope>,
fn_name: &str, fn_name: &str,
args: &mut FnCallArgs, args: &mut FnCallArgs,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
@ -444,25 +445,56 @@ impl Engine<'_> {
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
if let Some(ref fn_lib_arc) = self.fn_lib { 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()) { 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( scope.extend(
// Put arguments into scope as variables // Put arguments into scope as variables - variable name is copied
fn_def fn_def
.params .params
.iter() .iter()
.zip(args.iter().map(|x| (*x).into_dynamic())) .zip(args.iter().map(|x| (*x).into_dynamic()))
.map(|(name, value)| (name, ScopeEntryType::Normal, value)), .map(|(name, value)| (name.clone(), ScopeEntryType::Normal, value)),
); );
// 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( let result = self.eval_stmt(scope, &fn_def.body, level + 1).or_else(
|err| match err { |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);
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, level: usize,
) -> Result<Dynamic, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
match dot_rhs { match dot_rhs {
// xxx.fn_name(args) // xxx.fn_name(arg_expr_list)
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()
@ -572,17 +604,19 @@ impl Engine<'_> {
let this_ptr = target.get_mut(scope); 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)) .chain(values.iter_mut().map(Dynamic::as_mut))
.collect(); .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 // xxx.id
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let this_ptr = target.get_mut(scope); let mut args = [target.get_mut(scope)];
self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0)
} }
// xxx.idx_lhs[idx_expr] // xxx.idx_lhs[idx_expr]
@ -591,8 +625,8 @@ impl Engine<'_> {
let value = match idx_lhs.as_ref() { let value = match idx_lhs.as_ref() {
// xxx.id[idx_expr] // xxx.id[idx_expr]
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let this_ptr = target.get_mut(scope); let mut args = [target.get_mut(scope)];
self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0)? self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0)?
} }
// xxx.???[???][idx_expr] // xxx.???[???][idx_expr]
Expr::Index(_, _, _) => { Expr::Index(_, _, _) => {
@ -615,8 +649,8 @@ impl Engine<'_> {
Expr::Dot(dot_lhs, rhs, _) => match dot_lhs.as_ref() { Expr::Dot(dot_lhs, rhs, _) => match dot_lhs.as_ref() {
// xxx.id.rhs // xxx.id.rhs
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let this_ptr = target.get_mut(scope); let mut args = [target.get_mut(scope)];
self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) self.call_fn_raw(None, &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) 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() { 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 this_ptr = target.get_mut(scope); let mut args = [target.get_mut(scope)];
self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0)? self.call_fn_raw(None, &make_getter(id), &mut args, None, *pos, 0)?
} }
// xxx.???[???][idx_expr].rhs // xxx.???[???][idx_expr].rhs
Expr::Index(_, _, _) => { Expr::Index(_, _, _) => {
@ -977,7 +1011,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(&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] // xxx.lhs[idx_expr]
@ -986,7 +1020,7 @@ 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] // xxx.id[idx_expr]
Expr::Property(id, pos) => self 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| { .and_then(|val| {
let (_, _, idx) = let (_, _, idx) =
self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)?; self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)?;
@ -995,7 +1029,7 @@ impl Engine<'_> {
}) })
.and_then(|mut val| { .and_then(|mut val| {
let mut args = [this_ptr, val.as_mut()]; 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 // All others - syntax error for setters chain
@ -1009,14 +1043,14 @@ 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(&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| { .and_then(|mut val| {
self.set_dot_val_helper(scope, val.as_mut(), rhs, new_val, level) self.set_dot_val_helper(scope, val.as_mut(), 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 mut args = [this_ptr, val.as_mut()]; 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() { 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(&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| { .and_then(|v| {
let (mut value, _, idx) = let (mut value, _, idx) =
self.get_indexed_value(scope, &v, idx_expr, *op_pos, level)?; 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) Self::update_indexed_value(v, idx, value, val_pos)
}) })
.and_then(|mut v| { .and_then(|mut v| {
self.call_fn_raw( let mut args = [this_ptr, v.as_mut()];
&make_setter(id), self.call_fn_raw(None, &make_setter(id), &mut args, None, *pos, 0)
&mut [this_ptr, v.as_mut()],
None,
*pos,
0,
)
}) })
} }
@ -1315,7 +1344,8 @@ impl Engine<'_> {
.into_dynamic(); .into_dynamic();
// Redirect call to `print` // 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 // type_of
@ -1349,7 +1379,7 @@ impl Engine<'_> {
// Compile the script text // Compile the script text
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
let mut ast = { let ast = {
let orig_optimization_level = self.optimization_level; let orig_optimization_level = self.optimization_level;
self.set_optimization_level(OptimizationLevel::None); self.set_optimization_level(OptimizationLevel::None);
@ -1360,7 +1390,7 @@ impl Engine<'_> {
}; };
#[cfg(feature = "no_optimize")] #[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 // If new functions are defined, merge it into the current functions library
let merged = AST( let merged = AST(
@ -1404,7 +1434,9 @@ impl Engine<'_> {
let mut arg_values: Vec<_> = let mut arg_values: Vec<_> =
values.iter_mut().map(Dynamic::as_mut).collect(); 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(ref type_iterators) = self.type_iterators {
if let Some(iter_fn) = type_iterators.get(&tid) { if let Some(iter_fn) = type_iterators.get(&tid) {
// Add the loop variable - variable name is copied
scope.push(name.clone(), ()); scope.push(name.clone(), ());
let entry = ScopeSource { let entry = ScopeSource {

View File

@ -63,6 +63,7 @@ pub(crate) struct EntryRef<'a> {
/// allowing for automatic _shadowing_. /// allowing for automatic _shadowing_.
/// ///
/// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. /// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
#[derive(Debug, Clone)]
pub struct Scope<'a>(Vec<Entry<'a>>); pub struct Scope<'a>(Vec<Entry<'a>>);
impl<'a> Scope<'a> { impl<'a> Scope<'a> {

View File

@ -1,5 +1,5 @@
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use rhai::{Engine, EvalAltResult, ParseErrorType, INT}; use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT};
#[test] #[test]
fn test_fn() -> Result<(), EvalAltResult> { fn test_fn() -> Result<(), EvalAltResult> {
@ -21,6 +21,9 @@ 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 mut engine = Engine::new();
let mut scope = Scope::new();
scope.push("foo", 42 as INT);
let ast = engine.compile( let ast = engine.compile(
r" r"
@ -28,22 +31,22 @@ fn test_call_fn() -> Result<(), EvalAltResult> {
x + y x + y
} }
fn hello(x) { fn hello(x) {
x * 2 x * foo
} }
fn hello() { 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); assert_eq!(r, 165);
let r: i64 = engine.call_fn1(&ast, "hello", 123 as INT)?; let r: i64 = engine.call_fn1(&mut scope, &ast, "hello", 123 as INT)?;
assert_eq!(r, 246); assert_eq!(r, 5166);
let r: i64 = engine.call_fn0(&ast, "hello")?; let r: i64 = engine.call_fn0(&mut scope, &ast, "hello")?;
assert_eq!(r, 42); assert_eq!(r, 84);
Ok(()) Ok(())
} }