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

212
README.md
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 = 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:
@ -301,7 +301,7 @@ type_of('c') == "char";
type_of(42) == "i64";
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";
x = 99.999;
@ -325,7 +325,7 @@ That's about it. For other conversions, register custom conversion functions.
```rust
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 z = y.to_int() + x; // works
@ -341,8 +341,8 @@ To call these functions, they need to be registered with the [`Engine`].
```rust
use rhai::{Engine, EvalAltResult};
use rhai::RegisterFn; // use `RegisterFn` trait for `register_fn`
use rhai::{Dynamic, RegisterDynamicFn}; // use `RegisterDynamicFn` trait for `register_dynamic_fn`
use rhai::RegisterFn; // use `RegisterFn` trait for `register_fn`
use rhai::{Dynamic, RegisterDynamicFn}; // use `RegisterDynamicFn` trait for `register_dynamic_fn`
// Normal function
fn add(x: i64, y: i64) -> i64 {
@ -362,14 +362,14 @@ fn main() -> Result<(), EvalAltResult>
let result = engine.eval::<i64>("add(40, 2)")?;
println!("Answer: {}", result); // prints 42
println!("Answer: {}", result); // prints 42
// Functions that return Dynamic values must use register_dynamic_fn()
engine.register_dynamic_fn("get_an_any", get_an_any);
let result = engine.eval::<i64>("get_an_any()")?;
println!("Answer: {}", result); // prints 42
println!("Answer: {}", result); // prints 42
Ok(())
}
@ -423,13 +423,13 @@ The function must return `Result<_, EvalAltResult>`. `EvalAltResult` implements
```rust
use rhai::{Engine, EvalAltResult, Position};
use rhai::RegisterResultFn; // use `RegisterResultFn` trait for `register_result_fn`
use rhai::RegisterResultFn; // use `RegisterResultFn` trait for `register_result_fn`
// Function that may fail
fn safe_divide(x: i64, y: i64) -> Result<i64, EvalAltResult> {
if y == 0 {
// Return an error if y is zero
Err("Division by zero detected!".into()) // short-cut to create EvalAltResult
Err("Division by zero detected!".into()) // short-cut to create EvalAltResult
} else {
Ok(x / y)
}
@ -443,7 +443,7 @@ fn main()
engine.register_result_fn("divide", safe_divide);
if let Err(error) = engine.eval::<i64>("divide(40, 0)") {
println!("Error: {:?}", error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
println!("Error: {:?}", error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
}
}
```
@ -745,24 +745,24 @@ Variable names are also case _sensitive_.
Variables are defined using the `let` keyword. A variable defined within a statement block is _local_ to that block.
```rust
let x = 3; // ok
let _x = 42; // ok
let x_ = 42; // also ok
let _x_ = 42; // still ok
let x = 3; // ok
let _x = 42; // ok
let x_ = 42; // also ok
let _x_ = 42; // still ok
let _ = 123; // syntax error - illegal variable name
let _9 = 9; // syntax error - illegal variable name
let _ = 123; // <- syntax error: illegal variable name
let _9 = 9; // <- syntax error: illegal variable name
let x = 42; // variable is 'x', lower case
let X = 123; // variable is 'X', upper case
let x = 42; // variable is 'x', lower case
let X = 123; // variable is 'X', upper case
x == 42;
X == 123;
{
let x = 999; // local variable 'x' shadows the 'x' in parent block
x == 999; // access to local 'x'
let x = 999; // local variable 'x' shadows the 'x' in parent block
x == 999; // access to local 'x'
}
x == 42; // the parent block's 'x' is not changed
x == 42; // the parent block's 'x' is not changed
```
Constants
@ -772,14 +772,14 @@ Constants can be defined using the `const` keyword and are immutable. Constants
```rust
const x = 42;
print(x * 2); // prints 84
x = 123; // syntax error - cannot assign to constant
print(x * 2); // prints 84
x = 123; // <- syntax error: cannot assign to constant
```
Constants must be assigned a _value_, not an expression.
```rust
const x = 40 + 2; // syntax error - cannot assign expression to constant
const x = 40 + 2; // <- syntax error: cannot assign expression to constant
```
Numbers
@ -1135,21 +1135,21 @@ However, if the [`no_stdlib`] feature is turned on, comparisons can only be made
types - `INT` (`i64` or `i32` depending on [`only_i32`] and [`only_i64`]), `f64` (if not [`no_float`]), string, array, `bool`, `char`.
```rust
42 == 42; // true
42 > 42; // false
"hello" > "foo"; // true
"42" == 42; // false
42 == 42; // true
42 > 42; // false
"hello" > "foo"; // true
"42" == 42; // false
```
Comparing two values of _different_ data types, or of unknown data types, always results in `false`.
```rust
42 == 42.0; // false - i64 is different from f64
42 > "42"; // false - i64 is different from string
42 <= "42"; // false again
42 == 42.0; // false - i64 is different from f64
42 > "42"; // false - i64 is different from string
42 <= "42"; // false again
let ts = new_ts(); // custom type
ts == 42; // false - types are not the same
let ts = new_ts(); // custom type
ts == 42; // false - types are not the same
```
Boolean operators
@ -1169,11 +1169,11 @@ if the first one already proves the condition wrong.
Single boolean operators `&` and `|` always evaluate both operands.
```rust
this() || that(); // that() is not evaluated if this() is true
this() && that(); // that() is not evaluated if this() is false
this() || that(); // that() is not evaluated if this() is true
this() && that(); // that() is not evaluated if this() is false
this() | that(); // both this() and that() are evaluated
this() & that(); // both this() and that() are evaluated
this() | that(); // both this() and that() are evaluated
this() & that(); // both this() and that() are evaluated
```
Compound assignment operators
@ -1181,13 +1181,13 @@ Compound assignment operators
```rust
let number = 5;
number += 4; // number = number + 4
number -= 3; // number = number - 3
number *= 2; // number = number * 2
number /= 1; // number = number / 1
number %= 3; // number = number % 3
number <<= 2; // number = number << 2
number >>= 1; // number = number >> 1
number += 4; // number = number + 4
number -= 3; // number = number - 3
number *= 2; // number = number * 2
number /= 1; // number = number / 1
number %= 3; // number = number % 3
number <<= 2; // number = number << 2
number >>= 1; // number = number >> 1
```
The `+=` operator can also be used to build strings:
@ -1286,9 +1286,9 @@ for x in range(0, 50) {
-------------------
```rust
return; // equivalent to return ();
return; // equivalent to return ();
return 123 + 456; // returns 579
return 123 + 456; // returns 579
```
Errors and `throw`-ing exceptions
@ -1299,10 +1299,10 @@ To deliberately return an error during an evaluation, use the `throw` keyword.
```rust
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_`))`
@ -1317,7 +1317,7 @@ let result = engine.eval::<i64>(r#"
}
"#);
println!(result); // prints "Runtime error: 42 is too large! (line 5, position 15)"
println!(result); // prints "Runtime error: 42 is too large! (line 5, position 15)"
```
Functions
@ -1339,16 +1339,17 @@ 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.
```rust
fn add(x, y) {
x + y; // value of the last statement (no need for ending semicolon) is used as the return value
fn add(x, y) { // implicit return:
x + y; // value of the last statement (no need for ending semicolon)
// is used as the return value
}
fn add2(x) {
return x + 2; // explicit return
return x + 2; // explicit return
}
print(add(2, 3)); // prints 5
print(add2(42)); // prints 44
print(add(2, 3)); // prints 5
print(add2(42)); // prints 44
```
### No access to external scope
@ -1358,7 +1359,7 @@ Functions can only access their parameters. They cannot access external variabl
```rust
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
@ -1368,13 +1369,13 @@ It is important to remember that all arguments are passed by _value_, so all fun
Any update to an argument will **not** be reflected back to the caller. This can introduce subtle bugs, if not careful.
```rust
fn change(s) { // 's' is passed by value
s = 42; // only a COPY of 's' is changed
fn change(s) { // 's' is passed by value
s = 42; // only a COPY of 's' is changed
}
let x = 500;
x.change(); // de-sugars to change(x)
x == 500; // 'x' is NOT changed!
x.change(); // de-sugars to change(x)
x == 500; // 'x' is NOT changed!
```
### Global definitions only
@ -1389,7 +1390,7 @@ fn add(x, y) {
// The following will not compile
fn do_addition(x) {
fn add_y(n) { // functions cannot be defined inside another function
fn add_y(n) { // functions cannot be defined inside another function
n + y
}
@ -1410,10 +1411,10 @@ fn foo(x,y) { print("Two! " + x + "," + y) }
fn foo() { print("None.") }
fn foo(x) { print("HA! NEW ONE! " + x) } // overwrites previous definition
foo(1,2,3); // prints "Three!!! 1,2,3"
foo(42); // prints "HA! NEW ONE! 42"
foo(1,2); // prints "Two!! 1,2"
foo(); // prints "None."
foo(1,2,3); // prints "Three!!! 1,2,3"
foo(42); // prints "HA! NEW ONE! 42"
foo(1,2); // prints "Two!! 1,2"
foo(); // prints "None."
```
Members and methods
@ -1422,11 +1423,12 @@ Members and methods
Properties and methods in a Rust custom type registered with the [`Engine`] can be called just like in Rust.
```rust
let a = new_ts(); // constructor function
a.field = 500; // property access
a.update(); // method call
let a = new_ts(); // constructor function
a.field = 500; // property access
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.
@ -1437,10 +1439,10 @@ Custom types, properties and methods can be disabled via the [`no_object`] featu
The `print` and `debug` functions default to printing to `stdout`, with `debug` using standard debug formatting.
```rust
print("hello"); // prints hello to stdout
print(1 + 2 + 3); // prints 6 to stdout
print("hello" + 42); // prints hello42 to stdout
debug("world!"); // prints "world!" to stdout using debug formatting
print("hello"); // prints hello to stdout
print(1 + 2 + 3); // prints 6 to stdout
print("hello" + 42); // prints hello42 to stdout
debug("world!"); // prints "world!" to stdout using debug formatting
```
### Overriding `print` and `debug` with callback functions
@ -1481,14 +1483,13 @@ For example, in the following:
```rust
{
let x = 999; // NOT eliminated - Rhai doesn't check yet whether a variable is used later on
123; // eliminated - no effect
"hello"; // eliminated - no effect
[1, 2, x, x*2, 5]; // eliminated - no effect
foo(42); // NOT eliminated - the function 'foo' may have side effects
666 // NOT eliminated - this is the return value of the block,
// and the block is the last one
// so this is the return value of the whole script
let x = 999; // NOT eliminated: Rhai doesn't check yet whether a variable is used later on
123; // eliminated: no effect
"hello"; // eliminated: no effect
[1, 2, x, x*2, 5]; // eliminated: no effect
foo(42); // NOT eliminated: the function 'foo' may have side effects
666 // NOT eliminated: this is the return value of the block,
// and the block is the last one so this is the return value of the whole script
}
```
@ -1511,7 +1512,7 @@ if ABC || some_work() { print("done!"); } // 'ABC' is constant so it is replac
if true || some_work() { print("done!"); } // since '||' short-circuits, 'some_work' is never called
if true { print("done!"); } // <- the line above is equivalent to this
print("done!"); // <- the line above is further simplified to this
// because the condition is always true
// because the condition is always true
```
These are quite effective for template-based machine-generated scripts where certain constant values
@ -1526,7 +1527,7 @@ so they are not optimized away:
const DECISION = 1;
if DECISION == 1 { // NOT optimized away because you can define
: // your own '==' function to override the built-in default!
: // your own '==' function to override the built-in default!
:
} else if DECISION == 2 { // same here, NOT optimized away
:
@ -1596,14 +1597,15 @@ evaluated all function calls with constant arguments, using the result to replac
// When compiling the following with OptimizationLevel::Full...
const DECISION = 1;
// this condition is now eliminated because 'DECISION == 1'
if DECISION == 1 { // is a function call to the '==' function, and it returns 'true'
print("hello!"); // this block is promoted to the parent level
// this condition is now eliminated because 'DECISION == 1'
if DECISION == 1 { // is a function call to the '==' function, and it returns 'true'
print("hello!"); // this block is promoted to the parent level
} else {
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.
@ -1612,8 +1614,8 @@ This does not happen with [`OptimizationLevel::Simple`] which doesn't assume all
```rust
// When compiling the following with OptimizationLevel::Full...
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 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'
```
Function side effect considerations
@ -1644,13 +1646,13 @@ Subtle semantic changes
Some optimizations can alter subtle semantics of the script. For example:
```rust
if true { // condition always true
123.456; // eliminated
hello; // eliminated, EVEN THOUGH the variable doesn't exist!
foo(42) // promoted up-level
if true { // condition always true
123.456; // eliminated
hello; // eliminated, EVEN THOUGH the variable doesn't exist!
foo(42) // promoted up-level
}
foo(42) // <- the above optimizes to this
foo(42) // <- the above optimizes to this
```
Nevertheless, if the original script were evaluated instead, it would have been an error - the variable `hello` doesn't exist,
@ -1696,22 +1698,22 @@ let x = 10;
fn foo(x) { x += 12; x }
let script = "let y = x;"; // build a script
let script = "let y = x;"; // build a script
script += "y += foo(y);";
script += "x + y";
let result = eval(script); // <- look, JS, we can also do this!
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("y = " + y); // prints 32 - variables defined in 'eval' persist!
print("x = " + x); // prints 10: functions call arguments are passed by value
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_,
@ -1721,8 +1723,8 @@ physically pasted in at the position of the `eval` call.
```rust
let script = "x += 32";
let x = 10;
eval(script); // variable 'x' in the current scope is visible!
print(x); // prints 42
eval(script); // variable 'x' in the current scope is visible!
print(x); // prints 42
// The above is equivalent to:
let script = "x += 32";
@ -1737,7 +1739,7 @@ disable `eval` by overriding it, probably with something that throws.
```rust
fn eval(script) { throw "eval is evil! I refuse to run " + script }
let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2"
let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2"
```
Or override it from Rust:

View File

@ -691,14 +691,14 @@ impl<'e> Engine<'e> {
scope: &mut Scope,
ast: &AST,
) -> Result<T, EvalAltResult> {
self.eval_ast_with_scope_raw(scope, false, ast)
.and_then(|result| {
result.downcast::<T>().map(|v| *v).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(),
Position::none(),
)
})
self.eval_ast_with_scope_raw(scope, false, ast)?
.downcast::<T>()
.map(|v| *v)
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(),
Position::none(),
)
})
}
@ -735,7 +735,9 @@ impl<'e> Engine<'e> {
/// 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.
///
/// 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"))]
pub fn consume_file(
&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).
/// 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"))]
pub fn consume_file_with_scope(
&mut self,
@ -763,7 +767,9 @@ impl<'e> Engine<'e> {
/// 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.
///
/// 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> {
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).
/// 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(
&mut self,
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).
/// 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> {
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).
/// 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(
&mut self,
scope: &mut Scope,
@ -833,9 +845,9 @@ impl<'e> Engine<'e> {
&mut self,
functions: impl IntoIterator<Item = &'a Arc<FnDef>>,
) {
for f in functions.into_iter() {
self.fn_lib.add_or_replace_function(f.clone());
}
functions.into_iter().cloned().for_each(|f| {
self.fn_lib.add_or_replace_function(f);
});
}
/// Call a script function retained inside the Engine.
@ -871,14 +883,14 @@ impl<'e> Engine<'e> {
let mut values = args.into_vec();
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)
.and_then(|b| {
b.downcast().map(|b| *b).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),
Position::none(),
)
})
self.call_fn_raw(name, &mut arg_values, None, Position::none(), 0)?
.downcast()
.map(|b| *b)
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),
Position::none(),
)
})
}

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

View File

@ -145,7 +145,7 @@ macro_rules! def_register {
fn register_fn(&mut self, name: &str, f: FN) {
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.
const NUM_ARGS: usize = count_args!($($par)*);
@ -165,7 +165,7 @@ macro_rules! def_register {
let r = f($(($clone)($par)),*);
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) {
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.
const NUM_ARGS: usize = count_args!($($par)*);
@ -196,7 +196,7 @@ macro_rules! def_register {
// potentially clone the value, otherwise pass the reference.
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) {
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.
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)
.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>,
}
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.
pub fn reset(&mut self) {
self.changed = false;
@ -370,24 +378,16 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
},
// [ items .. ]
#[cfg(not(feature = "no_index"))]
Expr::Array(items, pos) => {
let items: Vec<_> = items
Expr::Array(items, pos) => Expr::Array(items
.into_iter()
.map(|expr| optimize_expr(expr, state))
.collect();
Expr::Array(items, pos)
}
.collect(), pos),
// [ items .. ]
#[cfg(not(feature = "no_object"))]
Expr::Map(items, pos) => {
let items: Vec<_> = items
Expr::Map(items, pos) => Expr::Map(items
.into_iter()
.map(|(key, expr, pos)| (key, optimize_expr(expr, state), pos))
.collect();
Expr::Map(items, pos)
}
.collect(), pos),
// lhs && rhs
Expr::And(lhs, rhs) => match (*lhs, *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),
// 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.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|
r.or_else(|| {
state.engine.call_ext_fn_raw(&id, &mut call_args, pos).ok().map(|result|
result.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
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
let mut state = State {
changed: false,
constants: vec![],
engine,
};
let mut state = State::new(engine);
// Add constants from the scope into the state
scope