Limit function calls depth.
This commit is contained in:
parent
cc8554d095
commit
337a96394f
12
README.md
12
README.md
@ -651,6 +651,18 @@ fn main() -> Result<(), EvalAltResult>
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Engine configuration options
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
| Method | Description |
|
||||||
|
| ------------------------ | ---------------------------------------------------------------------------------------- |
|
||||||
|
| `set_optimization_level` | Set the amount of script _optimizations_ performed. See [`script optimization`]. |
|
||||||
|
| `set_max_call_levels` | Set the maximum number of function call levels (default 50) to avoid infinite recursion. |
|
||||||
|
|
||||||
|
[`script optimization`]: #script-optimization
|
||||||
|
|
||||||
|
-------
|
||||||
|
|
||||||
Rhai Language Guide
|
Rhai Language Guide
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
12
src/api.rs
12
src/api.rs
@ -726,11 +726,9 @@ impl<'e> Engine<'e> {
|
|||||||
statements
|
statements
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result = ().into_dynamic();
|
let result = statements.iter().try_fold(().into_dynamic(), |_, stmt| {
|
||||||
|
engine.eval_stmt(scope, stmt, 0)
|
||||||
for stmt in statements {
|
})?;
|
||||||
result = engine.eval_stmt(scope, stmt)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !retain_functions {
|
if !retain_functions {
|
||||||
engine.clear_functions();
|
engine.clear_functions();
|
||||||
@ -829,7 +827,7 @@ impl<'e> Engine<'e> {
|
|||||||
|
|
||||||
let result = statements
|
let result = statements
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into_dynamic(), |_, o| self.eval_stmt(scope, o))
|
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0))
|
||||||
.map(|_| ());
|
.map(|_| ());
|
||||||
|
|
||||||
if !retain_functions {
|
if !retain_functions {
|
||||||
@ -889,7 +887,7 @@ impl<'e> Engine<'e> {
|
|||||||
mut values: Vec<Dynamic>,
|
mut values: Vec<Dynamic>,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
let mut values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect();
|
let mut values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect();
|
||||||
engine.call_fn_raw(name, &mut values, None, Position::none())
|
engine.call_fn_raw(name, &mut values, None, Position::none(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
call_fn_internal(self, name, args.into_vec()).and_then(|b| {
|
call_fn_internal(self, name, args.into_vec()).and_then(|b| {
|
||||||
|
234
src/engine.rs
234
src/engine.rs
@ -32,6 +32,7 @@ pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, EvalAltRes
|
|||||||
|
|
||||||
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||||
|
|
||||||
|
pub(crate) const MAX_CALL_STACK_DEPTH: usize = 50;
|
||||||
pub(crate) const KEYWORD_PRINT: &str = "print";
|
pub(crate) const KEYWORD_PRINT: &str = "print";
|
||||||
pub(crate) const KEYWORD_DEBUG: &str = "debug";
|
pub(crate) const KEYWORD_DEBUG: &str = "debug";
|
||||||
pub(crate) const KEYWORD_DUMP_AST: &str = "dump_ast";
|
pub(crate) const KEYWORD_DUMP_AST: &str = "dump_ast";
|
||||||
@ -127,22 +128,26 @@ impl FunctionsLib {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Engine<'e> {
|
pub struct Engine<'e> {
|
||||||
/// Optimize the AST after compilation
|
/// A hashmap containing all compiled functions known to the engine.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
pub(crate) optimization_level: OptimizationLevel,
|
|
||||||
/// A hashmap containing all compiled functions known to the engine
|
|
||||||
pub(crate) functions: HashMap<FnSpec<'e>, Box<FnAny>>,
|
pub(crate) functions: HashMap<FnSpec<'e>, Box<FnAny>>,
|
||||||
/// A hashmap containing all script-defined functions
|
/// A hashmap containing all script-defined functions.
|
||||||
pub(crate) fn_lib: FunctionsLib,
|
pub(crate) fn_lib: FunctionsLib,
|
||||||
/// A hashmap containing all iterators known to the engine
|
/// A hashmap containing all iterators known to the engine.
|
||||||
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
||||||
/// A hashmap mapping type names to pretty-print names
|
/// A hashmap mapping type names to pretty-print names.
|
||||||
pub(crate) type_names: HashMap<String, String>,
|
pub(crate) type_names: HashMap<String, String>,
|
||||||
|
|
||||||
/// Closure for implementing the print commands
|
/// Closure for implementing the print commands.
|
||||||
pub(crate) on_print: Box<dyn FnMut(&str) + 'e>,
|
pub(crate) on_print: Box<dyn FnMut(&str) + 'e>,
|
||||||
/// Closure for implementing the debug commands
|
/// Closure for implementing the debug commands.
|
||||||
pub(crate) on_debug: Box<dyn FnMut(&str) + 'e>,
|
pub(crate) on_debug: Box<dyn FnMut(&str) + 'e>,
|
||||||
|
|
||||||
|
/// Optimize the AST after compilation.
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
pub(crate) optimization_level: OptimizationLevel,
|
||||||
|
|
||||||
|
/// Maximum levels of call-stack to prevent infinite recursion.
|
||||||
|
pub(crate) max_call_stack_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Engine<'_> {
|
impl Default for Engine<'_> {
|
||||||
@ -174,6 +179,8 @@ impl Default for Engine<'_> {
|
|||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[cfg(feature = "optimize_full")]
|
#[cfg(feature = "optimize_full")]
|
||||||
optimization_level: OptimizationLevel::Full,
|
optimization_level: OptimizationLevel::Full,
|
||||||
|
|
||||||
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
engine.register_core_lib();
|
engine.register_core_lib();
|
||||||
@ -197,6 +204,12 @@ impl Engine<'_> {
|
|||||||
self.optimization_level = optimization_level
|
self.optimization_level = optimization_level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the maximum levels of function calls allowed for a script in order to avoid
|
||||||
|
/// infinite recursion and stack overflows.
|
||||||
|
pub fn set_max_call_levels(&mut self, levels: usize) {
|
||||||
|
self.max_call_stack_depth = levels
|
||||||
|
}
|
||||||
|
|
||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
pub(crate) fn call_ext_fn_raw(
|
pub(crate) fn call_ext_fn_raw(
|
||||||
@ -227,7 +240,12 @@ impl Engine<'_> {
|
|||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
|
if level >= self.max_call_stack_depth {
|
||||||
|
return Err(EvalAltResult::ErrorStackOverflow(pos));
|
||||||
|
}
|
||||||
|
|
||||||
// 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) = self.fn_lib.get_function(fn_name, args.len()) {
|
if let Some(fn_def) = self.fn_lib.get_function(fn_name, args.len()) {
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
@ -241,11 +259,11 @@ impl Engine<'_> {
|
|||||||
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Evaluate
|
// Evaluate the function at one higher level of call depth
|
||||||
// Convert return statement to return value
|
|
||||||
return self
|
return self
|
||||||
.eval_stmt(&mut scope, &fn_def.body)
|
.eval_stmt(&mut scope, &fn_def.body, level + 1)
|
||||||
.or_else(|err| match err {
|
.or_else(|err| match err {
|
||||||
|
// 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)),
|
||||||
});
|
});
|
||||||
@ -336,6 +354,7 @@ impl Engine<'_> {
|
|||||||
src: Option<ScopeSource>,
|
src: Option<ScopeSource>,
|
||||||
target: Option<&mut Variant>,
|
target: Option<&mut Variant>,
|
||||||
dot_rhs: &Expr,
|
dot_rhs: &Expr,
|
||||||
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
// Get the `this` reference. Either `src` or `target` should be `Some`.
|
// Get the `this` reference. Either `src` or `target` should be `Some`.
|
||||||
fn get_this_ptr<'a>(
|
fn get_this_ptr<'a>(
|
||||||
@ -360,7 +379,7 @@ impl Engine<'_> {
|
|||||||
Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => {
|
Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => {
|
||||||
let mut values = arg_expr_list
|
let mut values = arg_expr_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg_expr| self.eval_expr(scope, arg_expr))
|
.map(|arg_expr| self.eval_expr(scope, arg_expr, level))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
let this_ptr = get_this_ptr(scope, src, target);
|
let this_ptr = get_this_ptr(scope, src, target);
|
||||||
@ -369,14 +388,14 @@ impl Engine<'_> {
|
|||||||
.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)
|
self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
let this_ptr = get_this_ptr(scope, src, target);
|
let this_ptr = get_this_ptr(scope, src, target);
|
||||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// xxx.idx_lhs[idx_expr]
|
// xxx.idx_lhs[idx_expr]
|
||||||
@ -388,13 +407,13 @@ impl Engine<'_> {
|
|||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
let this_ptr = get_this_ptr(scope, src, target);
|
let this_ptr = get_this_ptr(scope, src, target);
|
||||||
(
|
(
|
||||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)?,
|
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?,
|
||||||
*pos,
|
*pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// xxx.???[???][idx_expr]
|
// xxx.???[???][idx_expr]
|
||||||
Expr::Index(_, _, _) => (
|
Expr::Index(_, _, _) => (
|
||||||
self.get_dot_val_helper(scope, src, target, idx_lhs)?,
|
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?,
|
||||||
*op_pos,
|
*op_pos,
|
||||||
),
|
),
|
||||||
// Syntax error
|
// Syntax error
|
||||||
@ -406,7 +425,7 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||||
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
|
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
@ -418,9 +437,9 @@ impl Engine<'_> {
|
|||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
let this_ptr = get_this_ptr(scope, src, target);
|
let this_ptr = get_this_ptr(scope, src, target);
|
||||||
|
|
||||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs)
|
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs, level)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// xxx.idx_lhs[idx_expr].rhs
|
// xxx.idx_lhs[idx_expr].rhs
|
||||||
@ -432,13 +451,13 @@ impl Engine<'_> {
|
|||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
let this_ptr = get_this_ptr(scope, src, target);
|
let this_ptr = get_this_ptr(scope, src, target);
|
||||||
(
|
(
|
||||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)?,
|
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?,
|
||||||
*pos,
|
*pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// xxx.???[???][idx_expr].rhs
|
// xxx.???[???][idx_expr].rhs
|
||||||
Expr::Index(_, _, _) => (
|
Expr::Index(_, _, _) => (
|
||||||
self.get_dot_val_helper(scope, src, target, idx_lhs)?,
|
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?,
|
||||||
*op_pos,
|
*op_pos,
|
||||||
),
|
),
|
||||||
// Syntax error
|
// Syntax error
|
||||||
@ -450,10 +469,10 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||||
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
|
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
|
||||||
.and_then(|(mut v, _)| {
|
.and_then(|(mut v, _)| {
|
||||||
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs)
|
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs, level)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Syntax error
|
// Syntax error
|
||||||
@ -477,6 +496,7 @@ impl Engine<'_> {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
dot_lhs: &Expr,
|
dot_lhs: &Expr,
|
||||||
dot_rhs: &Expr,
|
dot_rhs: &Expr,
|
||||||
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.???
|
// id.???
|
||||||
@ -488,15 +508,16 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// This is a variable property access (potential function call).
|
// This is a variable property access (potential function call).
|
||||||
// Use a direct index into `scope` to directly mutate the variable value.
|
// Use a direct index into `scope` to directly mutate the variable value.
|
||||||
self.get_dot_val_helper(scope, Some(entry), None, dot_rhs)
|
self.get_dot_val_helper(scope, Some(entry), None, dot_rhs, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// idx_lhs[idx_expr].???
|
// idx_lhs[idx_expr].???
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let (src_type, src, idx, mut target) =
|
let (src_type, src, idx, mut target) =
|
||||||
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos)?;
|
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?;
|
||||||
let val = self.get_dot_val_helper(scope, None, Some(target.as_mut()), dot_rhs);
|
let this_ptr = target.as_mut();
|
||||||
|
let val = self.get_dot_val_helper(scope, None, Some(this_ptr), dot_rhs, level);
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||||
if let Some(src) = src {
|
if let Some(src) = src {
|
||||||
@ -524,8 +545,9 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// {expr}.???
|
// {expr}.???
|
||||||
expr => {
|
expr => {
|
||||||
let mut target = self.eval_expr(scope, expr)?;
|
let mut target = self.eval_expr(scope, expr, level)?;
|
||||||
self.get_dot_val_helper(scope, None, Some(target.as_mut()), dot_rhs)
|
let this_ptr = target.as_mut();
|
||||||
|
self.get_dot_val_helper(scope, None, Some(this_ptr), dot_rhs, level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -549,8 +571,9 @@ impl Engine<'_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
idx_expr: &Expr,
|
idx_expr: &Expr,
|
||||||
|
level: usize,
|
||||||
) -> Result<INT, EvalAltResult> {
|
) -> Result<INT, EvalAltResult> {
|
||||||
self.eval_expr(scope, idx_expr)?
|
self.eval_expr(scope, idx_expr, level)?
|
||||||
.downcast::<INT>()
|
.downcast::<INT>()
|
||||||
.map(|v| *v)
|
.map(|v| *v)
|
||||||
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))
|
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))
|
||||||
@ -612,8 +635,9 @@ impl Engine<'_> {
|
|||||||
lhs: &'a Expr,
|
lhs: &'a Expr,
|
||||||
idx_expr: &Expr,
|
idx_expr: &Expr,
|
||||||
op_pos: Position,
|
op_pos: Position,
|
||||||
|
level: usize,
|
||||||
) -> Result<(IndexSourceType, Option<ScopeSource<'a>>, usize, Dynamic), EvalAltResult> {
|
) -> Result<(IndexSourceType, Option<ScopeSource<'a>>, usize, Dynamic), EvalAltResult> {
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
// id[idx_expr]
|
// id[idx_expr]
|
||||||
@ -638,7 +662,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// (expr)[idx_expr]
|
// (expr)[idx_expr]
|
||||||
expr => {
|
expr => {
|
||||||
let val = self.eval_expr(scope, expr)?;
|
let val = self.eval_expr(scope, expr, level)?;
|
||||||
|
|
||||||
self.get_indexed_value(&val, idx, idx_expr.position(), op_pos)
|
self.get_indexed_value(&val, idx, idx_expr.position(), op_pos)
|
||||||
.map(|(v, _)| (IndexSourceType::Expression, None, idx as usize, v))
|
.map(|(v, _)| (IndexSourceType::Expression, None, idx as usize, v))
|
||||||
@ -727,18 +751,14 @@ impl Engine<'_> {
|
|||||||
this_ptr: &mut Variant,
|
this_ptr: &mut Variant,
|
||||||
dot_rhs: &Expr,
|
dot_rhs: &Expr,
|
||||||
new_val: (&mut Dynamic, Position),
|
new_val: (&mut Dynamic, Position),
|
||||||
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match dot_rhs {
|
match dot_rhs {
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||||
|
let mut args = [this_ptr, new_val.0.as_mut()];
|
||||||
self.call_fn_raw(
|
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||||
&set_fn_name,
|
|
||||||
&mut [this_ptr, new_val.0.as_mut()],
|
|
||||||
None,
|
|
||||||
*pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// xxx.lhs[idx_expr]
|
// xxx.lhs[idx_expr]
|
||||||
@ -749,9 +769,9 @@ impl Engine<'_> {
|
|||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
|
|
||||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||||
.and_then(|v| {
|
.and_then(|v| {
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||||
Self::update_indexed_value(
|
Self::update_indexed_value(
|
||||||
v,
|
v,
|
||||||
idx as usize,
|
idx as usize,
|
||||||
@ -761,7 +781,8 @@ impl Engine<'_> {
|
|||||||
})
|
})
|
||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||||
self.call_fn_raw(&set_fn_name, &mut [this_ptr, v.as_mut()], None, *pos)
|
let mut args = [this_ptr, v.as_mut()];
|
||||||
|
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,15 +799,15 @@ impl Engine<'_> {
|
|||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
|
|
||||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
self.set_dot_val_helper(scope, v.as_mut(), rhs, new_val)
|
self.set_dot_val_helper(scope, v.as_mut(), rhs, new_val, level)
|
||||||
.map(|_| v) // Discard Ok return value
|
.map(|_| v) // Discard Ok return value
|
||||||
})
|
})
|
||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||||
|
let mut args = [this_ptr, v.as_mut()];
|
||||||
self.call_fn_raw(&set_fn_name, &mut [this_ptr, v.as_mut()], None, *pos)
|
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,26 +819,23 @@ impl Engine<'_> {
|
|||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
|
|
||||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||||
.and_then(|v| {
|
.and_then(|v| {
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||||
let (mut target, _) =
|
let (mut target, _) =
|
||||||
self.get_indexed_value(&v, idx, idx_expr.position(), *op_pos)?;
|
self.get_indexed_value(&v, idx, idx_expr.position(), *op_pos)?;
|
||||||
|
|
||||||
let val_pos = new_val.1;
|
let val_pos = new_val.1;
|
||||||
self.set_dot_val_helper(scope, target.as_mut(), rhs, new_val)?;
|
let this_ptr = target.as_mut();
|
||||||
|
self.set_dot_val_helper(scope, this_ptr, rhs, new_val, level)?;
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||||
Self::update_indexed_value(v, idx as usize, target, val_pos)
|
Self::update_indexed_value(v, idx as usize, target, val_pos)
|
||||||
})
|
})
|
||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||||
self.call_fn_raw(
|
let mut args = [this_ptr, v.as_mut()];
|
||||||
&set_fn_name,
|
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||||
&mut [this_ptr, v.as_mut()],
|
|
||||||
None,
|
|
||||||
*pos,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,6 +869,7 @@ impl Engine<'_> {
|
|||||||
dot_rhs: &Expr,
|
dot_rhs: &Expr,
|
||||||
new_val: (&mut Dynamic, Position),
|
new_val: (&mut Dynamic, Position),
|
||||||
op_pos: Position,
|
op_pos: Position,
|
||||||
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.???
|
// id.???
|
||||||
@ -865,8 +884,8 @@ impl Engine<'_> {
|
|||||||
_ => {
|
_ => {
|
||||||
// Avoid referencing scope which is used below as mut
|
// Avoid referencing scope which is used below as mut
|
||||||
let entry = ScopeSource { name: id, ..entry };
|
let entry = ScopeSource { name: id, ..entry };
|
||||||
|
let this_ptr = target.as_mut();
|
||||||
let val = self.set_dot_val_helper(scope, target.as_mut(), dot_rhs, new_val);
|
let val = self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level);
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||||
*scope.get_mut(entry) = target;
|
*scope.get_mut(entry) = target;
|
||||||
@ -881,9 +900,10 @@ impl Engine<'_> {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(lhs, idx_expr, op_pos) => {
|
Expr::Index(lhs, idx_expr, op_pos) => {
|
||||||
let (src_type, src, idx, mut target) =
|
let (src_type, src, idx, mut target) =
|
||||||
self.eval_index_expr(scope, lhs, idx_expr, *op_pos)?;
|
self.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)?;
|
||||||
let val_pos = new_val.1;
|
let val_pos = new_val.1;
|
||||||
let val = self.set_dot_val_helper(scope, target.as_mut(), dot_rhs, new_val);
|
let this_ptr = target.as_mut();
|
||||||
|
let val = self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level);
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||||
if let Some(src) = src {
|
if let Some(src) = src {
|
||||||
@ -918,7 +938,12 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an expression
|
/// Evaluate an expression
|
||||||
fn eval_expr(&mut self, scope: &mut Scope, expr: &Expr) -> Result<Dynamic, EvalAltResult> {
|
fn eval_expr(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
expr: &Expr,
|
||||||
|
level: usize,
|
||||||
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match expr {
|
match expr {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Expr::FloatConstant(f, _) => Ok(f.into_dynamic()),
|
Expr::FloatConstant(f, _) => Ok(f.into_dynamic()),
|
||||||
@ -932,15 +957,15 @@ impl Engine<'_> {
|
|||||||
// lhs[idx_expr]
|
// lhs[idx_expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(lhs, idx_expr, op_pos) => self
|
Expr::Index(lhs, idx_expr, op_pos) => self
|
||||||
.eval_index_expr(scope, lhs, idx_expr, *op_pos)
|
.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)
|
||||||
.map(|(_, _, _, x)| x),
|
.map(|(_, _, _, x)| x),
|
||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt),
|
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt, level),
|
||||||
|
|
||||||
// lhs = rhs
|
// lhs = rhs
|
||||||
Expr::Assignment(lhs, rhs, op_pos) => {
|
Expr::Assignment(lhs, rhs, op_pos) => {
|
||||||
let mut rhs_val = self.eval_expr(scope, rhs)?;
|
let mut rhs_val = self.eval_expr(scope, rhs, level)?;
|
||||||
|
|
||||||
match lhs.as_ref() {
|
match lhs.as_ref() {
|
||||||
// name = rhs
|
// name = rhs
|
||||||
@ -974,7 +999,7 @@ impl Engine<'_> {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let (src_type, src, idx, _) =
|
let (src_type, src, idx, _) =
|
||||||
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos)?;
|
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?;
|
||||||
|
|
||||||
if let Some(src) = src {
|
if let Some(src) = src {
|
||||||
match src.typ {
|
match src.typ {
|
||||||
@ -1006,6 +1031,7 @@ impl Engine<'_> {
|
|||||||
dot_rhs,
|
dot_rhs,
|
||||||
(&mut rhs_val, rhs.position()),
|
(&mut rhs_val, rhs.position()),
|
||||||
*op_pos,
|
*op_pos,
|
||||||
|
level,
|
||||||
),
|
),
|
||||||
|
|
||||||
// Error assignment to constant
|
// Error assignment to constant
|
||||||
@ -1019,15 +1045,15 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs),
|
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs, level),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(contents, _) => {
|
Expr::Array(contents, _) => {
|
||||||
let mut arr = Vec::new();
|
let mut arr = Vec::new();
|
||||||
|
|
||||||
for item in contents {
|
contents.into_iter().try_for_each(|item| {
|
||||||
arr.push(self.eval_expr(scope, item)?);
|
self.eval_expr(scope, item, level).map(|val| arr.push(val))
|
||||||
}
|
})?;
|
||||||
|
|
||||||
Ok(Box::new(arr))
|
Ok(Box::new(arr))
|
||||||
}
|
}
|
||||||
@ -1053,25 +1079,22 @@ impl Engine<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Change the argument to a debug dump of the expressions
|
// Change the argument to a debug dump of the expressions
|
||||||
let result = args_expr_list
|
let mut result = args_expr_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| format!("{:#?}", expr))
|
.map(|expr| format!("{:#?}", expr))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
.into_dynamic();
|
||||||
|
|
||||||
// Redirect call to `print`
|
// Redirect call to `print`
|
||||||
self.call_fn_raw(
|
self.call_fn_raw(KEYWORD_PRINT, &mut [result.as_mut()], None, pos, level)
|
||||||
KEYWORD_PRINT,
|
|
||||||
&mut [result.join("\n").into_dynamic().as_mut()],
|
|
||||||
None,
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// type_of
|
// type_of
|
||||||
KEYWORD_TYPE_OF
|
KEYWORD_TYPE_OF
|
||||||
if args_expr_list.len() == 1 && !has_override(self, KEYWORD_TYPE_OF) =>
|
if args_expr_list.len() == 1 && !has_override(self, KEYWORD_TYPE_OF) =>
|
||||||
{
|
{
|
||||||
let r = self.eval_expr(scope, &args_expr_list[0])?;
|
let r = self.eval_expr(scope, &args_expr_list[0], level)?;
|
||||||
Ok(self
|
Ok(self
|
||||||
.map_type_name((*r).type_name())
|
.map_type_name((*r).type_name())
|
||||||
.to_string()
|
.to_string()
|
||||||
@ -1083,7 +1106,7 @@ impl Engine<'_> {
|
|||||||
if args_expr_list.len() == 1 && !has_override(self, KEYWORD_EVAL) =>
|
if args_expr_list.len() == 1 && !has_override(self, KEYWORD_EVAL) =>
|
||||||
{
|
{
|
||||||
let pos = args_expr_list[0].position();
|
let pos = args_expr_list[0].position();
|
||||||
let r = self.eval_expr(scope, &args_expr_list[0])?;
|
let r = self.eval_expr(scope, &args_expr_list[0], level)?;
|
||||||
|
|
||||||
let script =
|
let script =
|
||||||
r.downcast_ref::<String>()
|
r.downcast_ref::<String>()
|
||||||
@ -1118,27 +1141,27 @@ impl Engine<'_> {
|
|||||||
_ => {
|
_ => {
|
||||||
let mut values = args_expr_list
|
let mut values = args_expr_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, expr))
|
.map(|expr| self.eval_expr(scope, expr, level))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
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)
|
self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos, level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::And(lhs, rhs) => Ok(Box::new(
|
Expr::And(lhs, rhs) => Ok(Box::new(
|
||||||
*self
|
*self
|
||||||
.eval_expr(scope, &*lhs)?
|
.eval_expr(scope, &*lhs, level)?
|
||||||
.downcast::<bool>()
|
.downcast::<bool>()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
&& // Short-circuit using &&
|
&& // Short-circuit using &&
|
||||||
*self
|
*self
|
||||||
.eval_expr(scope, &*rhs)?
|
.eval_expr(scope, &*rhs, level)?
|
||||||
.downcast::<bool>()
|
.downcast::<bool>()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
||||||
@ -1147,14 +1170,14 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
Expr::Or(lhs, rhs) => Ok(Box::new(
|
Expr::Or(lhs, rhs) => Ok(Box::new(
|
||||||
*self
|
*self
|
||||||
.eval_expr(scope, &*lhs)?
|
.eval_expr(scope, &*lhs, level)?
|
||||||
.downcast::<bool>()
|
.downcast::<bool>()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
|| // Short-circuit using ||
|
|| // Short-circuit using ||
|
||||||
*self
|
*self
|
||||||
.eval_expr(scope, &*rhs)?
|
.eval_expr(scope, &*rhs, level)?
|
||||||
.downcast::<bool>()
|
.downcast::<bool>()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
||||||
@ -1172,6 +1195,7 @@ impl Engine<'_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match stmt {
|
match stmt {
|
||||||
// No-op
|
// No-op
|
||||||
@ -1179,7 +1203,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// Expression as statement
|
// Expression as statement
|
||||||
Stmt::Expr(expr) => {
|
Stmt::Expr(expr) => {
|
||||||
let result = self.eval_expr(scope, expr)?;
|
let result = self.eval_expr(scope, expr, level)?;
|
||||||
|
|
||||||
Ok(if !matches!(expr.as_ref(), Expr::Assignment(_, _, _)) {
|
Ok(if !matches!(expr.as_ref(), Expr::Assignment(_, _, _)) {
|
||||||
result
|
result
|
||||||
@ -1192,15 +1216,10 @@ impl Engine<'_> {
|
|||||||
// Block scope
|
// Block scope
|
||||||
Stmt::Block(block, _) => {
|
Stmt::Block(block, _) => {
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let mut result: Result<Dynamic, EvalAltResult> = Ok(().into_dynamic());
|
|
||||||
|
|
||||||
for stmt in block.iter() {
|
let result = block.iter().try_fold(().into_dynamic(), |_, stmt| {
|
||||||
result = self.eval_stmt(scope, stmt);
|
self.eval_stmt(scope, stmt, level)
|
||||||
|
});
|
||||||
if result.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.rewind(prev_len);
|
scope.rewind(prev_len);
|
||||||
|
|
||||||
@ -1209,14 +1228,14 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// If-else statement
|
// If-else statement
|
||||||
Stmt::IfThenElse(guard, if_body, else_body) => self
|
Stmt::IfThenElse(guard, if_body, else_body) => self
|
||||||
.eval_expr(scope, guard)?
|
.eval_expr(scope, guard, level)?
|
||||||
.downcast::<bool>()
|
.downcast::<bool>()
|
||||||
.map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position()))
|
.map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position()))
|
||||||
.and_then(|guard_val| {
|
.and_then(|guard_val| {
|
||||||
if *guard_val {
|
if *guard_val {
|
||||||
self.eval_stmt(scope, if_body)
|
self.eval_stmt(scope, if_body, level)
|
||||||
} else if let Some(stmt) = else_body {
|
} else if let Some(stmt) = else_body {
|
||||||
self.eval_stmt(scope, stmt.as_ref())
|
self.eval_stmt(scope, stmt.as_ref(), level)
|
||||||
} else {
|
} else {
|
||||||
Ok(().into_dynamic())
|
Ok(().into_dynamic())
|
||||||
}
|
}
|
||||||
@ -1224,10 +1243,10 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
Stmt::While(guard, body) => loop {
|
Stmt::While(guard, body) => loop {
|
||||||
match self.eval_expr(scope, guard)?.downcast::<bool>() {
|
match self.eval_expr(scope, guard, level)?.downcast::<bool>() {
|
||||||
Ok(guard_val) => {
|
Ok(guard_val) => {
|
||||||
if *guard_val {
|
if *guard_val {
|
||||||
match self.eval_stmt(scope, body) {
|
match self.eval_stmt(scope, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(EvalAltResult::ErrorLoopBreak(_)) => {
|
Err(EvalAltResult::ErrorLoopBreak(_)) => {
|
||||||
return Ok(().into_dynamic())
|
return Ok(().into_dynamic())
|
||||||
@ -1244,7 +1263,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// Loop statement
|
// Loop statement
|
||||||
Stmt::Loop(body) => loop {
|
Stmt::Loop(body) => loop {
|
||||||
match self.eval_stmt(scope, body) {
|
match self.eval_stmt(scope, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(EvalAltResult::ErrorLoopBreak(_)) => return Ok(().into_dynamic()),
|
Err(EvalAltResult::ErrorLoopBreak(_)) => return Ok(().into_dynamic()),
|
||||||
Err(x) => return Err(x),
|
Err(x) => return Err(x),
|
||||||
@ -1253,7 +1272,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(name, expr, body) => {
|
Stmt::For(name, expr, body) => {
|
||||||
let arr = self.eval_expr(scope, expr)?;
|
let arr = self.eval_expr(scope, expr, level)?;
|
||||||
let tid = Any::type_id(&*arr);
|
let tid = Any::type_id(&*arr);
|
||||||
|
|
||||||
if let Some(iter_fn) = self.type_iterators.get(&tid) {
|
if let Some(iter_fn) = self.type_iterators.get(&tid) {
|
||||||
@ -1268,7 +1287,7 @@ impl Engine<'_> {
|
|||||||
for a in iter_fn(&arr) {
|
for a in iter_fn(&arr) {
|
||||||
*scope.get_mut(entry) = a;
|
*scope.get_mut(entry) = a;
|
||||||
|
|
||||||
match self.eval_stmt(scope, body) {
|
match self.eval_stmt(scope, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(EvalAltResult::ErrorLoopBreak(_)) => break,
|
Err(EvalAltResult::ErrorLoopBreak(_)) => break,
|
||||||
Err(x) => return Err(x),
|
Err(x) => return Err(x),
|
||||||
@ -1291,9 +1310,10 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return value
|
// Return value
|
||||||
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => {
|
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(EvalAltResult::Return(
|
||||||
Err(EvalAltResult::Return(self.eval_expr(scope, a)?, *pos))
|
self.eval_expr(scope, a, level)?,
|
||||||
}
|
*pos,
|
||||||
|
)),
|
||||||
|
|
||||||
// Empty throw
|
// Empty throw
|
||||||
Stmt::ReturnWithVal(None, ReturnType::Exception, pos) => {
|
Stmt::ReturnWithVal(None, ReturnType::Exception, pos) => {
|
||||||
@ -1302,7 +1322,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// Throw value
|
// Throw value
|
||||||
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
|
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
|
||||||
let val = self.eval_expr(scope, a)?;
|
let val = self.eval_expr(scope, a, level)?;
|
||||||
Err(EvalAltResult::ErrorRuntime(
|
Err(EvalAltResult::ErrorRuntime(
|
||||||
val.downcast::<String>()
|
val.downcast::<String>()
|
||||||
.map(|s| *s)
|
.map(|s| *s)
|
||||||
@ -1313,7 +1333,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// Let statement
|
// Let statement
|
||||||
Stmt::Let(name, Some(expr), _) => {
|
Stmt::Let(name, Some(expr), _) => {
|
||||||
let val = self.eval_expr(scope, expr)?;
|
let val = self.eval_expr(scope, expr, level)?;
|
||||||
scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false);
|
scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false);
|
||||||
Ok(().into_dynamic())
|
Ok(().into_dynamic())
|
||||||
}
|
}
|
||||||
@ -1325,7 +1345,7 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// Const statement
|
// Const statement
|
||||||
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
||||||
let val = self.eval_expr(scope, expr)?;
|
let val = self.eval_expr(scope, expr, level)?;
|
||||||
scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true);
|
scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true);
|
||||||
Ok(().into_dynamic())
|
Ok(().into_dynamic())
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,8 @@ pub enum EvalAltResult {
|
|||||||
ErrorDotExpr(String, Position),
|
ErrorDotExpr(String, Position),
|
||||||
/// Arithmetic error encountered. Wrapped value is the error message.
|
/// Arithmetic error encountered. Wrapped value is the error message.
|
||||||
ErrorArithmetic(String, Position),
|
ErrorArithmetic(String, Position),
|
||||||
|
/// Call stack over maximum limit.
|
||||||
|
ErrorStackOverflow(Position),
|
||||||
/// Run-time error encountered. Wrapped value is the error message.
|
/// Run-time error encountered. Wrapped value is the error message.
|
||||||
ErrorRuntime(String, Position),
|
ErrorRuntime(String, Position),
|
||||||
/// Breaking out of loops - not an error if within a loop.
|
/// Breaking out of loops - not an error if within a loop.
|
||||||
@ -105,6 +107,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorReadingScriptFile(_, _) => "Cannot read from script file",
|
Self::ErrorReadingScriptFile(_, _) => "Cannot read from script file",
|
||||||
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
||||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||||
|
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||||
Self::ErrorLoopBreak(_) => "Break statement not inside a loop",
|
Self::ErrorLoopBreak(_) => "Break statement not inside a loop",
|
||||||
Self::Return(_, _) => "[Not Error] Function returns value",
|
Self::Return(_, _) => "[Not Error] Function returns value",
|
||||||
@ -131,6 +134,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
Self::ErrorDotExpr(s, pos) if !s.is_empty() => write!(f, "{} {} ({})", desc, s, pos),
|
Self::ErrorDotExpr(s, pos) if !s.is_empty() => write!(f, "{} {} ({})", desc, s, pos),
|
||||||
Self::ErrorDotExpr(_, pos) => write!(f, "{} ({})", desc, pos),
|
Self::ErrorDotExpr(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||||
Self::ErrorArithmetic(s, pos) => write!(f, "{} ({})", s, pos),
|
Self::ErrorArithmetic(s, pos) => write!(f, "{} ({})", s, pos),
|
||||||
|
Self::ErrorStackOverflow(pos) => write!(f, "{} ({})", desc, pos),
|
||||||
Self::ErrorRuntime(s, pos) => {
|
Self::ErrorRuntime(s, pos) => {
|
||||||
write!(f, "{} ({})", if s.is_empty() { desc } else { s }, pos)
|
write!(f, "{} ({})", if s.is_empty() { desc } else { s }, pos)
|
||||||
}
|
}
|
||||||
@ -230,6 +234,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorMismatchOutputType(_, pos)
|
| Self::ErrorMismatchOutputType(_, pos)
|
||||||
| Self::ErrorDotExpr(_, pos)
|
| Self::ErrorDotExpr(_, pos)
|
||||||
| Self::ErrorArithmetic(_, pos)
|
| Self::ErrorArithmetic(_, pos)
|
||||||
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::ErrorLoopBreak(pos)
|
| Self::ErrorLoopBreak(pos)
|
||||||
| Self::Return(_, pos) => *pos,
|
| Self::Return(_, pos) => *pos,
|
||||||
@ -260,6 +265,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorMismatchOutputType(_, ref mut pos)
|
| Self::ErrorMismatchOutputType(_, ref mut pos)
|
||||||
| Self::ErrorDotExpr(_, ref mut pos)
|
| Self::ErrorDotExpr(_, ref mut pos)
|
||||||
| Self::ErrorArithmetic(_, ref mut pos)
|
| Self::ErrorArithmetic(_, ref mut pos)
|
||||||
|
| Self::ErrorStackOverflow(ref mut pos)
|
||||||
| Self::ErrorRuntime(_, ref mut pos)
|
| Self::ErrorRuntime(_, ref mut pos)
|
||||||
| Self::ErrorLoopBreak(ref mut pos)
|
| Self::ErrorLoopBreak(ref mut pos)
|
||||||
| Self::Return(_, ref mut pos) => *pos = new_position,
|
| Self::Return(_, ref mut pos) => *pos = new_position,
|
||||||
|
Loading…
Reference in New Issue
Block a user