Minor code cleanup.

This commit is contained in:
Stephen Chung 2020-04-01 09:51:33 +08:00
parent dcf5eaf64d
commit d7ac57c060
5 changed files with 223 additions and 224 deletions

View File

@ -179,7 +179,7 @@ let result = engine.eval::<i64>("40 + 2")?; // return type is i64, specified
let result: i64 = engine.eval("40 + 2")?; // return type is inferred to be i64 let result: i64 = engine.eval("40 + 2")?; // return type is inferred to be i64
let result = engine.eval<String>("40 + 2")?; // returns an error because the actual return type is i64, not String let result = engine.eval::<String>("40 + 2")?; // returns an error because the actual return type is i64, not String
``` ```
Evaluate a script file directly: Evaluate a script file directly:
@ -301,7 +301,7 @@ type_of('c') == "char";
type_of(42) == "i64"; type_of(42) == "i64";
let x = 123; let x = 123;
x.type_of(); // error - 'type_of' cannot use method-call style x.type_of(); // <- error: 'type_of' cannot use method-call style
type_of(x) == "i64"; type_of(x) == "i64";
x = 99.999; x = 99.999;
@ -325,7 +325,7 @@ That's about it. For other conversions, register custom conversion functions.
```rust ```rust
let x = 42; let x = 42;
let y = x * 100.0; // error: cannot multiply i64 with f64 let y = x * 100.0; // <- error: cannot multiply i64 with f64
let y = x.to_float() * 100.0; // works let y = x.to_float() * 100.0; // works
let z = y.to_int() + x; // works let z = y.to_int() + x; // works
@ -750,8 +750,8 @@ let _x = 42; // ok
let x_ = 42; // also ok let x_ = 42; // also ok
let _x_ = 42; // still ok let _x_ = 42; // still ok
let _ = 123; // syntax error - illegal variable name let _ = 123; // <- syntax error: illegal variable name
let _9 = 9; // syntax error - illegal variable name let _9 = 9; // <- syntax error: illegal variable name
let x = 42; // variable is 'x', lower case let x = 42; // variable is 'x', lower case
let X = 123; // variable is 'X', upper case let X = 123; // variable is 'X', upper case
@ -773,13 +773,13 @@ Constants can be defined using the `const` keyword and are immutable. Constants
```rust ```rust
const x = 42; const x = 42;
print(x * 2); // prints 84 print(x * 2); // prints 84
x = 123; // syntax error - cannot assign to constant x = 123; // <- syntax error: cannot assign to constant
``` ```
Constants must be assigned a _value_, not an expression. Constants must be assigned a _value_, not an expression.
```rust ```rust
const x = 40 + 2; // syntax error - cannot assign expression to constant const x = 40 + 2; // <- syntax error: cannot assign expression to constant
``` ```
Numbers Numbers
@ -1299,10 +1299,10 @@ To deliberately return an error during an evaluation, use the `throw` keyword.
```rust ```rust
if some_bad_condition_has_happened { if some_bad_condition_has_happened {
throw error; // 'throw' takes a string to form the exception text throw error; // 'throw' takes a string as the exception text
} }
throw; // empty exception text: "" throw; // defaults to empty exception text: ""
``` ```
Exceptions thrown via `throw` in the script can be captured by matching `Err(EvalAltResult::ErrorRuntime(`_reason_`, `_position_`))` Exceptions thrown via `throw` in the script can be captured by matching `Err(EvalAltResult::ErrorRuntime(`_reason_`, `_position_`))`
@ -1339,8 +1339,9 @@ Just like in Rust, an implicit return can be used. In fact, the last statement o
regardless of whether it is terminated with a semicolon `';'`. This is different from Rust. regardless of whether it is terminated with a semicolon `';'`. This is different from Rust.
```rust ```rust
fn add(x, y) { fn add(x, y) { // implicit return:
x + y; // value of the last statement (no need for ending semicolon) is used as the return value x + y; // value of the last statement (no need for ending semicolon)
// is used as the return value
} }
fn add2(x) { fn add2(x) {
@ -1358,7 +1359,7 @@ Functions can only access their parameters. They cannot access external variabl
```rust ```rust
let x = 42; let x = 42;
fn foo() { x } // syntax error - variable 'x' doesn't exist fn foo() { x } // <- syntax error: variable 'x' doesn't exist
``` ```
### Passing arguments by value ### Passing arguments by value
@ -1426,7 +1427,8 @@ let a = new_ts(); // constructor function
a.field = 500; // property access a.field = 500; // property access
a.update(); // method call a.update(); // method call
update(a); // this works, but 'a' is unchanged because only a COPY of 'a' is passed to 'update' by VALUE update(a); // this works, but 'a' is unchanged because only
// a COPY of 'a' is passed to 'update' by VALUE
``` ```
Custom types, properties and methods can be disabled via the [`no_object`] feature. Custom types, properties and methods can be disabled via the [`no_object`] feature.
@ -1481,14 +1483,13 @@ For example, in the following:
```rust ```rust
{ {
let x = 999; // NOT eliminated - Rhai doesn't check yet whether a variable is used later on let x = 999; // NOT eliminated: Rhai doesn't check yet whether a variable is used later on
123; // eliminated - no effect 123; // eliminated: no effect
"hello"; // eliminated - no effect "hello"; // eliminated: no effect
[1, 2, x, x*2, 5]; // eliminated - no effect [1, 2, x, x*2, 5]; // eliminated: no effect
foo(42); // NOT eliminated - the function 'foo' may have side effects foo(42); // NOT eliminated: the function 'foo' may have side effects
666 // NOT eliminated - this is the return value of the block, 666 // NOT eliminated: this is the return value of the block,
// and the block is the last one // and the block is the last one so this is the return value of the whole script
// so this is the return value of the whole script
} }
``` ```
@ -1603,7 +1604,8 @@ if DECISION == 1 { // is a function call to the '==' function, and it retur
print("boo!"); // this block is eliminated because it is never reached print("boo!"); // this block is eliminated because it is never reached
} }
print("hello!"); // <- the above is equivalent to this ('print' and 'debug' are handled specially) print("hello!"); // <- the above is equivalent to this
// ('print' and 'debug' are handled specially)
``` ```
Because of the eager evaluation of functions, many constant expressions will be evaluated and replaced by the result. Because of the eager evaluation of functions, many constant expressions will be evaluated and replaced by the result.
@ -1612,8 +1614,8 @@ This does not happen with [`OptimizationLevel::Simple`] which doesn't assume all
```rust ```rust
// When compiling the following with OptimizationLevel::Full... // When compiling the following with OptimizationLevel::Full...
let x = (1 + 2) * 3 - 4 / 5 % 6; // <- will be replaced by 'let x = 9' let x = (1+2)*3-4/5%6; // <- will be replaced by 'let x = 9'
let y = (1 > 2) || (3 <= 4); // <- will be replaced by 'let y = true' let y = (1>2) || (3<=4); // <- will be replaced by 'let y = true'
``` ```
Function side effect considerations Function side effect considerations
@ -1704,14 +1706,14 @@ let result = eval(script); // <- look, JS, we can also do this!
print("Answer: " + result); // prints 42 print("Answer: " + result); // prints 42
print("x = " + x); // prints 10 - functions call arguments are passed by value print("x = " + x); // prints 10: functions call arguments are passed by value
print("y = " + y); // prints 32 - variables defined in 'eval' persist! print("y = " + y); // prints 32: variables defined in 'eval' persist!
eval("{ let z = y }"); // to keep a variable local, use a statement block eval("{ let z = y }"); // to keep a variable local, use a statement block
print("z = " + z); // error - variable 'z' not found print("z = " + z); // <- error: variable 'z' not found
"print(42)".eval(); // nope - just like 'type_of', method-call style doesn't work "print(42)".eval(); // <- nope... just like 'type_of', method-call style doesn't work
``` ```
Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_, Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_,

View File

@ -691,15 +691,15 @@ impl<'e> Engine<'e> {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
self.eval_ast_with_scope_raw(scope, false, ast) self.eval_ast_with_scope_raw(scope, false, ast)?
.and_then(|result| { .downcast::<T>()
result.downcast::<T>().map(|v| *v).map_err(|a| { .map(|v| *v)
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(), self.map_type_name((*a).type_name()).to_string(),
Position::none(), Position::none(),
) )
}) })
})
} }
pub(crate) fn eval_ast_with_scope_raw( pub(crate) fn eval_ast_with_scope_raw(
@ -735,7 +735,9 @@ impl<'e> Engine<'e> {
/// Evaluate a file, but throw away the result and only return error (if any). /// Evaluate a file, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
/// ///
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run. /// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run.
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
pub fn consume_file( pub fn consume_file(
&mut self, &mut self,
@ -748,7 +750,9 @@ impl<'e> Engine<'e> {
/// Evaluate a file with own scope, but throw away the result and only return error (if any). /// Evaluate a file with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
/// ///
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run. /// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run.
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
pub fn consume_file_with_scope( pub fn consume_file_with_scope(
&mut self, &mut self,
@ -763,7 +767,9 @@ impl<'e> Engine<'e> {
/// Evaluate a string, but throw away the result and only return error (if any). /// Evaluate a string, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
/// ///
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run. /// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume(&mut self, retain_functions: bool, input: &str) -> Result<(), EvalAltResult> { pub fn consume(&mut self, retain_functions: bool, input: &str) -> Result<(), EvalAltResult> {
self.consume_with_scope(&mut Scope::new(), retain_functions, input) self.consume_with_scope(&mut Scope::new(), retain_functions, input)
} }
@ -771,7 +777,9 @@ impl<'e> Engine<'e> {
/// Evaluate a string with own scope, but throw away the result and only return error (if any). /// Evaluate a string with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
/// ///
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run. /// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume_with_scope( pub fn consume_with_scope(
&mut self, &mut self,
scope: &mut Scope, scope: &mut Scope,
@ -789,7 +797,9 @@ impl<'e> Engine<'e> {
/// Evaluate an AST, but throw away the result and only return error (if any). /// Evaluate an AST, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
/// ///
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run. /// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume_ast(&mut self, retain_functions: bool, ast: &AST) -> Result<(), EvalAltResult> { pub fn consume_ast(&mut self, retain_functions: bool, ast: &AST) -> Result<(), EvalAltResult> {
self.consume_ast_with_scope(&mut Scope::new(), retain_functions, ast) self.consume_ast_with_scope(&mut Scope::new(), retain_functions, ast)
} }
@ -797,7 +807,9 @@ impl<'e> Engine<'e> {
/// Evaluate an `AST` with own scope, but throw away the result and only return error (if any). /// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
/// ///
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run. /// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume_ast_with_scope( pub fn consume_ast_with_scope(
&mut self, &mut self,
scope: &mut Scope, scope: &mut Scope,
@ -833,9 +845,9 @@ impl<'e> Engine<'e> {
&mut self, &mut self,
functions: impl IntoIterator<Item = &'a Arc<FnDef>>, functions: impl IntoIterator<Item = &'a Arc<FnDef>>,
) { ) {
for f in functions.into_iter() { functions.into_iter().cloned().for_each(|f| {
self.fn_lib.add_or_replace_function(f.clone()); self.fn_lib.add_or_replace_function(f);
} });
} }
/// Call a script function retained inside the Engine. /// Call a script function retained inside the Engine.
@ -871,15 +883,15 @@ impl<'e> Engine<'e> {
let mut values = args.into_vec(); let mut values = args.into_vec();
let mut arg_values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect(); let mut arg_values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect();
self.call_fn_raw(name, &mut arg_values, None, Position::none(), 0) self.call_fn_raw(name, &mut arg_values, None, Position::none(), 0)?
.and_then(|b| { .downcast()
b.downcast().map(|b| *b).map_err(|a| { .map(|b| *b)
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(), self.map_type_name((*a).type_name()).into(),
Position::none(), Position::none(),
) )
}) })
})
} }
/// Optimize the `AST` with constants defined in an external Scope. /// Optimize the `AST` with constants defined in an external Scope.

View File

@ -36,15 +36,15 @@ 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 = 64; pub const MAX_CALL_STACK_DEPTH: usize = 64;
pub(crate) const KEYWORD_PRINT: &str = "print"; pub const KEYWORD_PRINT: &str = "print";
pub(crate) const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_DEBUG: &str = "debug";
pub(crate) const KEYWORD_DUMP_AST: &str = "dump_ast"; pub const KEYWORD_DUMP_AST: &str = "dump_ast";
pub(crate) const KEYWORD_TYPE_OF: &str = "type_of"; pub const KEYWORD_TYPE_OF: &str = "type_of";
pub(crate) const KEYWORD_EVAL: &str = "eval"; pub const KEYWORD_EVAL: &str = "eval";
pub(crate) const FUNC_TO_STRING: &str = "to_string"; pub const FUNC_TO_STRING: &str = "to_string";
pub(crate) const FUNC_GETTER: &str = "get$"; pub const FUNC_GETTER: &str = "get$";
pub(crate) const FUNC_SETTER: &str = "set$"; pub const FUNC_SETTER: &str = "set$";
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -317,8 +317,7 @@ impl Engine<'_> {
} }
} }
/// Universal method for calling functions, that are either /// Universal method for calling functions either registered with the `Engine` or written in Rhai
/// registered with the `Engine` or written in Rhai
pub(crate) fn call_fn_raw( pub(crate) fn call_fn_raw(
&mut self, &mut self,
fn_name: &str, fn_name: &str,
@ -370,12 +369,10 @@ impl Engine<'_> {
// See if the function match print/debug (which requires special processing) // See if the function match print/debug (which requires special processing)
return Ok(match fn_name { return Ok(match fn_name {
KEYWORD_PRINT => { KEYWORD_PRINT => {
self.on_print.as_mut()(cast_to_string(result.as_ref(), pos)?); self.on_print.as_mut()(cast_to_string(result.as_ref(), pos)?).into_dynamic()
().into_dynamic()
} }
KEYWORD_DEBUG => { KEYWORD_DEBUG => {
self.on_debug.as_mut()(cast_to_string(result.as_ref(), pos)?); self.on_debug.as_mut()(cast_to_string(result.as_ref(), pos)?).into_dynamic()
().into_dynamic()
} }
_ => result, _ => result,
}); });
@ -400,11 +397,11 @@ impl Engine<'_> {
if let Some(prop) = extract_prop_from_setter(fn_name) { if let Some(prop) = extract_prop_from_setter(fn_name) {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let val = args[1].into_dynamic(); let value = args[1].into_dynamic();
// Map property update // Map property update
if let Some(map) = args[0].downcast_mut::<Map>() { if let Some(map) = args[0].downcast_mut::<Map>() {
map.insert(prop.to_string(), val); map.insert(prop.to_string(), value);
return Ok(().into_dynamic()); return Ok(().into_dynamic());
} }
} }
@ -469,7 +466,7 @@ impl Engine<'_> {
// xxx.idx_lhs[idx_expr] // xxx.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 val = 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 this_ptr = target.get_mut(scope);
@ -488,8 +485,8 @@ impl Engine<'_> {
} }
}; };
self.get_indexed_value(scope, &val, idx_expr, *op_pos, level) self.get_indexed_value(scope, &value, idx_expr, *op_pos, level)
.map(|(v, _, _)| v) .map(|(val, _, _)| val)
} }
// xxx.dot_lhs.rhs // xxx.dot_lhs.rhs
@ -498,8 +495,8 @@ impl Engine<'_> {
Expr::Property(id, pos) => { Expr::Property(id, pos) => {
let this_ptr = target.get_mut(scope); let this_ptr = target.get_mut(scope);
self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0) self.call_fn_raw(&make_getter(id), &mut [this_ptr], None, *pos, 0)
.and_then(|mut v| { .and_then(|mut val| {
self.get_dot_val_helper(scope, Target::from(v.as_mut()), rhs, level) self.get_dot_val_helper(scope, Target::from(val.as_mut()), rhs, level)
}) })
} }
// xxx.idx_lhs[idx_expr].rhs // xxx.idx_lhs[idx_expr].rhs
@ -525,8 +522,8 @@ impl Engine<'_> {
}; };
self.get_indexed_value(scope, &val, idx_expr, *op_pos, level) self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)
.and_then(|(mut v, _, _)| { .and_then(|(mut val, _, _)| {
self.get_dot_val_helper(scope, Target::from(v.as_mut()), rhs, level) self.get_dot_val_helper(scope, Target::from(val.as_mut()), rhs, level)
}) })
} }
// Syntax error // Syntax error
@ -569,10 +566,10 @@ impl Engine<'_> {
// 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 (idx_src_type, src, idx, mut value) = let (idx_src_type, src, idx, mut val) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?; self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?;
let val = let value =
self.get_dot_val_helper(scope, Target::from(value.as_mut()), dot_rhs, level); self.get_dot_val_helper(scope, Target::from(val.as_mut()), 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 {
@ -589,19 +586,19 @@ impl Engine<'_> {
scope, scope,
src, src,
idx, idx,
(value, dot_rhs.position()), (val, dot_rhs.position()),
)?; )?;
} }
} }
} }
val value
} }
// {expr}.??? // {expr}.???
expr => { expr => {
let mut value = self.eval_expr(scope, expr, level)?; let mut val = self.eval_expr(scope, expr, level)?;
self.get_dot_val_helper(scope, Target::from(value.as_mut()), dot_rhs, level) self.get_dot_val_helper(scope, Target::from(val.as_mut()), dot_rhs, level)
} }
} }
} }
@ -629,10 +626,8 @@ impl Engine<'_> {
) -> Result<(Dynamic, IndexSourceType, IndexValue), EvalAltResult> { ) -> Result<(Dynamic, IndexSourceType, IndexValue), EvalAltResult> {
let idx_pos = idx_expr.position(); let idx_pos = idx_expr.position();
if val.is::<Array>() {
// val_array[idx] // val_array[idx]
let arr = val.downcast_ref::<Array>().expect("array expected"); if let Some(arr) = val.downcast_ref::<Array>() {
let idx = *self let idx = *self
.eval_expr(scope, idx_expr, level)? .eval_expr(scope, idx_expr, level)?
.downcast::<INT>() .downcast::<INT>()
@ -650,10 +645,8 @@ impl Engine<'_> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
if val.is::<Map>() {
// val_map[idx] // val_map[idx]
let map = val.downcast_ref::<Map>().expect("array expected"); if let Some(map) = val.downcast_ref::<Map>() {
let idx = *self let idx = *self
.eval_expr(scope, idx_expr, level)? .eval_expr(scope, idx_expr, level)?
.downcast::<String>() .downcast::<String>()
@ -667,10 +660,8 @@ impl Engine<'_> {
} }
} }
if val.is::<String>() {
// val_string[idx] // val_string[idx]
let s = val.downcast_ref::<String>().expect("string expected"); if let Some(s) = val.downcast_ref::<String>() {
let idx = *self let idx = *self
.eval_expr(scope, idx_expr, level)? .eval_expr(scope, idx_expr, level)?
.downcast::<INT>() .downcast::<INT>()
@ -699,10 +690,10 @@ impl Engine<'_> {
} }
// Error - cannot be indexed // Error - cannot be indexed
return Err(EvalAltResult::ErrorIndexingType( Err(EvalAltResult::ErrorIndexingType(
self.map_type_name(val.type_name()).to_string(), self.map_type_name(val.type_name()).to_string(),
op_pos, op_pos,
)); ))
} }
/// Evaluate an index expression /// Evaluate an index expression
@ -755,7 +746,7 @@ impl Engine<'_> {
let val = self.eval_expr(scope, expr, level)?; let val = self.eval_expr(scope, expr, level)?;
self.get_indexed_value(scope, &val, idx_expr, op_pos, level) self.get_indexed_value(scope, &val, idx_expr, op_pos, level)
.map(|(v, _, idx)| (IndexSourceType::Expression, None, idx, v)) .map(|(val, _, idx)| (IndexSourceType::Expression, None, idx, val))
} }
} }
} }
@ -824,23 +815,20 @@ impl Engine<'_> {
new_val: Dynamic, new_val: Dynamic,
pos: Position, pos: Position,
) -> Result<Dynamic, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
if target.is::<Array>() { if let Some(arr) = target.downcast_mut::<Array>() {
let arr = target.downcast_mut::<Array>().expect("array expected");
arr[idx.as_num()] = new_val; arr[idx.as_num()] = new_val;
return Ok(target); return Ok(target);
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
if target.is::<Map>() { if let Some(map) = target.downcast_mut::<Map>() {
let map = target.downcast_mut::<Map>().expect("array expected");
map.insert(idx.as_str(), new_val); map.insert(idx.as_str(), new_val);
return Ok(target); return Ok(target);
} }
} }
if target.is::<String>() { if let Some(s) = target.downcast_mut::<String>() {
let s = target.downcast_mut::<String>().expect("string expected");
// Value must be a character // Value must be a character
let ch = *new_val let ch = *new_val
.downcast::<char>() .downcast::<char>()
@ -877,14 +865,14 @@ impl Engine<'_> {
// 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(&make_getter(id), &mut [this_ptr], None, *pos, 0)
.and_then(|v| { .and_then(|val| {
let (_, _, idx) = let (_, _, idx) =
self.get_indexed_value(scope, &v, idx_expr, *op_pos, level)?; self.get_indexed_value(scope, &val, idx_expr, *op_pos, level)?;
Self::update_indexed_value(v, idx, new_val.0.clone(), new_val.1) Self::update_indexed_value(val, idx, new_val.0.clone(), new_val.1)
}) })
.and_then(|mut v| { .and_then(|mut val| {
let mut args = [this_ptr, v.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(&make_setter(id), &mut args, None, *pos, 0)
}), }),
@ -900,12 +888,12 @@ impl Engine<'_> {
// 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(&make_getter(id), &mut [this_ptr], None, *pos, 0)
.and_then(|mut v| { .and_then(|mut val| {
self.set_dot_val_helper(scope, v.as_mut(), rhs, new_val, level) self.set_dot_val_helper(scope, val.as_mut(), rhs, new_val, level)
.map(|_| v) // Discard Ok return value .map(|_| val) // Discard Ok return value
}) })
.and_then(|mut v| { .and_then(|mut val| {
let mut args = [this_ptr, v.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(&make_setter(id), &mut args, None, *pos, 0)
}) })
} }
@ -986,12 +974,13 @@ 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 this_ptr = target.as_mut();
let val = self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level); let value =
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;
val value
} }
} }
} }
@ -1004,7 +993,7 @@ impl Engine<'_> {
self.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)?; self.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)?;
let val_pos = new_val.1; let val_pos = new_val.1;
let this_ptr = target.as_mut(); let this_ptr = target.as_mut();
let val = self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level); let value = 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 {
@ -1027,7 +1016,7 @@ impl Engine<'_> {
} }
} }
val value
} }
// Syntax error // Syntax error
@ -1474,7 +1463,7 @@ impl Engine<'_> {
pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str { pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
self.type_names self.type_names
.get(name) .get(name)
.map(|s| s.as_str()) .map(String::as_str)
.unwrap_or(name) .unwrap_or(name)
} }

View File

@ -145,7 +145,7 @@ macro_rules! def_register {
fn register_fn(&mut self, name: &str, f: FN) { fn register_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string(); let fn_name = name.to_string();
let fun = move |args: &mut FnCallArgs, pos: Position| { let func = move |args: &mut FnCallArgs, pos: Position| {
// Check for length at the beginning to avoid per-element bound checks. // Check for length at the beginning to avoid per-element bound checks.
const NUM_ARGS: usize = count_args!($($par)*); const NUM_ARGS: usize = count_args!($($par)*);
@ -165,7 +165,7 @@ macro_rules! def_register {
let r = f($(($clone)($par)),*); let r = f($(($clone)($par)),*);
Ok(Box::new(r) as Dynamic) Ok(Box::new(r) as Dynamic)
}; };
self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(fun)); self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func));
} }
} }
@ -177,7 +177,7 @@ macro_rules! def_register {
fn register_dynamic_fn(&mut self, name: &str, f: FN) { fn register_dynamic_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string(); let fn_name = name.to_string();
let fun = move |args: &mut FnCallArgs, pos: Position| { let func = move |args: &mut FnCallArgs, pos: Position| {
// Check for length at the beginning to avoid per-element bound checks. // Check for length at the beginning to avoid per-element bound checks.
const NUM_ARGS: usize = count_args!($($par)*); const NUM_ARGS: usize = count_args!($($par)*);
@ -196,7 +196,7 @@ macro_rules! def_register {
// potentially clone the value, otherwise pass the reference. // potentially clone the value, otherwise pass the reference.
Ok(f($(($clone)($par)),*)) Ok(f($(($clone)($par)),*))
}; };
self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(fun)); self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func));
} }
} }
@ -209,7 +209,7 @@ macro_rules! def_register {
fn register_result_fn(&mut self, name: &str, f: FN) { fn register_result_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string(); let fn_name = name.to_string();
let fun = move |args: &mut FnCallArgs, pos: Position| { let func = move |args: &mut FnCallArgs, pos: Position| {
// Check for length at the beginning to avoid per-element bound checks. // Check for length at the beginning to avoid per-element bound checks.
const NUM_ARGS: usize = count_args!($($par)*); const NUM_ARGS: usize = count_args!($($par)*);
@ -229,7 +229,7 @@ macro_rules! def_register {
f($(($clone)($par)),*).map(|r| Box::new(r) as Dynamic) f($(($clone)($par)),*).map(|r| Box::new(r) as Dynamic)
.map_err(|err| err.set_position(pos)) .map_err(|err| err.set_position(pos))
}; };
self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(fun)); self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func));
} }
} }

View File

@ -37,7 +37,15 @@ struct State<'a> {
engine: &'a Engine<'a>, engine: &'a Engine<'a>,
} }
impl State<'_> { impl<'a> State<'a> {
/// Create a new State.
pub fn new(engine: &'a Engine<'a>) -> Self {
Self {
changed: false,
constants: vec![],
engine,
}
}
/// Reset the state from dirty to clean. /// Reset the state from dirty to clean.
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.changed = false; self.changed = false;
@ -370,24 +378,16 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
}, },
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(items, pos) => { Expr::Array(items, pos) => Expr::Array(items
let items: Vec<_> = items
.into_iter() .into_iter()
.map(|expr| optimize_expr(expr, state)) .map(|expr| optimize_expr(expr, state))
.collect(); .collect(), pos),
Expr::Array(items, pos)
}
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(items, pos) => { Expr::Map(items, pos) => Expr::Map(items
let items: Vec<_> = items
.into_iter() .into_iter()
.map(|(key, expr, pos)| (key, optimize_expr(expr, state), pos)) .map(|(key, expr, pos)| (key, optimize_expr(expr, state), pos))
.collect(); .collect(), pos),
Expr::Map(items, pos)
}
// lhs && rhs // lhs && rhs
Expr::And(lhs, rhs) => match (*lhs, *rhs) { Expr::And(lhs, rhs) => match (*lhs, *rhs) {
// true && rhs -> rhs // true && rhs -> rhs
@ -439,7 +439,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
Expr::FunctionCall(id, args, def_value, pos) if id == KEYWORD_DUMP_AST => Expr::FunctionCall(id, args, def_value, pos) if id == KEYWORD_DUMP_AST =>
Expr::FunctionCall(id, args, def_value, pos), Expr::FunctionCall(id, args, def_value, pos),
// Do not optimize anything within built-in function keywords // Do not call some special keywords
Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_str())=> Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_str())=>
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos), Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
@ -465,8 +465,8 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
"" ""
}; };
state.engine.call_ext_fn_raw(&id, &mut call_args, pos).ok().map(|r| state.engine.call_ext_fn_raw(&id, &mut call_args, pos).ok().map(|result|
r.or_else(|| { result.or_else(|| {
if !arg_for_type_of.is_empty() { if !arg_for_type_of.is_empty() {
// Handle `type_of()` // Handle `type_of()`
Some(arg_for_type_of.to_string().into_dynamic()) Some(arg_for_type_of.to_string().into_dynamic())
@ -506,11 +506,7 @@ pub(crate) fn optimize<'a>(statements: Vec<Stmt>, engine: &Engine<'a>, scope: &S
} }
// Set up the state // Set up the state
let mut state = State { let mut state = State::new(engine);
changed: false,
constants: vec![],
engine,
};
// Add constants from the scope into the state // Add constants from the scope into the state
scope scope