Move fn_lib into State, and use StaticVec for function call arguments in dotting/indexing chains.

This commit is contained in:
Stephen Chung 2020-05-07 10:00:10 +08:00
parent 8e60976cfa
commit fb64adca93
3 changed files with 123 additions and 107 deletions

View File

@ -867,12 +867,12 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut state = State::new(); let mut state = State::new(ast.fn_lib());
ast.statements() ast.statements()
.iter() .iter()
.try_fold(().into(), |_, stmt| { .try_fold(().into(), |_, stmt| {
self.eval_stmt(scope, &mut state, ast.fn_lib(), stmt, 0) self.eval_stmt(scope, &mut state, stmt, 0)
}) })
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out), EvalAltResult::Return(out, _) => Ok(out),
@ -932,12 +932,12 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
let mut state = State::new(); let mut state = State::new(ast.fn_lib());
ast.statements() ast.statements()
.iter() .iter()
.try_fold(().into(), |_, stmt| { .try_fold(().into(), |_, stmt| {
self.eval_stmt(scope, &mut state, ast.fn_lib(), stmt, 0) self.eval_stmt(scope, &mut state, stmt, 0)
}) })
.map_or_else( .map_or_else(
|err| match *err { |err| match *err {
@ -1000,7 +1000,9 @@ impl Engine {
.get_function(name, args.len()) .get_function(name, args.len())
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?; .ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
let result = self.call_fn_from_lib(Some(scope), fn_lib, fn_def, &mut args, pos, 0)?; let state = State::new(fn_lib);
let result = self.call_script_fn(Some(scope), &state, fn_def, &mut args, pos, 0)?;
let return_type = self.map_type_name(result.type_name()); let return_type = self.map_type_name(result.type_name());

View File

@ -141,21 +141,35 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
} }
/// A type that holds all the current states of the Engine. /// A type that holds all the current states of the Engine.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy)]
pub struct State { pub struct State<'a> {
/// Global script-defined functions.
pub fn_lib: &'a FunctionsLib,
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup. /// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
/// In some situation, e.g. after running an `eval` statement, subsequent offsets may become mis-aligned. /// In some situation, e.g. after running an `eval` statement, subsequent offsets may become mis-aligned.
/// When that happens, this flag is turned on to force a scope lookup by name. /// When that happens, this flag is turned on to force a scope lookup by name.
pub always_search: bool, pub always_search: bool,
} }
impl State { impl<'a> State<'a> {
/// Create a new `State`. /// Create a new `State`.
pub fn new() -> Self { pub fn new(fn_lib: &'a FunctionsLib) -> Self {
Self { Self {
always_search: false, always_search: false,
fn_lib,
} }
} }
/// Does a certain script-defined function exist in the `State`?
pub fn has_function(&self, name: &str, params: usize) -> bool {
self.fn_lib.contains_key(&calc_fn_def(name, params))
}
/// Get a script-defined function definition from the `State`.
pub fn get_function(&self, name: &str, params: usize) -> Option<&FnDef> {
self.fn_lib
.get(&calc_fn_def(name, params))
.map(|f| f.as_ref())
}
} }
/// A type that holds a library (`HashMap`) of script-defined functions. /// A type that holds a library (`HashMap`) of script-defined functions.
@ -511,7 +525,7 @@ impl Engine {
pub(crate) fn call_fn_raw( pub(crate) fn call_fn_raw(
&self, &self,
scope: Option<&mut Scope>, scope: Option<&mut Scope>,
fn_lib: &FunctionsLib, state: &State,
fn_name: &str, fn_name: &str,
args: &mut FnCallArgs, args: &mut FnCallArgs,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
@ -524,8 +538,8 @@ 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(fn_def) = fn_lib.get_function(fn_name, args.len()) { if let Some(fn_def) = state.get_function(fn_name, args.len()) {
return self.call_fn_from_lib(scope, fn_lib, fn_def, args, pos, level); return self.call_script_fn(scope, state, fn_def, args, pos, level);
} }
// Search built-in's and external functions // Search built-in's and external functions
@ -608,10 +622,10 @@ impl Engine {
/// Function call arguments may be _consumed_ when the function requires them to be passed by value. /// Function call arguments may be _consumed_ when the function requires them to be passed by value.
/// All function arguments not in the first position are always passed by value and thus consumed. /// All function arguments not in the first position are always passed by value and thus consumed.
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
pub(crate) fn call_fn_from_lib( pub(crate) fn call_script_fn(
&self, &self,
scope: Option<&mut Scope>, scope: Option<&mut Scope>,
fn_lib: &FunctionsLib, state: &State,
fn_def: &FnDef, fn_def: &FnDef,
args: &mut FnCallArgs, args: &mut FnCallArgs,
pos: Position, pos: Position,
@ -621,7 +635,7 @@ impl Engine {
// 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 => {
let scope_len = scope.len(); let scope_len = scope.len();
let mut state = State::new(); let mut state = State::new(state.fn_lib);
// Put arguments into scope as variables - variable name is copied // Put arguments into scope as variables - variable name is copied
scope.extend( scope.extend(
@ -637,7 +651,7 @@ 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 let result = self
.eval_stmt(scope, &mut state, fn_lib, &fn_def.body, level + 1) .eval_stmt(scope, &mut state, &fn_def.body, level + 1)
.or_else(|err| match *err { .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),
@ -651,7 +665,7 @@ impl Engine {
// No new scope - create internal scope // No new scope - create internal scope
_ => { _ => {
let mut scope = Scope::new(); let mut scope = Scope::new();
let mut state = State::new(); let mut state = State::new(state.fn_lib);
// Put arguments into scope as variables // Put arguments into scope as variables
scope.extend( scope.extend(
@ -667,7 +681,7 @@ impl Engine {
// Evaluate the function at one higher level of call depth // Evaluate the function at one higher level of call depth
return self return self
.eval_stmt(&mut scope, &mut state, fn_lib, &fn_def.body, level + 1) .eval_stmt(&mut scope, &mut state, &fn_def.body, level + 1)
.or_else(|err| match *err { .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),
@ -678,7 +692,7 @@ impl Engine {
} }
// Has a system function an override? // Has a system function an override?
fn has_override(&self, fn_lib: &FunctionsLib, name: &str) -> bool { fn has_override(&self, state: &State, name: &str) -> bool {
let hash = calc_fn_hash(name, once(TypeId::of::<String>())); let hash = calc_fn_hash(name, once(TypeId::of::<String>()));
// First check registered functions // First check registered functions
@ -686,7 +700,7 @@ impl Engine {
// Then check packages // Then check packages
|| self.packages.iter().any(|p| p.functions.contains_key(&hash)) || self.packages.iter().any(|p| p.functions.contains_key(&hash))
// Then check script-defined functions // Then check script-defined functions
|| fn_lib.has_function(name, 1) || state.has_function(name, 1)
} }
// Perform an actual function call, taking care of special functions // Perform an actual function call, taking care of special functions
@ -698,7 +712,7 @@ impl Engine {
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
fn exec_fn_call( fn exec_fn_call(
&self, &self,
fn_lib: &FunctionsLib, state: &State,
fn_name: &str, fn_name: &str,
args: &mut FnCallArgs, args: &mut FnCallArgs,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
@ -707,19 +721,19 @@ impl Engine {
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
match fn_name { match fn_name {
// type_of // type_of
KEYWORD_TYPE_OF if args.len() == 1 && !self.has_override(fn_lib, KEYWORD_TYPE_OF) => { KEYWORD_TYPE_OF if args.len() == 1 && !self.has_override(state, KEYWORD_TYPE_OF) => {
Ok(self.map_type_name(args[0].type_name()).to_string().into()) Ok(self.map_type_name(args[0].type_name()).to_string().into())
} }
// eval - reaching this point it must be a method-style call // eval - reaching this point it must be a method-style call
KEYWORD_EVAL if args.len() == 1 && !self.has_override(fn_lib, KEYWORD_EVAL) => { KEYWORD_EVAL if args.len() == 1 && !self.has_override(state, KEYWORD_EVAL) => {
Err(Box::new(EvalAltResult::ErrorRuntime( Err(Box::new(EvalAltResult::ErrorRuntime(
"'eval' should not be called in method style. Try eval(...);".into(), "'eval' should not be called in method style. Try eval(...);".into(),
pos, pos,
))) )))
} }
// Normal method call // Normal method call
_ => self.call_fn_raw(None, fn_lib, fn_name, args, def_val, pos, level), _ => self.call_fn_raw(None, state, fn_name, args, def_val, pos, level),
} }
} }
@ -727,7 +741,7 @@ impl Engine {
fn eval_script_expr( fn eval_script_expr(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
fn_lib: &FunctionsLib, state: &State,
script: &Dynamic, script: &Dynamic,
pos: Position, pos: Position,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -751,7 +765,7 @@ impl Engine {
} }
let statements = mem::take(ast.statements_mut()); let statements = mem::take(ast.statements_mut());
let ast = AST::new(statements, fn_lib.clone()); let ast = AST::new(statements, state.fn_lib.clone());
// Evaluate the AST // Evaluate the AST
self.eval_ast_with_scope_raw(scope, &ast) self.eval_ast_with_scope_raw(scope, &ast)
@ -761,7 +775,7 @@ impl Engine {
/// Chain-evaluate a dot/index chain. /// Chain-evaluate a dot/index chain.
fn eval_dot_index_chain_helper( fn eval_dot_index_chain_helper(
&self, &self,
fn_lib: &FunctionsLib, state: &State,
mut target: Target, mut target: Target,
rhs: &Expr, rhs: &Expr,
idx_values: &mut StaticVec<Dynamic>, idx_values: &mut StaticVec<Dynamic>,
@ -788,20 +802,20 @@ impl Engine {
Expr::Index(idx, idx_rhs, pos) => { Expr::Index(idx, idx_rhs, pos) => {
let is_index = matches!(rhs, Expr::Index(_,_,_)); let is_index = matches!(rhs, Expr::Index(_,_,_));
let indexed_val = self.get_indexed_mut(fn_lib, obj, idx_val, idx.position(), op_pos, false)?; let indexed_val = self.get_indexed_mut(state, obj, idx_val, idx.position(), op_pos, false)?;
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
fn_lib, indexed_val, idx_rhs.as_ref(), idx_values, is_index, *pos, level, new_val state, indexed_val, idx_rhs.as_ref(), idx_values, is_index, *pos, level, new_val
) )
} }
// xxx[rhs] = new_val // xxx[rhs] = new_val
_ if new_val.is_some() => { _ if new_val.is_some() => {
let mut indexed_val = self.get_indexed_mut(fn_lib, obj, idx_val, rhs.position(), op_pos, true)?; let mut indexed_val = self.get_indexed_mut(state, obj, idx_val, rhs.position(), op_pos, true)?;
indexed_val.set_value(new_val.unwrap(), rhs.position())?; indexed_val.set_value(new_val.unwrap(), rhs.position())?;
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
// xxx[rhs] // xxx[rhs]
_ => self _ => self
.get_indexed_mut(fn_lib, obj, idx_val, rhs.position(), op_pos, false) .get_indexed_mut(state, obj, idx_val, rhs.position(), op_pos, false)
.map(|v| (v.clone_into_dynamic(), false)) .map(|v| (v.clone_into_dynamic(), false))
} }
} else { } else {
@ -809,12 +823,12 @@ impl Engine {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(fn_name, None,_, def_val, pos) => { Expr::FnCall(fn_name, None,_, def_val, pos) => {
let mut args: Vec<_> = once(obj) let mut args: Vec<_> = once(obj)
.chain(idx_val.downcast_mut::<Vec<Dynamic>>().unwrap().iter_mut()) .chain(idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap().iter_mut())
.collect(); .collect();
let def_val = def_val.as_deref(); let def_val = def_val.as_deref();
// A function call is assumed to have side effects, so the value is changed // A function call is assumed to have side effects, so the value is changed
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut // TODO - Remove assumption of side effects by checking whether the first parameter is &mut
self.exec_fn_call(fn_lib, fn_name, &mut args, def_val, *pos, 0).map(|v| (v, true)) self.exec_fn_call(state, fn_name, &mut args, def_val, *pos, 0).map(|v| (v, true))
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_,_,_,_,_) => unreachable!(), Expr::FnCall(_,_,_,_,_) => unreachable!(),
@ -822,7 +836,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => { Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => {
let mut indexed_val = let mut indexed_val =
self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, true)?; self.get_indexed_mut(state, obj, id.to_string().into(), *pos, op_pos, true)?;
indexed_val.set_value(new_val.unwrap(), rhs.position())?; indexed_val.set_value(new_val.unwrap(), rhs.position())?;
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
@ -830,20 +844,20 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() => { Expr::Property(id, pos) if obj.is::<Map>() => {
let indexed_val = let indexed_val =
self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, false)?; self.get_indexed_mut(state, obj, id.to_string().into(), *pos, op_pos, false)?;
Ok((indexed_val.clone_into_dynamic(), false)) Ok((indexed_val.clone_into_dynamic(), false))
} }
// xxx.id = ??? a // xxx.id = ??? a
Expr::Property(id, pos) if new_val.is_some() => { Expr::Property(id, pos) if new_val.is_some() => {
let fn_name = make_setter(id); let fn_name = make_setter(id);
let mut args = [obj, new_val.as_mut().unwrap()]; let mut args = [obj, new_val.as_mut().unwrap()];
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, true)) self.exec_fn_call(state, &fn_name, &mut args, None, *pos, 0).map(|v| (v, true))
} }
// xxx.id // xxx.id
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let fn_name = make_getter(id); let fn_name = make_getter(id);
let mut args = [obj]; let mut args = [obj];
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, false)) self.exec_fn_call(state, &fn_name, &mut args, None, *pos, 0).map(|v| (v, false))
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
// {xxx:map}.idx_lhs[idx_expr] // {xxx:map}.idx_lhs[idx_expr]
@ -853,7 +867,7 @@ impl Engine {
let is_index = matches!(rhs, Expr::Index(_,_,_)); let is_index = matches!(rhs, Expr::Index(_,_,_));
let indexed_val = if let Expr::Property(id, pos) = dot_lhs.as_ref() { let indexed_val = if let Expr::Property(id, pos) = dot_lhs.as_ref() {
self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, false)? self.get_indexed_mut(state, obj, id.to_string().into(), *pos, op_pos, false)?
} else { } else {
// Syntax error // Syntax error
return Err(Box::new(EvalAltResult::ErrorDotExpr( return Err(Box::new(EvalAltResult::ErrorDotExpr(
@ -862,7 +876,7 @@ impl Engine {
))); )));
}; };
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
fn_lib, indexed_val, dot_rhs, idx_values, is_index, *pos, level, new_val state, indexed_val, dot_rhs, idx_values, is_index, *pos, level, new_val
) )
} }
// xxx.idx_lhs[idx_expr] // xxx.idx_lhs[idx_expr]
@ -874,7 +888,7 @@ impl Engine {
let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() { let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() {
let fn_name = make_getter(id); let fn_name = make_getter(id);
self.exec_fn_call(fn_lib, &fn_name, &mut args[..1], None, *pos, 0)? self.exec_fn_call(state, &fn_name, &mut args[..1], None, *pos, 0)?
} else { } else {
// Syntax error // Syntax error
return Err(Box::new(EvalAltResult::ErrorDotExpr( return Err(Box::new(EvalAltResult::ErrorDotExpr(
@ -883,7 +897,7 @@ impl Engine {
))); )));
}); });
let (result, may_be_changed) = self.eval_dot_index_chain_helper( let (result, may_be_changed) = self.eval_dot_index_chain_helper(
fn_lib, indexed_val.into(), dot_rhs, idx_values, is_index, *pos, level, new_val state, indexed_val.into(), dot_rhs, idx_values, is_index, *pos, level, new_val
)?; )?;
// Feed the value back via a setter just in case it has been updated // Feed the value back via a setter just in case it has been updated
@ -892,7 +906,7 @@ impl Engine {
let fn_name = make_setter(id); let fn_name = make_setter(id);
// Re-use args because the first &mut parameter will not be consumed // Re-use args because the first &mut parameter will not be consumed
args[1] = indexed_val; args[1] = indexed_val;
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).or_else(|err| match *err { self.exec_fn_call(state, &fn_name, &mut args, None, *pos, 0).or_else(|err| match *err {
// If there is no setter, no need to feed it back because the property is read-only // If there is no setter, no need to feed it back because the property is read-only
EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()), EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()),
err => Err(Box::new(err)) err => Err(Box::new(err))
@ -916,7 +930,6 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
fn_lib: &FunctionsLib,
dot_lhs: &Expr, dot_lhs: &Expr,
dot_rhs: &Expr, dot_rhs: &Expr,
is_index: bool, is_index: bool,
@ -926,7 +939,7 @@ impl Engine {
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let idx_values = &mut StaticVec::new(); let idx_values = &mut StaticVec::new();
self.eval_indexed_chain(scope, state, fn_lib, dot_rhs, idx_values, 0, level)?; self.eval_indexed_chain(scope, state, dot_rhs, idx_values, 0, level)?;
match dot_lhs { match dot_lhs {
// id.??? or id[???] // id.??? or id[???]
@ -948,7 +961,7 @@ impl Engine {
let this_ptr = target.into(); let this_ptr = target.into();
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
fn_lib, this_ptr, dot_rhs, idx_values, is_index, op_pos, level, new_val, state, this_ptr, dot_rhs, idx_values, is_index, op_pos, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
} }
@ -960,10 +973,10 @@ impl Engine {
} }
// {expr}.??? or {expr}[???] // {expr}.??? or {expr}[???]
expr => { expr => {
let val = self.eval_expr(scope, state, fn_lib, expr, level)?; let val = self.eval_expr(scope, state, expr, level)?;
let this_ptr = val.into(); let this_ptr = val.into();
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
fn_lib, this_ptr, dot_rhs, idx_values, is_index, op_pos, level, new_val, state, this_ptr, dot_rhs, idx_values, is_index, op_pos, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
} }
@ -979,7 +992,6 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
fn_lib: &FunctionsLib,
expr: &Expr, expr: &Expr,
idx_values: &mut StaticVec<Dynamic>, idx_values: &mut StaticVec<Dynamic>,
size: usize, size: usize,
@ -987,14 +999,12 @@ impl Engine {
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
match expr { match expr {
Expr::FnCall(_, None, arg_exprs, _, _) => { Expr::FnCall(_, None, arg_exprs, _, _) => {
let arg_values = arg_exprs let mut arg_values = StaticVec::<Dynamic>::new();
.iter()
.map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level)) for arg_expr in arg_exprs.iter() {
.collect::<Result<Vec<_>, _>>()?; arg_values.push(self.eval_expr(scope, state, arg_expr, level)?);
}
#[cfg(not(feature = "no_index"))]
idx_values.push(arg_values);
#[cfg(feature = "no_index")]
idx_values.push(Dynamic::from(arg_values)); idx_values.push(Dynamic::from(arg_values));
} }
Expr::FnCall(_, _, _, _, _) => unreachable!(), Expr::FnCall(_, _, _, _, _) => unreachable!(),
@ -1003,15 +1013,15 @@ impl Engine {
// Evaluate in left-to-right order // Evaluate in left-to-right order
let lhs_val = match lhs.as_ref() { let lhs_val = match lhs.as_ref() {
Expr::Property(_, _) => Default::default(), // Store a placeholder in case of a property Expr::Property(_, _) => Default::default(), // Store a placeholder in case of a property
_ => self.eval_expr(scope, state, fn_lib, lhs, level)?, _ => self.eval_expr(scope, state, lhs, level)?,
}; };
// Push in reverse order // Push in reverse order
self.eval_indexed_chain(scope, state, fn_lib, rhs, idx_values, size, level)?; self.eval_indexed_chain(scope, state, rhs, idx_values, size, level)?;
idx_values.push(lhs_val); idx_values.push(lhs_val);
} }
_ => idx_values.push(self.eval_expr(scope, state, fn_lib, expr, level)?), _ => idx_values.push(self.eval_expr(scope, state, expr, level)?),
} }
Ok(()) Ok(())
@ -1020,7 +1030,7 @@ impl Engine {
/// Get the value at the indexed position of a base type /// Get the value at the indexed position of a base type
fn get_indexed_mut<'a>( fn get_indexed_mut<'a>(
&self, &self,
fn_lib: &FunctionsLib, state: &State,
val: &'a mut Dynamic, val: &'a mut Dynamic,
mut idx: Dynamic, mut idx: Dynamic,
idx_pos: Position, idx_pos: Position,
@ -1100,7 +1110,7 @@ impl Engine {
_ => { _ => {
let args = &mut [val, &mut idx]; let args = &mut [val, &mut idx];
self.exec_fn_call(fn_lib, FUNC_INDEXER, args, None, op_pos, 0) self.exec_fn_call(state, FUNC_INDEXER, args, None, op_pos, 0)
.map(|v| v.into()) .map(|v| v.into())
.map_err(|_| { .map_err(|_| {
Box::new(EvalAltResult::ErrorIndexingType( Box::new(EvalAltResult::ErrorIndexingType(
@ -1118,13 +1128,12 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
fn_lib: &FunctionsLib,
lhs: &Expr, lhs: &Expr,
rhs: &Expr, rhs: &Expr,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let lhs_value = self.eval_expr(scope, state, fn_lib, lhs, level)?; let lhs_value = self.eval_expr(scope, state, lhs, level)?;
let rhs_value = self.eval_expr(scope, state, fn_lib, rhs, level)?; let rhs_value = self.eval_expr(scope, state, rhs, level)?;
match rhs_value { match rhs_value {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -1141,7 +1150,7 @@ impl Engine {
let pos = rhs.position(); let pos = rhs.position();
if self if self
.call_fn_raw(None, fn_lib, "==", args, def_value, pos, level)? .call_fn_raw(None, state, "==", args, def_value, pos, level)?
.as_bool() .as_bool()
.unwrap_or(false) .unwrap_or(false)
{ {
@ -1173,7 +1182,6 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
fn_lib: &FunctionsLib,
expr: &Expr, expr: &Expr,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -1191,11 +1199,11 @@ impl Engine {
Expr::Property(_, _) => unreachable!(), Expr::Property(_, _) => unreachable!(),
// Statement block // Statement block
Expr::Stmt(stmt, _) => self.eval_stmt(scope, state, fn_lib, stmt, level), Expr::Stmt(stmt, _) => self.eval_stmt(scope, state, stmt, level),
// lhs = rhs // lhs = rhs
Expr::Assignment(lhs, rhs, op_pos) => { Expr::Assignment(lhs, rhs, op_pos) => {
let rhs_val = self.eval_expr(scope, state, fn_lib, rhs, level)?; let rhs_val = self.eval_expr(scope, state, rhs, level)?;
match lhs.as_ref() { match lhs.as_ref() {
// name = rhs // name = rhs
@ -1218,7 +1226,7 @@ impl Engine {
Expr::Index(idx_lhs, idx_expr, op_pos) => { Expr::Index(idx_lhs, idx_expr, op_pos) => {
let new_val = Some(rhs_val); let new_val = Some(rhs_val);
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, state, fn_lib, idx_lhs, idx_expr, true, *op_pos, level, new_val, scope, state, idx_lhs, idx_expr, true, *op_pos, level, new_val,
) )
} }
// dot_lhs.dot_rhs = rhs // dot_lhs.dot_rhs = rhs
@ -1226,7 +1234,7 @@ impl Engine {
Expr::Dot(dot_lhs, dot_rhs, _) => { Expr::Dot(dot_lhs, dot_rhs, _) => {
let new_val = Some(rhs_val); let new_val = Some(rhs_val);
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, state, fn_lib, dot_lhs, dot_rhs, false, *op_pos, level, new_val, scope, state, dot_lhs, dot_rhs, false, *op_pos, level, new_val,
) )
} }
// Error assignment to constant // Error assignment to constant
@ -1245,21 +1253,21 @@ 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.eval_dot_index_chain( Expr::Index(lhs, idx_expr, op_pos) => {
scope, state, fn_lib, lhs, idx_expr, true, *op_pos, level, None, self.eval_dot_index_chain(scope, state, lhs, idx_expr, true, *op_pos, level, None)
), }
// lhs.dot_rhs // lhs.dot_rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(lhs, dot_rhs, op_pos) => self.eval_dot_index_chain( Expr::Dot(lhs, dot_rhs, op_pos) => {
scope, state, fn_lib, lhs, dot_rhs, false, *op_pos, level, None, self.eval_dot_index_chain(scope, state, lhs, dot_rhs, false, *op_pos, level, None)
), }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => Ok(Dynamic(Union::Array(Box::new( Expr::Array(contents, _) => Ok(Dynamic(Union::Array(Box::new(
contents contents
.iter() .iter()
.map(|item| self.eval_expr(scope, state, fn_lib, item, level)) .map(|item| self.eval_expr(scope, state, item, level))
.collect::<Result<Vec<_>, _>>()?, .collect::<Result<Vec<_>, _>>()?,
)))), )))),
@ -1268,7 +1276,7 @@ impl Engine {
contents contents
.iter() .iter()
.map(|(key, expr, _)| { .map(|(key, expr, _)| {
self.eval_expr(scope, state, fn_lib, expr, level) self.eval_expr(scope, state, expr, level)
.map(|val| (key.clone(), val)) .map(|val| (key.clone(), val))
}) })
.collect::<Result<HashMap<_, _>, _>>()?, .collect::<Result<HashMap<_, _>, _>>()?,
@ -1278,21 +1286,21 @@ impl Engine {
Expr::FnCall(fn_name, None, arg_exprs, def_val, pos) => { Expr::FnCall(fn_name, None, arg_exprs, def_val, pos) => {
let mut arg_values = arg_exprs let mut arg_values = arg_exprs
.iter() .iter()
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level)) .map(|expr| self.eval_expr(scope, state, expr, level))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let mut args: Vec<_> = arg_values.iter_mut().collect(); let mut args: Vec<_> = arg_values.iter_mut().collect();
if fn_name.as_ref() == KEYWORD_EVAL if fn_name.as_ref() == KEYWORD_EVAL
&& args.len() == 1 && args.len() == 1
&& !self.has_override(fn_lib, KEYWORD_EVAL) && !self.has_override(state, KEYWORD_EVAL)
{ {
// eval - only in function call style // eval - only in function call style
let prev_len = scope.len(); let prev_len = scope.len();
// Evaluate the text string as a script // Evaluate the text string as a script
let result = let result =
self.eval_script_expr(scope, fn_lib, args[0], arg_exprs[0].position()); self.eval_script_expr(scope, state, args[0], arg_exprs[0].position());
if scope.len() != prev_len { if scope.len() != prev_len {
// IMPORTANT! If the eval defines new variables in the current scope, // IMPORTANT! If the eval defines new variables in the current scope,
@ -1304,7 +1312,7 @@ impl Engine {
} else { } else {
// Normal function call - except for eval (handled above) // Normal function call - except for eval (handled above)
let def_value = def_val.as_deref(); let def_value = def_val.as_deref();
self.exec_fn_call(fn_lib, fn_name, &mut args, def_value, *pos, level) self.exec_fn_call(state, fn_name, &mut args, def_value, *pos, level)
} }
} }
@ -1315,7 +1323,7 @@ impl Engine {
let mut arg_values = arg_exprs let mut arg_values = arg_exprs
.iter() .iter()
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level)) .map(|expr| self.eval_expr(scope, state, expr, level))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let mut args: Vec<_> = arg_values.iter_mut().collect(); let mut args: Vec<_> = arg_values.iter_mut().collect();
@ -1328,7 +1336,7 @@ 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(fn_def) = module.get_qualified_fn_lib(fn_name, args.len(), modules)? { if let Some(fn_def) = module.get_qualified_fn_lib(fn_name, args.len(), modules)? {
self.call_fn_from_lib(None, fn_lib, fn_def, &mut args, *pos, level) self.call_script_fn(None, state, fn_def, &mut args, *pos, level)
} else { } else {
// Then search in Rust functions // Then search in Rust functions
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id())); let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
@ -1342,18 +1350,18 @@ impl Engine {
} }
Expr::In(lhs, rhs, _) => { Expr::In(lhs, rhs, _) => {
self.eval_in_expr(scope, state, fn_lib, lhs.as_ref(), rhs.as_ref(), level) self.eval_in_expr(scope, state, lhs.as_ref(), rhs.as_ref(), level)
} }
Expr::And(lhs, rhs, _) => Ok((self Expr::And(lhs, rhs, _) => Ok((self
.eval_expr(scope, state, fn_lib, lhs.as_ref(), level)? .eval_expr(scope, state, lhs.as_ref(), level)?
.as_bool() .as_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, state, fn_lib, rhs.as_ref(), level)? .eval_expr(scope, state, rhs.as_ref(), level)?
.as_bool() .as_bool()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position()) EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
@ -1361,14 +1369,14 @@ impl Engine {
.into()), .into()),
Expr::Or(lhs, rhs, _) => Ok((self Expr::Or(lhs, rhs, _) => Ok((self
.eval_expr(scope, state, fn_lib, lhs.as_ref(), level)? .eval_expr(scope, state, lhs.as_ref(), level)?
.as_bool() .as_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, state, fn_lib, rhs.as_ref(), level)? .eval_expr(scope, state, rhs.as_ref(), level)?
.as_bool() .as_bool()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
@ -1388,7 +1396,6 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
fn_lib: &FunctionsLib,
stmt: &Stmt, stmt: &Stmt,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -1398,7 +1405,7 @@ impl Engine {
// Expression as statement // Expression as statement
Stmt::Expr(expr) => { Stmt::Expr(expr) => {
let result = self.eval_expr(scope, state, fn_lib, expr, level)?; let result = self.eval_expr(scope, state, expr, level)?;
Ok(if let Expr::Assignment(_, _, _) = *expr.as_ref() { Ok(if let Expr::Assignment(_, _, _) = *expr.as_ref() {
// If it is an assignment, erase the result at the root // If it is an assignment, erase the result at the root
@ -1413,7 +1420,7 @@ impl Engine {
let prev_len = scope.len(); let prev_len = scope.len();
let result = block.iter().try_fold(Default::default(), |_, stmt| { let result = block.iter().try_fold(Default::default(), |_, stmt| {
self.eval_stmt(scope, state, fn_lib, stmt, level) self.eval_stmt(scope, state, stmt, level)
}); });
scope.rewind(prev_len); scope.rewind(prev_len);
@ -1427,14 +1434,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, state, fn_lib, guard, level)? .eval_expr(scope, state, guard, level)?
.as_bool() .as_bool()
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(guard.position()))) .map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(guard.position())))
.and_then(|guard_val| { .and_then(|guard_val| {
if guard_val { if guard_val {
self.eval_stmt(scope, state, fn_lib, if_body, level) self.eval_stmt(scope, state, if_body, level)
} else if let Some(stmt) = else_body { } else if let Some(stmt) = else_body {
self.eval_stmt(scope, state, fn_lib, stmt.as_ref(), level) self.eval_stmt(scope, state, stmt.as_ref(), level)
} else { } else {
Ok(Default::default()) Ok(Default::default())
} }
@ -1442,11 +1449,8 @@ impl Engine {
// While loop // While loop
Stmt::While(guard, body) => loop { Stmt::While(guard, body) => loop {
match self match self.eval_expr(scope, state, guard, level)?.as_bool() {
.eval_expr(scope, state, fn_lib, guard, level)? Ok(true) => match self.eval_stmt(scope, state, body, level) {
.as_bool()
{
Ok(true) => match self.eval_stmt(scope, state, fn_lib, body, level) {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (), EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -1463,7 +1467,7 @@ impl Engine {
// Loop statement // Loop statement
Stmt::Loop(body) => loop { Stmt::Loop(body) => loop {
match self.eval_stmt(scope, state, fn_lib, body, level) { match self.eval_stmt(scope, state, body, level) {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (), EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -1475,7 +1479,7 @@ impl Engine {
// For loop // For loop
Stmt::For(name, expr, body) => { Stmt::For(name, expr, body) => {
let arr = self.eval_expr(scope, state, fn_lib, expr, level)?; let arr = self.eval_expr(scope, state, expr, level)?;
let tid = arr.type_id(); let tid = arr.type_id();
if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| { if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| {
@ -1492,7 +1496,7 @@ impl Engine {
for a in iter_fn(arr) { for a in iter_fn(arr) {
*scope.get_mut(index).0 = a; *scope.get_mut(index).0 = a;
match self.eval_stmt(scope, state, fn_lib, body, level) { match self.eval_stmt(scope, state, body, level) {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (), EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -1522,7 +1526,7 @@ impl Engine {
// Return value // Return value
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(Box::new( Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(Box::new(
EvalAltResult::Return(self.eval_expr(scope, state, fn_lib, a, level)?, *pos), EvalAltResult::Return(self.eval_expr(scope, state, a, level)?, *pos),
)), )),
// Empty throw // Empty throw
@ -1532,7 +1536,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, state, fn_lib, a, level)?; let val = self.eval_expr(scope, state, a, level)?;
Err(Box::new(EvalAltResult::ErrorRuntime( Err(Box::new(EvalAltResult::ErrorRuntime(
val.take_string().unwrap_or_else(|_| "".to_string()), val.take_string().unwrap_or_else(|_| "".to_string()),
*pos, *pos,
@ -1541,7 +1545,7 @@ impl Engine {
// Let statement // Let statement
Stmt::Let(name, Some(expr), _) => { Stmt::Let(name, Some(expr), _) => {
let val = self.eval_expr(scope, state, fn_lib, expr, level)?; let val = self.eval_expr(scope, state, expr, level)?;
// TODO - avoid copying variable name in inner block? // TODO - avoid copying variable name in inner block?
let var_name = name.as_ref().clone(); let var_name = name.as_ref().clone();
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false); scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
@ -1557,7 +1561,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, state, fn_lib, expr, level)?; let val = self.eval_expr(scope, state, expr, level)?;
// TODO - avoid copying variable name in inner block? // TODO - avoid copying variable name in inner block?
let var_name = name.as_ref().clone(); let var_name = name.as_ref().clone();
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true); scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
@ -1575,7 +1579,7 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
if let Some(path) = self if let Some(path) = self
.eval_expr(scope, state, fn_lib, expr, level)? .eval_expr(scope, state, expr, level)?
.try_cast::<String>() .try_cast::<String>()
{ {
if let Some(resolver) = self.module_resolver.as_ref() { if let Some(resolver) = self.module_resolver.as_ref() {

View File

@ -117,6 +117,16 @@ impl<T: Default + Clone> StaticVec<T> {
self.list[..num].iter().chain(self.more.iter()) self.list[..num].iter().chain(self.more.iter())
} }
/// Get a mutable iterator to entries in the `StaticVec`.
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
let num = if self.len >= self.list.len() {
self.list.len()
} else {
self.len
};
self.list[..num].iter_mut().chain(self.more.iter_mut())
}
} }
impl<T: Default + Clone + fmt::Debug> fmt::Debug for StaticVec<T> { impl<T: Default + Clone + fmt::Debug> fmt::Debug for StaticVec<T> {