diff --git a/README.md b/README.md index e31fc25f..9393fd1b 100644 --- a/README.md +++ b/README.md @@ -314,9 +314,9 @@ Use `Engine::new_raw` to create a _raw_ `Engine`, in which: * the _standard library_ of utility functions is _not_ loaded by default (load it using the `register_stdlib` method). ```rust -let mut engine = Engine::new_raw(); // Create a 'raw' Engine +let mut engine = Engine::new_raw(); // create a 'raw' Engine -engine.register_stdlib(); // Register the standard library manually +engine.register_stdlib(); // register the standard library manually engine. ``` @@ -439,7 +439,7 @@ The `cast` method (from the `rhai::AnyExt` trait) then converts the value into a Alternatively, use the `try_cast` method which does not panic but returns an error when the cast fails. ```rust -use rhai::AnyExt; // Pull in the trait. +use rhai::AnyExt; // pull in the trait. let list: Array = engine.eval("...")?; // return type is 'Array' let item = list[0]; // an element in an 'Array' is 'Dynamic' @@ -455,14 +455,14 @@ let value = item.try_cast::()?; // 'try_cast' does not panic whe The `type_name` method gets the name of the actual type as a static string slice, which you may match against. ```rust -use rhai::Any; // Pull in the trait. +use rhai::Any; // pull in the trait. let list: Array = engine.eval("...")?; // return type is 'Array' let item = list[0]; // an element in an 'Array' is 'Dynamic' match item.type_name() { // 'type_name' returns the name of the actual Rust type "i64" => ... - "std::string::String" => ... + "alloc::string::String" => ... "bool" => ... "path::to::module::TestStruct" => ... } @@ -548,7 +548,7 @@ To return a [`Dynamic`] value from a Rust function, use the `into_dynamic()` met (under the `rhai::Any` trait) to convert it. ```rust -use rhai::Any; // Pull in the trait +use rhai::Any; // pull in the trait fn decide(yes_no: bool) -> Dynamic { if yes_no { @@ -598,13 +598,13 @@ and the error text gets converted into `EvalAltResult::ErrorRuntime`. ```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!".into()) // short-cut to create EvalAltResult } else { Ok(x / y) } @@ -618,7 +618,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)") } } ``` @@ -672,7 +672,7 @@ fn main() -> Result<(), EvalAltResult> let result = engine.eval::("let x = new_ts(); x.update(); x")?; - println!("result: {}", result.field); // prints 42 + println!("result: {}", result.field); // prints 42 Ok(()) } @@ -1329,7 +1329,7 @@ foo == 42; y.has("a") == true; y.has("xyz") == false; -y.xyz == (); // A non-existing property returns '()' +y.xyz == (); // a non-existing property returns '()' y["xyz"] == (); print(y.len()); // prints 3 @@ -1449,10 +1449,11 @@ if (decision) print("I've decided!"); Like Rust, `if` statements can also be used as _expressions_, replacing the `? :` conditional operators in other C-like languages. ```rust -let x = 1 + if true { 42 } else { 123 } / 2; +// The following is equivalent to C: int x = 1 + (decision ? 42 : 123) / 2; +let x = 1 + if decision { 42 } else { 123 } / 2; x == 22; -let x = if false { 42 }; // No else branch defaults to '()' +let x = if decision { 42 }; // no else branch defaults to '()' x == (); ``` @@ -1506,7 +1507,6 @@ for x in range(0, 50) { if x == 42 { break; } // break out of for loop } - // The 'range' function also takes a step for x in range(0, 50, 3) { // step by 3 if x > 10 { continue; } // skip to the next iteration @@ -1514,15 +1514,20 @@ for x in range(0, 50, 3) { // step by 3 if x == 42 { break; } // break out of for loop } -// Iterate through the values of an object map +// Iterate through object map let map = #{a:1, b:3, c:5, d:7, e:9}; -// Remember that keys are returned in random order +// Property names are returned in random order for x in keys(map) { if x > 10 { continue; } // skip to the next iteration print(x); if x == 42 { break; } // break out of for loop } + +// Property values are returned in random order +for val in values(map) { + print(val); +} ``` `return`-ing values diff --git a/src/api.rs b/src/api.rs index e0e53ba4..a2c7755a 100644 --- a/src/api.rs +++ b/src/api.rs @@ -5,13 +5,11 @@ use crate::engine::{make_getter, make_setter, Engine, FnAny, FnSpec}; use crate::error::ParseError; use crate::fn_call::FuncArgs; use crate::fn_register::RegisterFn; +use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::parser::{lex, parse, parse_global_expr, Position, AST}; use crate::result::EvalAltResult; use crate::scope::Scope; -#[cfg(not(feature = "no_optimize"))] -use crate::optimize::{optimize_into_ast, OptimizationLevel}; - use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, @@ -387,12 +385,7 @@ impl<'e> Engine<'e> { /// # } /// ``` pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result { - self.compile_with_scope_and_optimization_level( - scope, - script, - #[cfg(not(feature = "no_optimize"))] - self.optimization_level, - ) + self.compile_with_scope_and_optimization_level(scope, script, self.optimization_level) } /// Compile a string into an `AST` using own scope at a specific optimization level. @@ -400,7 +393,7 @@ impl<'e> Engine<'e> { &self, scope: &Scope, script: &str, - #[cfg(not(feature = "no_optimize"))] optimization_level: OptimizationLevel, + optimization_level: OptimizationLevel, ) -> Result { let tokens_stream = lex(script); @@ -408,7 +401,6 @@ impl<'e> Engine<'e> { &mut tokens_stream.peekable(), self, scope, - #[cfg(not(feature = "no_optimize"))] optimization_level, ) } @@ -821,7 +813,6 @@ impl<'e> Engine<'e> { &mut tokens_stream.peekable(), self, scope, - #[cfg(not(feature = "no_optimize"))] self.optimization_level, )?; diff --git a/src/engine.rs b/src/engine.rs index 559a6ef0..c308b236 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,13 +2,11 @@ use crate::any::{Any, AnyExt, Dynamic, Variant}; use crate::error::ParseErrorType; +use crate::optimize::OptimizationLevel; use crate::parser::{Expr, FnDef, Position, ReturnType, Stmt, INT}; use crate::result::EvalAltResult; use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope}; -#[cfg(not(feature = "no_optimize"))] -use crate::optimize::OptimizationLevel; - use crate::stdlib::{ any::{type_name, TypeId}, borrow::Cow, @@ -28,13 +26,11 @@ use crate::stdlib::{ /// An dynamic array of `Dynamic` values. /// /// Not available under the `no_index` feature. -#[cfg(not(feature = "no_index"))] pub type Array = Vec; /// An dynamic hash map of `Dynamic` values with `String` keys. /// /// Not available under the `no_object` feature. -#[cfg(not(feature = "no_object"))] pub type Map = HashMap; pub type FnCallArgs<'a> = [&'a mut Variant]; @@ -57,7 +53,6 @@ pub const MAX_CALL_STACK_DEPTH: usize = 256; 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"; @@ -65,12 +60,10 @@ pub const FUNC_GETTER: &str = "get$"; pub const FUNC_SETTER: &str = "set$"; #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] -#[cfg(not(feature = "no_index"))] enum IndexSourceType { Expression, String, Array, - #[cfg(not(feature = "no_object"))] Map, } @@ -272,7 +265,6 @@ pub struct Engine<'e> { pub(crate) on_debug: Option>, /// Optimize the AST after compilation. - #[cfg(not(feature = "no_optimize"))] pub(crate) optimization_level: OptimizationLevel, /// Maximum levels of call-stack to prevent infinite recursion. @@ -290,18 +282,13 @@ impl Default for Engine<'_> { type_names: None, // default print/debug implementations - #[cfg(not(feature = "no_std"))] on_print: Some(Box::new(default_print)), - #[cfg(not(feature = "no_std"))] on_debug: Some(Box::new(default_print)), - // default print/debug implementations - #[cfg(feature = "no_std")] - on_print: None, - #[cfg(feature = "no_std")] - on_debug: None, - // optimization level + #[cfg(feature = "no_optimize")] + optimization_level: OptimizationLevel::None, + #[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "optimize_full"))] optimization_level: OptimizationLevel::Simple, @@ -330,9 +317,16 @@ pub fn make_getter(id: &str) -> String { /// Extract the property name from a getter function name. fn extract_prop_from_getter(fn_name: &str) -> Option<&str> { - if fn_name.starts_with(FUNC_GETTER) { - Some(&fn_name[FUNC_GETTER.len()..]) - } else { + #[cfg(not(feature = "no_object"))] + { + if fn_name.starts_with(FUNC_GETTER) { + Some(&fn_name[FUNC_GETTER.len()..]) + } else { + None + } + } + #[cfg(feature = "no_object")] + { None } } @@ -344,9 +338,16 @@ pub fn make_setter(id: &str) -> String { /// Extract the property name from a setter function name. fn extract_prop_from_setter(fn_name: &str) -> Option<&str> { - if fn_name.starts_with(FUNC_SETTER) { - Some(&fn_name[FUNC_SETTER.len()..]) - } else { + #[cfg(not(feature = "no_object"))] + { + if fn_name.starts_with(FUNC_SETTER) { + Some(&fn_name[FUNC_SETTER.len()..]) + } else { + None + } + } + #[cfg(feature = "no_object")] + { None } } @@ -384,6 +385,9 @@ impl Engine<'_> { on_print: None, on_debug: None, + #[cfg(feature = "no_optimize")] + optimization_level: OptimizationLevel::None, + #[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "optimize_full"))] optimization_level: OptimizationLevel::Simple, @@ -431,6 +435,9 @@ impl Engine<'_> { return Err(EvalAltResult::ErrorStackOverflow(pos)); } + #[cfg(feature = "no_function")] + const fn_lib: Option<&FunctionsLib> = None; + // First search in script-defined functions (can override built-in) if let Some(lib) = fn_lib { if let Some(fn_def) = lib.get_function(fn_name, args.len()) { @@ -523,12 +530,9 @@ impl Engine<'_> { } if let Some(prop) = extract_prop_from_getter(fn_name) { - #[cfg(not(feature = "no_object"))] - { - // Map property access - if let Some(map) = args[0].downcast_ref::() { - return Ok(map.get(prop).cloned().unwrap_or_else(|| ().into_dynamic())); - } + // Map property access + if let Some(map) = args[0].downcast_ref::() { + return Ok(map.get(prop).cloned().unwrap_or_else(|| ().into_dynamic())); } // Getter function not found @@ -539,15 +543,12 @@ impl Engine<'_> { } if let Some(prop) = extract_prop_from_setter(fn_name) { - #[cfg(not(feature = "no_object"))] - { - let value = 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(), value); - return Ok(().into_dynamic()); - } + // Map property update + if let Some(map) = args[0].downcast_mut::() { + map.insert(prop.to_string(), value); + return Ok(().into_dynamic()); } // Setter function not found @@ -576,7 +577,6 @@ impl Engine<'_> { } /// Chain-evaluate a dot setter. - #[cfg(not(feature = "no_object"))] fn get_dot_val_helper( &self, scope: &mut Scope, @@ -611,7 +611,6 @@ impl Engine<'_> { } // xxx.idx_lhs[idx_expr] - #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, idx_expr, op_pos) => { let value = match idx_lhs.as_ref() { // xxx.id[idx_expr] @@ -648,7 +647,6 @@ impl Engine<'_> { }) } // xxx.idx_lhs[idx_expr].rhs - #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, idx_expr, op_pos) => { let val = match idx_lhs.as_ref() { // xxx.id[idx_expr].rhs @@ -692,7 +690,6 @@ impl Engine<'_> { } /// Evaluate a dot chain getter - #[cfg(not(feature = "no_object"))] fn get_dot_val( &self, scope: &mut Scope, @@ -715,9 +712,8 @@ 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 val) = + let (idx_src_type, src, index, mut val) = self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?; let target = Target::from(val.as_mut()); let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level); @@ -736,7 +732,7 @@ impl Engine<'_> { idx_src_type, scope, src, - idx, + index, (val, dot_rhs.position()), )?; } @@ -766,7 +762,6 @@ impl Engine<'_> { } /// Get the value at the indexed position of a base type - #[cfg(not(feature = "no_index"))] fn get_indexed_value( &self, scope: &mut Scope, @@ -780,62 +775,61 @@ impl Engine<'_> { // val_array[idx] if let Some(arr) = val.downcast_ref::() { - let idx = self + let index = self .eval_expr(scope, fn_lib, idx_expr, level)? .try_cast::() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; - return if idx >= 0 { - arr.get(idx as usize) + return if index >= 0 { + arr.get(index as usize) .cloned() - .map(|v| (v, IndexSourceType::Array, IndexValue::from_num(idx))) - .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos)) + .map(|v| (v, IndexSourceType::Array, IndexValue::from_num(index))) + .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos)) } else { - Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos)) + Err(EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos)) }; } - #[cfg(not(feature = "no_object"))] - { - // val_map[idx] - if let Some(map) = val.downcast_ref::() { - let idx = self - .eval_expr(scope, fn_lib, idx_expr, level)? - .try_cast::() - .map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?; + // val_map[idx] + if let Some(map) = val.downcast_ref::() { + let index = self + .eval_expr(scope, fn_lib, idx_expr, level)? + .try_cast::() + .map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?; - return Ok(( - map.get(&idx).cloned().unwrap_or_else(|| ().into_dynamic()), - IndexSourceType::Map, - IndexValue::from_str(idx), - )); - } + return Ok(( + map.get(&index) + .cloned() + .unwrap_or_else(|| ().into_dynamic()), + IndexSourceType::Map, + IndexValue::from_str(index), + )); } // val_string[idx] if let Some(s) = val.downcast_ref::() { - let idx = self + let index = self .eval_expr(scope, fn_lib, idx_expr, level)? .try_cast::() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; - return if idx >= 0 { + return if index >= 0 { s.chars() - .nth(idx as usize) + .nth(index as usize) .map(|ch| { ( ch.into_dynamic(), IndexSourceType::String, - IndexValue::from_num(idx), + IndexValue::from_num(index), ) }) .ok_or_else(|| { - EvalAltResult::ErrorStringBounds(s.chars().count(), idx, idx_pos) + EvalAltResult::ErrorStringBounds(s.chars().count(), index, idx_pos) }) } else { Err(EvalAltResult::ErrorStringBounds( s.chars().count(), - idx, + index, idx_pos, )) }; @@ -849,7 +843,6 @@ impl Engine<'_> { } /// Evaluate an index expression - #[cfg(not(feature = "no_index"))] fn eval_index_expr<'a>( &self, scope: &mut Scope, @@ -879,7 +872,7 @@ impl Engine<'_> { val, ) = Self::search_scope(scope, &id, lhs.position())?; - let (val, idx_src_type, idx) = + let (val, idx_src_type, index) = self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)?; Ok(( @@ -889,7 +882,7 @@ impl Engine<'_> { typ: src_type, index: src_idx, }), - idx, + index, val, )) } @@ -899,13 +892,12 @@ impl Engine<'_> { let val = self.eval_expr(scope, fn_lib, expr, level)?; self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level) - .map(|(val, _, idx)| (IndexSourceType::Expression, None, idx, val)) + .map(|(val, _, index)| (IndexSourceType::Expression, None, index, val)) } } } /// Replace a character at an index position in a mutable string - #[cfg(not(feature = "no_index"))] fn str_replace_char(s: &mut String, idx: usize, new_ch: char) { let mut chars: Vec = s.chars().collect(); let ch = *chars.get(idx).expect("string index out of bounds"); @@ -919,7 +911,6 @@ impl Engine<'_> { } /// Update the value at an index position in a variable inside the scope - #[cfg(not(feature = "no_index"))] fn update_indexed_var_in_scope( idx_src_type: IndexSourceType, scope: &mut Scope, @@ -936,7 +927,6 @@ impl Engine<'_> { } // map_id[idx] = val - #[cfg(not(feature = "no_object"))] IndexSourceType::Map => { let arr = scope.get_mut_by_type::(src); arr.insert(idx.as_str(), new_val.0); @@ -961,7 +951,6 @@ impl Engine<'_> { } /// Update the value at an index position - #[cfg(not(feature = "no_index"))] fn update_indexed_value( mut target: Dynamic, idx: IndexValue, @@ -973,12 +962,9 @@ impl Engine<'_> { return Ok(target); } - #[cfg(not(feature = "no_object"))] - { - if let Some(map) = target.downcast_mut::() { - map.insert(idx.as_str(), new_val); - return Ok(target); - } + if let Some(map) = target.downcast_mut::() { + map.insert(idx.as_str(), new_val); + return Ok(target); } if let Some(s) = target.downcast_mut::() { @@ -995,7 +981,6 @@ impl Engine<'_> { } /// Chain-evaluate a dot setter - #[cfg(not(feature = "no_object"))] fn set_dot_val_helper( &self, scope: &mut Scope, @@ -1014,17 +999,16 @@ impl Engine<'_> { // xxx.lhs[idx_expr] // TODO - Allow chaining of indexing! - #[cfg(not(feature = "no_index"))] Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() { // xxx.id[idx_expr] Expr::Property(id, pos) => { let fn_name = make_getter(id); self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0) .and_then(|val| { - let (_, _, idx) = self + let (_, _, index) = self .get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level)?; - Self::update_indexed_value(val, idx, new_val.0.clone(), new_val.1) + Self::update_indexed_value(val, index, new_val.0.clone(), new_val.1) }) .and_then(|mut val| { let fn_name = make_setter(id); @@ -1060,14 +1044,13 @@ impl Engine<'_> { // xxx.lhs[idx_expr].rhs // TODO - Allow chaining of indexing! - #[cfg(not(feature = "no_index"))] Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() { // xxx.id[idx_expr].rhs Expr::Property(id, pos) => { let fn_name = make_getter(id); self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0) .and_then(|v| { - let (mut value, _, idx) = self.get_indexed_value( + let (mut value, _, index) = self.get_indexed_value( scope, fn_lib, &v, idx_expr, *op_pos, level, )?; @@ -1078,7 +1061,7 @@ impl Engine<'_> { )?; // In case the expression mutated `target`, we need to update it back into the scope because it is cloned. - Self::update_indexed_value(v, idx, value, val_pos) + Self::update_indexed_value(v, index, value, val_pos) }) .and_then(|mut v| { let fn_name = make_setter(id); @@ -1110,7 +1093,6 @@ impl Engine<'_> { } // Evaluate a dot chain setter - #[cfg(not(feature = "no_object"))] fn set_dot_val( &self, scope: &mut Scope, @@ -1148,9 +1130,8 @@ impl Engine<'_> { // lhs[idx_expr].??? // TODO - Allow chaining of indexing! - #[cfg(not(feature = "no_index"))] Expr::Index(lhs, idx_expr, op_pos) => { - let (idx_src_type, src, idx, mut target) = + let (idx_src_type, src, index, mut target) = self.eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level)?; let val_pos = new_val.1; let this_ptr = target.as_mut(); @@ -1171,7 +1152,7 @@ impl Engine<'_> { idx_src_type, scope, src, - idx, + index, (target, val_pos), )?; } @@ -1201,56 +1182,46 @@ impl Engine<'_> { let mut lhs_value = self.eval_expr(scope, fn_lib, lhs, level)?; let rhs_value = self.eval_expr(scope, fn_lib, rhs, level)?; - #[cfg(not(feature = "no_index"))] - { - if rhs_value.is::() { - let mut rhs_value = rhs_value.cast::(); - let def_value = false.into_dynamic(); - let mut result = false; + if rhs_value.is::() { + let mut rhs_value = rhs_value.cast::(); + let def_value = false.into_dynamic(); + let mut result = false; - // Call the '==' operator to compare each value - for value in rhs_value.iter_mut() { - let args = &mut [lhs_value.as_mut(), value.as_mut()]; - let def_value = Some(&def_value); - if self - .call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)? - .try_cast::() - .unwrap_or(false) - { - result = true; - break; - } + // Call the '==' operator to compare each value + for value in rhs_value.iter_mut() { + let args = &mut [lhs_value.as_mut(), value.as_mut()]; + let def_value = Some(&def_value); + if self + .call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)? + .try_cast::() + .unwrap_or(false) + { + result = true; + break; } - - return Ok(result.into_dynamic()); } - } - #[cfg(not(feature = "no_object"))] - { - if rhs_value.is::() { - let rhs_value = rhs_value.cast::(); + Ok(result.into_dynamic()) + } else if rhs_value.is::() { + let rhs_value = rhs_value.cast::(); - // Only allows String or char - return if lhs_value.is::() { - Ok(rhs_value - .contains_key(&lhs_value.cast::()) - .into_dynamic()) - } else if lhs_value.is::() { - Ok(rhs_value - .contains_key(&lhs_value.cast::().to_string()) - .into_dynamic()) - } else { - Err(EvalAltResult::ErrorInExpr(lhs.position())) - }; + // Only allows String or char + if lhs_value.is::() { + Ok(rhs_value + .contains_key(&lhs_value.cast::()) + .into_dynamic()) + } else if lhs_value.is::() { + Ok(rhs_value + .contains_key(&lhs_value.cast::().to_string()) + .into_dynamic()) + } else { + Err(EvalAltResult::ErrorInExpr(lhs.position())) } - } - - if rhs_value.is::() { + } else if rhs_value.is::() { let rhs_value = rhs_value.cast::(); // Only allows String or char - return if lhs_value.is::() { + if lhs_value.is::() { Ok(rhs_value .contains(&lhs_value.cast::()) .into_dynamic()) @@ -1258,10 +1229,10 @@ impl Engine<'_> { Ok(rhs_value.contains(lhs_value.cast::()).into_dynamic()) } else { Err(EvalAltResult::ErrorInExpr(lhs.position())) - }; + } + } else { + Err(EvalAltResult::ErrorInExpr(rhs.position())) } - - return Err(EvalAltResult::ErrorInExpr(rhs.position())); } /// Evaluate an expression @@ -1273,21 +1244,13 @@ impl Engine<'_> { level: usize, ) -> Result { match expr { - #[cfg(not(feature = "no_float"))] - Expr::FloatConstant(f, _) => Ok(f.into_dynamic()), - Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()), + Expr::FloatConstant(f, _) => Ok(f.into_dynamic()), Expr::StringConstant(s, _) => Ok(s.clone().into_owned().into_dynamic()), Expr::CharConstant(c, _) => Ok(c.into_dynamic()), Expr::Variable(id, pos) => Self::search_scope(scope, id, *pos).map(|(_, val)| val), Expr::Property(_, _) => panic!("unexpected property."), - // lhs[idx_expr] - #[cfg(not(feature = "no_index"))] - Expr::Index(lhs, idx_expr, op_pos) => self - .eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level) - .map(|(_, _, _, x)| x), - // Statement block Expr::Stmt(stmt, _) => self.eval_stmt(scope, fn_lib, stmt, level), @@ -1312,7 +1275,6 @@ impl Engine<'_> { } => { // Avoid referencing scope which is used below as mut let entry = ScopeSource { name, ..entry }; - *scope.get_mut(entry) = rhs_val.clone(); Ok(rhs_val) } @@ -1329,7 +1291,7 @@ impl Engine<'_> { // idx_lhs[idx_expr] = rhs #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, idx_expr, op_pos) => { - let (idx_src_type, src, idx, _) = + let (idx_src_type, src, index, _) = self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?; if let Some(src) = src { @@ -1344,7 +1306,7 @@ impl Engine<'_> { idx_src_type, scope, src, - idx, + index, (rhs_val, rhs.position()), )?), } @@ -1373,6 +1335,12 @@ impl Engine<'_> { } } + // lhs[idx_expr] + #[cfg(not(feature = "no_index"))] + Expr::Index(lhs, idx_expr, op_pos) => self + .eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level) + .map(|(_, _, _, x)| x), + #[cfg(not(feature = "no_object"))] Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, fn_lib, lhs, rhs, level), @@ -1417,35 +1385,14 @@ impl Engine<'_> { } match fn_name.as_ref() { - // Dump AST - KEYWORD_DUMP_AST => { - let pos = if args_expr_list.is_empty() { - *pos - } else { - args_expr_list[0].position() - }; - - // Change the argument to a debug dump of the expressions - let mut result = args_expr_list - .iter() - .map(|expr| format!("{:#?}", expr)) - .collect::>() - .join("\n") - .into_dynamic(); - - // Redirect call to `print` - let mut args = [result.as_mut()]; - self.call_fn_raw(None, fn_lib, KEYWORD_PRINT, &mut args, None, pos, level) - } - // type_of KEYWORD_TYPE_OF if args_expr_list.len() == 1 && !has_override(self, fn_lib, KEYWORD_TYPE_OF) => { - let r = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; + let result = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; Ok(self - .map_type_name((*r).type_name()) + .map_type_name((*result).type_name()) .to_string() .into_dynamic()) } @@ -1456,18 +1403,18 @@ impl Engine<'_> { && !has_override(self, fn_lib, KEYWORD_EVAL) => { let pos = args_expr_list[0].position(); - let r = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; + let result = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; // Get the script text by evaluating the expression - let script = - r.downcast_ref::() - .map(String::as_str) - .ok_or_else(|| { - EvalAltResult::ErrorMismatchOutputType( - r.type_name().into(), - pos, - ) - })?; + let script = result + .downcast_ref::() + .map(String::as_str) + .ok_or_else(|| { + EvalAltResult::ErrorMismatchOutputType( + result.type_name().into(), + pos, + ) + })?; // Compile the script text // No optimizations because we only run it once @@ -1475,19 +1422,15 @@ impl Engine<'_> { .compile_with_scope_and_optimization_level( &Scope::new(), script, - #[cfg(not(feature = "no_optimize"))] OptimizationLevel::None, ) .map_err(EvalAltResult::ErrorParsing)?; // If new functions are defined within the eval string, it is an error - #[cfg(not(feature = "no_function"))] - { - if ast.1.len() > 0 { - return Err(EvalAltResult::ErrorParsing( - ParseErrorType::WrongFnDefinition.into_err(pos), - )); - } + if ast.1.len() > 0 { + return Err(EvalAltResult::ErrorParsing( + ParseErrorType::WrongFnDefinition.into_err(pos), + )); } if let Some(lib) = fn_lib { @@ -1559,6 +1502,8 @@ impl Engine<'_> { Expr::True(_) => Ok(true.into_dynamic()), Expr::False(_) => Ok(false.into_dynamic()), Expr::Unit(_) => Ok(().into_dynamic()), + + expr => panic!("should not appear: {:?}", expr), } } @@ -1753,7 +1698,7 @@ impl Engine<'_> { } /// Print/debug to stdout -#[cfg(not(feature = "no_std"))] fn default_print(s: &str) { + #[cfg(not(feature = "no_std"))] println!("{}", s); } diff --git a/src/error.rs b/src/error.rs index 5ff11832..6ad21bbd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -37,6 +37,10 @@ impl fmt::Display for LexError { } /// Type of error encountered when parsing a script. +/// +/// Some errors never appear when certain features are turned on. +/// They still exist so that the application can turn features on and off without going through +/// massive code changes to remove/add back enum variants in match statements. #[derive(Debug, PartialEq, Clone)] pub enum ParseErrorType { /// Error in the script text. Wrapped value is the error message. @@ -51,19 +55,21 @@ pub enum ParseErrorType { MalformedCallExpr(String), /// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error description (if any). /// - /// Not available under the `no_index` feature. - #[cfg(not(feature = "no_index"))] + /// Never appears under the `no_index` feature. MalformedIndexExpr(String), /// An expression in an `in` expression has syntax error. Wrapped value is the error description (if any). + /// + /// Never appears under the `no_object` and `no_index` features combination. MalformedInExpr(String), /// A map definition has duplicated property names. Wrapped value is the property name. /// - /// Not available under the `no_object` feature. - #[cfg(not(feature = "no_object"))] + /// Never appears under the `no_object` feature. DuplicatedProperty(String), /// Invalid expression assigned to constant. Wrapped value is the name of the constant. ForbiddenConstantExpr(String), /// Missing a property name for custom types and maps. + /// + /// Never appears under the `no_object` feature. PropertyExpected, /// Missing a variable name after the `let`, `const` or `for` keywords. VariableExpected, @@ -71,28 +77,23 @@ pub enum ParseErrorType { ExprExpected(String), /// Defining a function `fn` in an appropriate place (e.g. inside another function). /// - /// Not available under the `no_function` feature. - #[cfg(not(feature = "no_function"))] + /// Never appears under the `no_function` feature. WrongFnDefinition, /// Missing a function name after the `fn` keyword. /// - /// Not available under the `no_function` feature. - #[cfg(not(feature = "no_function"))] + /// Never appears under the `no_function` feature. FnMissingName, /// A function definition is missing the parameters list. Wrapped value is the function name. /// - /// Not available under the `no_function` feature. - #[cfg(not(feature = "no_function"))] + /// Never appears under the `no_function` feature. FnMissingParams(String), /// A function definition has duplicated parameters. Wrapped values are the function name and parameter name. /// - /// Not available under the `no_function` feature. - #[cfg(not(feature = "no_function"))] + /// Never appears under the `no_function` feature. FnDuplicatedParam(String, String), /// A function definition is missing the body. Wrapped value is the function name. /// - /// Not available under the `no_function` feature. - #[cfg(not(feature = "no_function"))] + /// Never appears under the `no_function` feature. FnMissingBody(String), /// Assignment to an inappropriate LHS (left-hand-side) expression. AssignmentToInvalidLHS, @@ -138,24 +139,17 @@ impl ParseError { ParseErrorType::UnknownOperator(_) => "Unknown operator", ParseErrorType::MissingToken(_, _) => "Expecting a certain token that is missing", ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments", - #[cfg(not(feature = "no_index"))] ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression", ParseErrorType::MalformedInExpr(_) => "Invalid 'in' expression", - #[cfg(not(feature = "no_object"))] ParseErrorType::DuplicatedProperty(_) => "Duplicated property in object map literal", ParseErrorType::ForbiddenConstantExpr(_) => "Expecting a constant", ParseErrorType::PropertyExpected => "Expecting name of a property", ParseErrorType::VariableExpected => "Expecting name of a variable", ParseErrorType::ExprExpected(_) => "Expecting an expression", - #[cfg(not(feature = "no_function"))] ParseErrorType::FnMissingName => "Expecting name in function declaration", - #[cfg(not(feature = "no_function"))] ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration", - #[cfg(not(feature = "no_function"))] ParseErrorType::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration", - #[cfg(not(feature = "no_function"))] ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration", - #[cfg(not(feature = "no_function"))] ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function", ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression", ParseErrorType::AssignmentToCopy => "Cannot assign to this expression because it will only be changing a copy of the value", @@ -178,7 +172,6 @@ impl fmt::Display for ParseError { } ParseErrorType::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s)?, - #[cfg(not(feature = "no_index"))] ParseErrorType::MalformedIndexExpr(s) => { write!(f, "{}", if s.is_empty() { self.desc() } else { s })? } @@ -187,24 +180,20 @@ impl fmt::Display for ParseError { write!(f, "{}", if s.is_empty() { self.desc() } else { s })? } - #[cfg(not(feature = "no_object"))] ParseErrorType::DuplicatedProperty(s) => { write!(f, "Duplicated property '{}' for object map literal", s)? } ParseErrorType::ExprExpected(s) => write!(f, "Expecting {} expression", s)?, - #[cfg(not(feature = "no_function"))] ParseErrorType::FnMissingParams(s) => { write!(f, "Expecting parameters for function '{}'", s)? } - #[cfg(not(feature = "no_function"))] ParseErrorType::FnMissingBody(s) => { write!(f, "Expecting body statement block for function '{}'", s)? } - #[cfg(not(feature = "no_function"))] ParseErrorType::FnDuplicatedParam(s, arg) => { write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)? } diff --git a/src/optimize.rs b/src/optimize.rs index 86950423..fef42473 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,9 +1,7 @@ -#![cfg(not(feature = "no_optimize"))] - use crate::any::{Any, Dynamic}; use crate::engine::{ - Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_EVAL, - KEYWORD_PRINT, KEYWORD_TYPE_OF, + Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, + KEYWORD_TYPE_OF, }; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Position, ReturnType, Stmt, AST}; use crate::result::EvalAltResult; @@ -33,6 +31,17 @@ pub enum OptimizationLevel { Full, } +impl OptimizationLevel { + /// Is the `OptimizationLevel` None. + pub fn is_none(self) -> bool { + self == Self::None + } + /// Is the `OptimizationLevel` Full. + pub fn is_full(self) -> bool { + self == Self::Full + } +} + /// Mutable state throughout an optimization pass. struct State<'a> { /// Has the AST been changed during this pass? @@ -519,10 +528,6 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { ), }, - // Do not optimize anything within dump_ast - Expr::FunctionCall(id, args, def_value, pos) if id == KEYWORD_DUMP_AST => - Expr::FunctionCall(id, args, def_value, pos), - // Do not call some special keywords Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref())=> Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos), @@ -587,7 +592,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { } } -pub(crate) fn optimize<'a>( +fn optimize<'a>( statements: Vec, engine: &Engine<'a>, scope: &Scope, @@ -675,6 +680,9 @@ pub fn optimize_into_ast( functions: Vec, level: OptimizationLevel, ) -> AST { + #[cfg(feature = "no_optimize")] + const level: OptimizationLevel = OptimizationLevel::None; + let fn_lib: Vec<_> = functions .iter() .map(|fn_def| (fn_def.name.as_str(), fn_def.params.len())) @@ -685,7 +693,7 @@ pub fn optimize_into_ast( .iter() .cloned() .map(|mut fn_def| { - if level != OptimizationLevel::None { + if !level.is_none() { let pos = fn_def.body.position(); // Optimize the function body diff --git a/src/parser.rs b/src/parser.rs index 569d0ac4..07d5433c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,12 +1,10 @@ //! Main module defining the lexer and parser. use crate::any::{Any, AnyExt, Dynamic}; -use crate::engine::{Engine, FunctionsLib}; +use crate::engine::{Array, Engine, FunctionsLib, Map}; use crate::error::{LexError, ParseError, ParseErrorType}; -use crate::scope::{EntryType as ScopeEntryType, Scope}; - -#[cfg(not(feature = "no_optimize"))] use crate::optimize::{optimize_into_ast, OptimizationLevel}; +use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::stdlib::{ borrow::Cow, @@ -42,7 +40,6 @@ pub type INT = i32; /// The system floating-point type. /// /// Not available under the `no_float` feature. -#[cfg(not(feature = "no_float"))] pub type FLOAT = f64; type LERR = LexError; @@ -398,7 +395,6 @@ pub enum Expr { /// Integer constant. IntegerConstant(INT, Position), /// Floating-point constant. - #[cfg(not(feature = "no_float"))] FloatConstant(FLOAT, Position), /// Character constant. CharConstant(char, Position), @@ -415,16 +411,12 @@ pub enum Expr { /// expr = expr Assignment(Box, Box, Position), /// lhs.rhs - #[cfg(not(feature = "no_object"))] Dot(Box, Box, Position), /// expr[expr] - #[cfg(not(feature = "no_index"))] Index(Box, Box, Position), /// [ expr, ... ] - #[cfg(not(feature = "no_index"))] Array(Vec, Position), /// #{ name:expr, ... } - #[cfg(not(feature = "no_object"))] Map(Vec<(String, Expr, Position)>, Position), /// lhs in rhs In(Box, Box, Position), @@ -449,29 +441,25 @@ impl Expr { pub fn get_constant_value(&self) -> Dynamic { match self { Self::IntegerConstant(i, _) => i.into_dynamic(), + Self::FloatConstant(f, _) => f.into_dynamic(), Self::CharConstant(c, _) => c.into_dynamic(), Self::StringConstant(s, _) => s.clone().into_owned().into_dynamic(), Self::True(_) => true.into_dynamic(), Self::False(_) => false.into_dynamic(), Self::Unit(_) => ().into_dynamic(), - #[cfg(not(feature = "no_index"))] Self::Array(items, _) if items.iter().all(Self::is_constant) => items .iter() .map(Self::get_constant_value) .collect::>() .into_dynamic(), - #[cfg(not(feature = "no_object"))] Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => items .iter() .map(|(k, v, _)| (k.clone(), v.get_constant_value())) .collect::>() .into_dynamic(), - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(f, _) => f.into_dynamic(), - _ => panic!("cannot get value of non-constant expression"), } } @@ -484,18 +472,15 @@ impl Expr { pub fn get_constant_str(&self) -> String { match self { Self::IntegerConstant(i, _) => i.to_string(), + Self::FloatConstant(f, _) => f.to_string(), Self::CharConstant(c, _) => c.to_string(), Self::StringConstant(_, _) => "string".to_string(), Self::True(_) => "true".to_string(), Self::False(_) => "false".to_string(), Self::Unit(_) => "()".to_string(), - #[cfg(not(feature = "no_index"))] Self::Array(items, _) if items.iter().all(Self::is_constant) => "array".to_string(), - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(f, _) => f.to_string(), - _ => panic!("cannot get value of non-constant expression"), } } @@ -504,8 +489,11 @@ impl Expr { pub fn position(&self) -> Position { match self { Self::IntegerConstant(_, pos) + | Self::FloatConstant(_, pos) | Self::CharConstant(_, pos) | Self::StringConstant(_, pos) + | Self::Array(_, pos) + | Self::Map(_, pos) | Self::Variable(_, pos) | Self::Property(_, pos) | Self::Stmt(_, pos) @@ -517,22 +505,9 @@ impl Expr { | Self::False(pos) | Self::Unit(pos) => *pos, - Self::Assignment(expr, _, _) => expr.position(), - - #[cfg(not(feature = "no_object"))] - Self::Dot(expr, _, _) => expr.position(), - - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_, pos) => *pos, - - #[cfg(not(feature = "no_index"))] - Self::Array(_, pos) => *pos, - - #[cfg(not(feature = "no_object"))] - Self::Map(_, pos) => *pos, - - #[cfg(not(feature = "no_index"))] - Self::Index(expr, _, _) => expr.position(), + Self::Assignment(expr, _, _) | Self::Dot(expr, _, _) | Self::Index(expr, _, _) => { + expr.position() + } } } @@ -541,13 +516,9 @@ impl Expr { /// A pure expression has no side effects. pub fn is_pure(&self) -> bool { match self { - #[cfg(not(feature = "no_index"))] Self::Array(expressions, _) => expressions.iter().all(Self::is_pure), - #[cfg(not(feature = "no_index"))] - Self::Index(x, y, _) => x.is_pure() && y.is_pure(), - - Self::And(x, y, _) | Self::Or(x, y, _) | Self::In(x, y, _) => { + Self::Index(x, y, _) | Self::And(x, y, _) | Self::Or(x, y, _) | Self::In(x, y, _) => { x.is_pure() && y.is_pure() } @@ -561,21 +532,17 @@ impl Expr { pub fn is_constant(&self) -> bool { match self { Self::IntegerConstant(_, _) + | Self::FloatConstant(_, _) | Self::CharConstant(_, _) | Self::StringConstant(_, _) | Self::True(_) | Self::False(_) | Self::Unit(_) => true, - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_, _) => true, - // An array literal is constant if all items are constant - #[cfg(not(feature = "no_index"))] Self::Array(expressions, _) => expressions.iter().all(Self::is_constant), // An map literal is constant if all items are constant - #[cfg(not(feature = "no_object"))] Self::Map(items, _) => items.iter().map(|(_, expr, _)| expr).all(Self::is_constant), // Check in expression @@ -594,7 +561,6 @@ impl Expr { #[derive(Debug, PartialEq, Clone)] pub enum Token { IntegerConstant(INT), - #[cfg(not(feature = "no_float"))] FloatConstant(FLOAT), Identifier(String), CharConstant(char), @@ -603,9 +569,7 @@ pub enum Token { RightBrace, LeftParen, RightParen, - #[cfg(not(feature = "no_index"))] LeftBracket, - #[cfg(not(feature = "no_index"))] RightBracket, Plus, UnaryPlus, @@ -673,7 +637,6 @@ impl Token { match self { IntegerConstant(i) => i.to_string().into(), - #[cfg(not(feature = "no_float"))] FloatConstant(f) => f.to_string().into(), Identifier(s) => s.into(), CharConstant(c) => c.to_string().into(), @@ -685,9 +648,7 @@ impl Token { RightBrace => "}", LeftParen => "(", RightParen => ")", - #[cfg(not(feature = "no_index"))] LeftBracket => "[", - #[cfg(not(feature = "no_index"))] RightBracket => "]", Plus => "+", UnaryPlus => "+", @@ -762,6 +723,8 @@ impl Token { // RightBrace | {expr} - expr not unary & is closing LeftParen | // {-expr} - is unary // RightParen | (expr) - expr not unary & is closing + LeftBracket | // [-expr] - is unary + // RightBracket | [expr] - expr not unary & is closing Plus | UnaryPlus | Minus | @@ -805,10 +768,6 @@ impl Token { In | PowerOfAssign => true, - #[cfg(not(feature = "no_index"))] - LeftBracket => true, // [-expr] - is unary - // RightBracket | [expr] - expr not unary & is closing - _ => false, } } @@ -1211,9 +1170,7 @@ impl<'a> TokenIterator<'a> { (')', _) => return Some((Token::RightParen, pos)), // Indexing - #[cfg(not(feature = "no_index"))] ('[', _) => return Some((Token::LeftBracket, pos)), - #[cfg(not(feature = "no_index"))] (']', _) => return Some((Token::RightBracket, pos)), // Map literal @@ -1506,7 +1463,6 @@ fn parse_call_expr<'a, S: Into> + Display>( } /// Parse an indexing expression. -#[cfg(not(feature = "no_index"))] fn parse_index_expr<'a>( lhs: Box, input: &mut Peekable>, @@ -1528,7 +1484,6 @@ fn parse_index_expr<'a>( Expr::IntegerConstant(_, pos) => match *lhs { Expr::Array(_, _) | Expr::StringConstant(_, _) => (), - #[cfg(not(feature = "no_object"))] Expr::Map(_, _) => { return Err(PERR::MalformedIndexExpr( "Object map access expects string index, not a number".into(), @@ -1556,7 +1511,6 @@ fn parse_index_expr<'a>( // lhs[string] Expr::StringConstant(_, pos) => match *lhs { - #[cfg(not(feature = "no_object"))] Expr::Map(_, _) => (), Expr::Array(_, _) | Expr::StringConstant(_, _) => { @@ -1584,7 +1538,6 @@ fn parse_index_expr<'a>( }, // lhs[float] - #[cfg(not(feature = "no_float"))] Expr::FloatConstant(_, pos) => { return Err(PERR::MalformedIndexExpr( "Array access expects integer index, not a float".into(), @@ -1673,7 +1626,6 @@ fn parse_ident_expr<'a, S: Into> + Display>( } /// Parse an array literal. -#[cfg(not(feature = "no_index"))] fn parse_array_literal<'a>( input: &mut Peekable>, begin: Position, @@ -1715,7 +1667,6 @@ fn parse_array_literal<'a>( } /// Parse a map literal. -#[cfg(not(feature = "no_object"))] fn parse_map_literal<'a>( input: &mut Peekable>, begin: Position, @@ -1840,10 +1791,8 @@ fn parse_primary<'a>( let mut can_be_indexed = false; let mut root_expr = match token { - #[cfg(not(feature = "no_float"))] - (Token::FloatConstant(x), pos) => Ok(Expr::FloatConstant(x, pos)), - (Token::IntegerConstant(x), pos) => Ok(Expr::IntegerConstant(x, pos)), + (Token::FloatConstant(x), pos) => Ok(Expr::FloatConstant(x, pos)), (Token::CharConstant(c), pos) => Ok(Expr::CharConstant(c, pos)), (Token::StringConst(s), pos) => { can_be_indexed = true; @@ -1875,9 +1824,11 @@ fn parse_primary<'a>( } }?; + #[cfg(feature = "no_index")] + let can_be_indexed = false; + if can_be_indexed { // Tail processing all possible indexing - #[cfg(not(feature = "no_index"))] while let Some((Token::LeftBracket, pos)) = input.peek() { let pos = *pos; input.next(); @@ -1918,10 +1869,13 @@ fn parse_unary<'a>( .map(|x| Expr::IntegerConstant(x, pos)) .or_else(|| { #[cfg(not(feature = "no_float"))] - return Some(Expr::FloatConstant(-(i as FLOAT), pos)); - + { + Some(Expr::FloatConstant(-(i as FLOAT), pos)) + } #[cfg(feature = "no_float")] - return None; + { + None + } }) .ok_or_else(|| { PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string()) @@ -1976,48 +1930,41 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result { assert!(is_top, "property expected but gets variable"); None } // property[...] - #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) => { assert!(!is_top, "variable expected but gets property"); None } // idx_lhs[...] - #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, _, pos) => match idx_lhs.as_ref() { Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)), _ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)), }, // dot_lhs.dot_rhs - #[cfg(not(feature = "no_object"))] Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() { // var.dot_rhs Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false), // property.dot_rhs Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false), // var[...] - #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) && is_top => { valid_assignment_chain(dot_rhs, false) } // property[...] - #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) && !is_top => { valid_assignment_chain(dot_rhs, false) } // idx_lhs[...] - #[cfg(not(feature = "no_index"))] Expr::Index(idx_lhs, _, _) => { Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position())) } @@ -2055,15 +2002,8 @@ fn parse_op_assignment>>( /// Parse an 'in' expression. fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { match (&lhs, &rhs) { - #[cfg(not(feature = "no_float"))] - (_, Expr::FloatConstant(_, pos)) => { - return Err(PERR::MalformedInExpr( - "'in' expression expects a string, array or object map".into(), - ) - .into_err(*pos)) - } - (_, Expr::IntegerConstant(_, pos)) + | (_, Expr::FloatConstant(_, pos)) | (_, Expr::And(_, _, pos)) | (_, Expr::Or(_, _, pos)) | (_, Expr::In(_, _, pos)) @@ -2082,7 +2022,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), // 123.456 in "xxxx" - #[cfg(not(feature = "no_float"))] (Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not a float".into(), @@ -2109,7 +2048,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not an array".into(), @@ -2117,7 +2055,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not an object map".into(), @@ -2134,13 +2071,10 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), // 123.456 in #{...} - #[cfg(not(feature = "no_float"))] - #[cfg(not(feature = "no_object"))] (Expr::FloatConstant(_, pos), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not a float".into(), @@ -2148,7 +2082,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not a number".into(), @@ -2157,7 +2090,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result Result { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not an array".into(), @@ -2178,7 +2108,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not an object map".into(), @@ -2186,7 +2115,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not ()".into(), @@ -2269,7 +2197,6 @@ fn parse_binary_op<'a>( pos, )), // xxx.lhs[idx] - #[cfg(not(feature = "no_index"))] Expr::Index(lhs, idx, pos) => { Ok(Expr::Index(Box::new(check_property(*lhs)?), idx, pos)) } @@ -2290,37 +2217,37 @@ fn parse_binary_op<'a>( Token::EqualsTo => Expr::FunctionCall( "==".into(), vec![current_lhs, rhs], - Some(Box::new(false)), + Some((false).into_dynamic()), pos, ), Token::NotEqualsTo => Expr::FunctionCall( "!=".into(), vec![current_lhs, rhs], - Some(Box::new(false)), + Some((false).into_dynamic()), pos, ), Token::LessThan => Expr::FunctionCall( "<".into(), vec![current_lhs, rhs], - Some(Box::new(false)), + Some((false).into_dynamic()), pos, ), Token::LessThanEqualsTo => Expr::FunctionCall( "<=".into(), vec![current_lhs, rhs], - Some(Box::new(false)), + Some((false).into_dynamic()), pos, ), Token::GreaterThan => Expr::FunctionCall( ">".into(), vec![current_lhs, rhs], - Some(Box::new(false)), + Some((false).into_dynamic()), pos, ), Token::GreaterThanEqualsTo => Expr::FunctionCall( ">=".into(), vec![current_lhs, rhs], - Some(Box::new(false)), + Some((false).into_dynamic()), pos, ), @@ -2680,7 +2607,6 @@ fn parse_stmt<'a>( } /// Parse a function definition. -#[cfg(not(feature = "no_function"))] fn parse_fn<'a>( input: &mut Peekable>, allow_stmt_expr: bool, @@ -2780,7 +2706,6 @@ pub fn parse_global_expr<'a, 'e>( Ok( // Optimize AST - #[cfg(not(feature = "no_optimize"))] optimize_into_ast( engine, scope, @@ -2788,20 +2713,6 @@ pub fn parse_global_expr<'a, 'e>( vec![], engine.optimization_level, ), - // - // Do not optimize AST if `no_optimize` - #[cfg(feature = "no_optimize")] - AST( - vec![Stmt::Expr(Box::new(expr))], - #[cfg(feature = "sync")] - { - Arc::new(FunctionsLib::new()) - }, - #[cfg(not(feature = "sync"))] - { - Rc::new(FunctionsLib::new()) - }, - ), ) } @@ -2813,9 +2724,9 @@ fn parse_global_level<'a>( let mut functions = Vec::::new(); while input.peek().is_some() { + // Collect all the function definitions #[cfg(not(feature = "no_function"))] { - // Collect all the function definitions if matches!(input.peek().expect("should not be None"), (Token::Fn, _)) { let f = parse_fn(input, true)?; @@ -2866,18 +2777,13 @@ pub fn parse<'a, 'e>( input: &mut Peekable>, engine: &Engine<'e>, scope: &Scope, - #[cfg(not(feature = "no_optimize"))] optimization_level: OptimizationLevel, + optimization_level: OptimizationLevel, ) -> Result { let (statements, functions) = parse_global_level(input)?; Ok( // Optimize AST - #[cfg(not(feature = "no_optimize"))] optimize_into_ast(engine, scope, statements, functions, optimization_level), - // - // Do not optimize AST if `no_optimize` - #[cfg(feature = "no_optimize")] - AST(statements, Arc::new(FunctionsLib::from_vec(functions))), ) } @@ -2898,6 +2804,47 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { Expr::False(pos) }) } else { + #[cfg(not(feature = "no_index"))] + { + if value.is::() { + let array = value.cast::(); + let items: Vec<_> = array + .into_iter() + .map(|x| map_dynamic_to_expr(x, pos)) + .collect(); + if items.iter().all(Option::is_some) { + return Some(Expr::Array( + items.into_iter().map(Option::unwrap).collect(), + pos, + )); + } else { + return None; + } + } + } + + #[cfg(not(feature = "no_object"))] + { + if value.is::() { + let map = value.cast::(); + let items: Vec<_> = map + .into_iter() + .map(|(k, v)| (k, map_dynamic_to_expr(v, pos), pos)) + .collect(); + if items.iter().all(|(_, expr, _)| expr.is_some()) { + return Some(Expr::Map( + items + .into_iter() + .map(|(k, expr, pos)| (k, expr.unwrap(), pos)) + .collect(), + pos, + )); + } else { + return None; + } + } + } + #[cfg(not(feature = "no_float"))] { if value.is::() { diff --git a/src/result.rs b/src/result.rs index 29af3bbd..ce2b1b87 100644 --- a/src/result.rs +++ b/src/result.rs @@ -25,7 +25,7 @@ pub enum EvalAltResult { /// Error reading from a script file. Wrapped value is the path of the script file. /// - /// Not available under the `no_std` feature. + /// Never appears under the `no_std` feature. #[cfg(not(feature = "no_std"))] ErrorReadingScriptFile(PathBuf, std::io::Error),