Minor code cleanup.
This commit is contained in:
parent
dcf5eaf64d
commit
d7ac57c060
58
README.md
58
README.md
@ -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_,
|
||||||
|
46
src/api.rs
46
src/api.rs
@ -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.
|
||||||
|
117
src/engine.rs
117
src/engine.rs
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user