From d7ac57c0604e7541861521651de96d23c61f5408 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 1 Apr 2020 09:51:33 +0800 Subject: [PATCH] Minor code cleanup. --- README.md | 212 +++++++++++++++++++++++---------------------- src/api.rs | 62 +++++++------ src/engine.rs | 123 ++++++++++++-------------- src/fn_register.rs | 12 +-- src/optimize.rs | 38 ++++---- 5 files changed, 223 insertions(+), 224 deletions(-) diff --git a/README.md b/README.md index 719aa523..969e727a 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ let result = engine.eval::("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("40 + 2")?; // returns an error because the actual return type is i64, not String +let result = engine.eval::("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::("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::("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 { 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::("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::(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: diff --git a/src/api.rs b/src/api.rs index 77a870c7..7e010192 100644 --- a/src/api.rs +++ b/src/api.rs @@ -691,14 +691,14 @@ impl<'e> Engine<'e> { scope: &mut Scope, ast: &AST, ) -> Result { - self.eval_ast_with_scope_raw(scope, false, ast) - .and_then(|result| { - result.downcast::().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::() + .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>, ) { - 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(), + ) }) } diff --git a/src/engine.rs b/src/engine.rs index 67240897..b20cfabd 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -36,15 +36,15 @@ pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result Box>; -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.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::() { - // val_array[idx] - let arr = val.downcast_ref::().expect("array expected"); - + // val_array[idx] + if let Some(arr) = val.downcast_ref::() { let idx = *self .eval_expr(scope, idx_expr, level)? .downcast::() @@ -650,10 +645,8 @@ impl Engine<'_> { #[cfg(not(feature = "no_object"))] { - if val.is::() { - // val_map[idx] - let map = val.downcast_ref::().expect("array expected"); - + // val_map[idx] + if let Some(map) = val.downcast_ref::() { let idx = *self .eval_expr(scope, idx_expr, level)? .downcast::() @@ -667,10 +660,8 @@ impl Engine<'_> { } } - if val.is::() { - // val_string[idx] - let s = val.downcast_ref::().expect("string expected"); - + // val_string[idx] + if let Some(s) = val.downcast_ref::() { let idx = *self .eval_expr(scope, idx_expr, level)? .downcast::() @@ -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 { - if target.is::() { - let arr = target.downcast_mut::().expect("array expected"); + if let Some(arr) = target.downcast_mut::() { arr[idx.as_num()] = new_val; return Ok(target); } #[cfg(not(feature = "no_object"))] { - if target.is::() { - let map = target.downcast_mut::().expect("array expected"); + if let Some(map) = target.downcast_mut::() { map.insert(idx.as_str(), new_val); return Ok(target); } } - if target.is::() { - let s = target.downcast_mut::().expect("string expected"); + if let Some(s) = target.downcast_mut::() { // Value must be a character let ch = *new_val .downcast::() @@ -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) } diff --git a/src/fn_register.rs b/src/fn_register.rs index 5e7efd13..cfc1b345 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -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)); } } diff --git a/src/optimize.rs b/src/optimize.rs index 826df90f..0651536c 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -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, 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