diff --git a/src/engine.rs b/src/engine.rs index 401edc2e..ee102724 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -6,7 +6,7 @@ use crate::fn_native::{Callback, FnPtr, OnVarCallback}; use crate::module::{Module, ModuleRef}; use crate::optimize::OptimizationLevel; use crate::packages::{Package, PackagesCollection, StandardPackage}; -use crate::parser::{Expr, ReturnType, Stmt}; +use crate::parser::{BinaryExpr, Expr, ReturnType, Stmt}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; @@ -901,18 +901,17 @@ impl Engine { match rhs { // xxx[idx].expr... | xxx[idx][expr]... Expr::Dot(x) | Expr::Index(x) => { - let (idx, expr, pos) = x.as_ref(); - let idx_pos = idx.position(); + let idx_pos = x.lhs.position(); let idx_val = idx_val.as_value(); let obj_ptr = &mut self.get_indexed_mut( state, lib, target, idx_val, idx_pos, false, true, level, )?; self.eval_dot_index_chain_helper( - state, lib, this_ptr, obj_ptr, expr, idx_values, next_chain, level, + state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain, level, new_val, ) - .map_err(|err| err.fill_position(*pos)) + .map_err(|err| err.fill_position(x.pos)) } // xxx[rhs] = new_val _ if new_val.is_some() => { @@ -1031,9 +1030,7 @@ impl Engine { } // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr Expr::Index(x) | Expr::Dot(x) if target.is::() => { - let (sub_lhs, expr, pos) = x.as_ref(); - - let mut val = match sub_lhs { + let mut val = match &x.lhs { Expr::Property(p) => { let ((prop, _, _), pos) = p.as_ref(); let index = prop.clone().into(); @@ -1061,16 +1058,14 @@ impl Engine { }; self.eval_dot_index_chain_helper( - state, lib, this_ptr, &mut val, expr, idx_values, next_chain, level, + state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, level, new_val, ) - .map_err(|err| err.fill_position(*pos)) + .map_err(|err| err.fill_position(x.pos)) } // xxx.sub_lhs[expr] | xxx.sub_lhs.expr Expr::Index(x) | Expr::Dot(x) => { - let (sub_lhs, expr, _) = x.as_ref(); - - match sub_lhs { + match &x.lhs { // xxx.prop[expr] | xxx.prop.expr Expr::Property(p) => { let ((_, getter, setter), pos) = p.as_ref(); @@ -1091,13 +1086,13 @@ impl Engine { lib, this_ptr, &mut val.into(), - expr, + &x.rhs, idx_values, next_chain, level, new_val, ) - .map_err(|err| err.fill_position(*pos))?; + .map_err(|err| err.fill_position(x.pos))?; // Feed the value back via a setter just in case it has been updated if updated || may_be_changed { @@ -1113,7 +1108,7 @@ impl Engine { EvalAltResult::ErrorDotExpr(_, _) => { Ok(Default::default()) } - _ => Err(err.fill_position(*pos)), + _ => Err(err.fill_position(x.pos)), }, )?; } @@ -1121,8 +1116,8 @@ impl Engine { Ok((result, may_be_changed)) } // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr - Expr::FnCall(x) if x.1.is_none() => { - let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref(); + Expr::FnCall(f) if f.1.is_none() => { + let ((name, native, _, pos), _, hash, _, def_val) = f.as_ref(); let def_val = def_val.map(Into::::into); let args = idx_val.as_fn_call_args(); let (mut val, _) = self @@ -1135,7 +1130,7 @@ impl Engine { let target = &mut val.into(); self.eval_dot_index_chain_helper( - state, lib, this_ptr, target, expr, idx_values, next_chain, + state, lib, this_ptr, target, &x.rhs, idx_values, next_chain, level, new_val, ) .map_err(|err| err.fill_position(*pos)) @@ -1168,7 +1163,14 @@ impl Engine { level: usize, new_val: Option<(Dynamic, Position)>, ) -> Result> { - let ((dot_lhs, dot_rhs, op_pos), chain_type) = match expr { + let ( + BinaryExpr { + lhs: dot_lhs, + rhs: dot_rhs, + pos: op_pos, + }, + chain_type, + ) = match expr { Expr::Index(x) => (x.as_ref(), ChainType::Index), Expr::Dot(x) => (x.as_ref(), ChainType::Dot), _ => unreachable!(), @@ -1266,7 +1268,7 @@ impl Engine { Expr::FnCall(_) => unreachable!(), Expr::Property(_) => idx_values.push(IndexChainValue::None), Expr::Index(x) | Expr::Dot(x) => { - let (lhs, rhs, _) = x.as_ref(); + let BinaryExpr { lhs, rhs, .. } = x.as_ref(); // Evaluate in left-to-right order let lhs_val = match lhs { @@ -1721,33 +1723,33 @@ impl Engine { .map_err(|err| err.fill_position(*pos)) } - Expr::In(x) => self.eval_in_expr(scope, mods, state, lib, this_ptr, &x.0, &x.1, level), + Expr::In(x) => { + self.eval_in_expr(scope, mods, state, lib, this_ptr, &x.lhs, &x.rhs, level) + } Expr::And(x) => { - let (lhs, rhs, _) = x.as_ref(); Ok((self - .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, lhs.position()))? + .map_err(|err| self.make_type_mismatch_err::(err, x.lhs.position()))? && // Short-circuit using && self - .eval_expr(scope, mods, state, lib, this_ptr, rhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, rhs.position()))?) + .map_err(|err| self.make_type_mismatch_err::(err, x.rhs.position()))?) .into()) } Expr::Or(x) => { - let (lhs, rhs, _) = x.as_ref(); Ok((self - .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, lhs.position()))? + .map_err(|err| self.make_type_mismatch_err::(err, x.lhs.position()))? || // Short-circuit using || self - .eval_expr(scope, mods, state, lib, this_ptr, rhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, rhs.position()))?) + .map_err(|err| self.make_type_mismatch_err::(err, x.rhs.position()))?) .into()) } @@ -1755,9 +1757,9 @@ impl Engine { Expr::False(_) => Ok(false.into()), Expr::Unit(_) => Ok(().into()), - Expr::Custom(x) => { - let func = (x.0).func(); - let expressions = (x.0) + Expr::Custom(custom) => { + let func = custom.func(); + let expressions = custom .keywords() .iter() .map(Into::into) diff --git a/src/optimize.rs b/src/optimize.rs index 9672fc7f..0c7e485a 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -7,7 +7,7 @@ use crate::engine::{ }; use crate::fn_call::run_builtin_binary_op; use crate::module::Module; -use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST}; +use crate::parser::{map_dynamic_to_expr, BinaryExpr, Expr, ScriptFnDef, Stmt, AST}; use crate::scope::{Entry as ScopeEntry, Scope}; use crate::token::{is_valid_identifier, Position}; use crate::{calc_fn_hash, StaticVec}; @@ -447,7 +447,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { // lhs.rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(x) => match (x.0, x.1) { + Expr::Dot(x) => match (x.lhs, x.rhs) { // map.string (Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => { let ((prop, _, _), _) = p.as_ref(); @@ -460,12 +460,16 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { .unwrap_or_else(|| Expr::Unit(pos)) } // lhs.rhs - (lhs, rhs) => Expr::Dot(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))) + (lhs, rhs) => Expr::Dot(Box::new(BinaryExpr { + lhs: optimize_expr(lhs, state), + rhs: optimize_expr(rhs, state), + pos: x.pos + })) } // lhs[rhs] #[cfg(not(feature = "no_index"))] - Expr::Index(x) => match (x.0, x.1) { + Expr::Index(x) => match (x.lhs, x.rhs) { // array[int] (Expr::Array(mut a), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < a.0.len() && a.0.iter().all(Expr::is_pure) => @@ -494,7 +498,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { Expr::CharConstant(Box::new((s.0.chars().nth(i.0 as usize).unwrap(), s.1))) } // lhs[rhs] - (lhs, rhs) => Expr::Index(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))), + (lhs, rhs) => Expr::Index(Box::new(BinaryExpr { + lhs: optimize_expr(lhs, state), + rhs: optimize_expr(rhs, state), + pos: x.pos + })), }, // [ items .. ] #[cfg(not(feature = "no_index"))] @@ -507,7 +515,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { .into_iter().map(|((key, pos), expr)| ((key, pos), optimize_expr(expr, state))) .collect(), m.1))), // lhs in rhs - Expr::In(x) => match (x.0, x.1) { + Expr::In(x) => match (x.lhs, x.rhs) { // "xxx" in "xxxxx" (Expr::StringConstant(a), Expr::StringConstant(b)) => { state.set_dirty(); @@ -539,10 +547,14 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } } // lhs in rhs - (lhs, rhs) => Expr::In(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))), + (lhs, rhs) => Expr::In(Box::new(BinaryExpr { + lhs: optimize_expr(lhs, state), + rhs: optimize_expr(rhs, state), + pos: x.pos + })), }, // lhs && rhs - Expr::And(x) => match (x.0, x.1) { + Expr::And(x) => match (x.lhs, x.rhs) { // true && rhs -> rhs (Expr::True(_), rhs) => { state.set_dirty(); @@ -559,10 +571,14 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { optimize_expr(lhs, state) } // lhs && rhs - (lhs, rhs) => Expr::And(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))), + (lhs, rhs) => Expr::And(Box::new(BinaryExpr { + lhs: optimize_expr(lhs, state), + rhs: optimize_expr(rhs, state), + pos: x.pos + })), }, // lhs || rhs - Expr::Or(x) => match (x.0, x.1) { + Expr::Or(x) => match (x.lhs, x.rhs) { // false || rhs -> rhs (Expr::False(_), rhs) => { state.set_dirty(); @@ -579,7 +595,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { optimize_expr(lhs, state) } // lhs || rhs - (lhs, rhs) => Expr::Or(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))), + (lhs, rhs) => Expr::Or(Box::new(BinaryExpr { + lhs: optimize_expr(lhs, state), + rhs: optimize_expr(rhs, state), + pos: x.pos + })), }, // Do not call some special keywords diff --git a/src/parser.rs b/src/parser.rs index 7ac6ad02..fb7446d9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -924,19 +924,23 @@ impl Stmt { /// /// This type is volatile and may change. #[derive(Clone)] -pub struct CustomExpr(pub StaticVec, pub Shared); +pub struct CustomExpr { + keywords: StaticVec, + func: Shared, + pos: Position, +} impl fmt::Debug for CustomExpr { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) + fmt::Debug::fmt(&self.keywords, f) } } impl Hash for CustomExpr { #[inline(always)] fn hash(&self, state: &mut H) { - self.0.hash(state); + self.keywords.hash(state); } } @@ -944,12 +948,17 @@ impl CustomExpr { /// Get the keywords for this `CustomExpr`. #[inline(always)] pub fn keywords(&self) -> &[Expr] { - &self.0 + &self.keywords } /// Get the implementation function for this `CustomExpr`. #[inline(always)] pub fn func(&self) -> &FnCustomSyntaxEval { - self.1.as_ref() + self.func.as_ref() + } + /// Get the position of this `CustomExpr`. + #[inline(always)] + pub fn position(&self) -> Position { + self.pos } } @@ -975,6 +984,13 @@ impl Hash for FloatWrapper { } } +#[derive(Debug, Clone, Hash)] +pub struct BinaryExpr { + pub lhs: Expr, + pub rhs: Expr, + pub pos: Position, +} + /// _[INTERNALS]_ An expression sub-tree. /// Exported under the `internals` feature only. /// @@ -1027,19 +1043,19 @@ pub enum Expr { /// expr op= expr Assignment(Box<(Expr, Cow<'static, str>, Expr, Position)>), /// lhs.rhs - Dot(Box<(Expr, Expr, Position)>), + Dot(Box), /// expr[expr] - Index(Box<(Expr, Expr, Position)>), + Index(Box), /// [ expr, ... ] Array(Box<(StaticVec, Position)>), /// #{ name:expr, ... } Map(Box<(StaticVec<((ImmutableString, Position), Expr)>, Position)>), /// lhs in rhs - In(Box<(Expr, Expr, Position)>), + In(Box), /// lhs && rhs - And(Box<(Expr, Expr, Position)>), + And(Box), /// lhs || rhs - Or(Box<(Expr, Expr, Position)>), + Or(Box), /// true True(Position), /// false @@ -1047,7 +1063,7 @@ pub enum Expr { /// () Unit(Position), /// Custom syntax - Custom(Box<(CustomExpr, Position)>), + Custom(Box), } impl Default for Expr { @@ -1154,13 +1170,13 @@ impl Expr { Self::FnCall(x) => (x.0).3, Self::Assignment(x) => x.0.position(), - Self::And(x) | Self::Or(x) | Self::In(x) => x.2, + Self::And(x) | Self::Or(x) | Self::In(x) => x.pos, Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos, - Self::Dot(x) | Self::Index(x) => x.0.position(), + Self::Dot(x) | Self::Index(x) => x.lhs.position(), - Self::Custom(x) => x.1, + Self::Custom(x) => x.pos, } } @@ -1184,16 +1200,11 @@ impl Expr { Self::Property(x) => x.1 = new_pos, Self::Stmt(x) => x.1 = new_pos, Self::FnCall(x) => (x.0).3 = new_pos, - Self::And(x) => x.2 = new_pos, - Self::Or(x) => x.2 = new_pos, - Self::In(x) => x.2 = new_pos, - Self::True(pos) => *pos = new_pos, - Self::False(pos) => *pos = new_pos, - Self::Unit(pos) => *pos = new_pos, + Self::And(x) | Self::Or(x) | Self::In(x) => x.pos = new_pos, + Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos, Self::Assignment(x) => x.3 = new_pos, - Self::Dot(x) => x.2 = new_pos, - Self::Index(x) => x.2 = new_pos, - Self::Custom(x) => x.1 = new_pos, + Self::Dot(x) | Self::Index(x) => x.pos = new_pos, + Self::Custom(x) => x.pos = new_pos, } self @@ -1209,8 +1220,7 @@ impl Expr { Self::Array(x) => x.0.iter().all(Self::is_pure), Self::Index(x) | Self::And(x) | Self::Or(x) | Self::In(x) => { - let (lhs, rhs, _) = x.as_ref(); - lhs.is_pure() && rhs.is_pure() + x.lhs.is_pure() && x.rhs.is_pure() } Self::Stmt(x) => x.0.is_pure(), @@ -1253,7 +1263,7 @@ impl Expr { Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_literal), // Check in expression - Self::In(x) => match (&x.0, &x.1) { + Self::In(x) => match (&x.lhs, &x.rhs) { (Self::StringConstant(_), Self::StringConstant(_)) | (Self::CharConstant(_), Self::StringConstant(_)) => true, _ => false, @@ -1286,7 +1296,7 @@ impl Expr { Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant), // Check in expression - Self::In(x) => match (&x.0, &x.1) { + Self::In(x) => match (&x.lhs, &x.rhs) { (Self::StringConstant(_), Self::StringConstant(_)) | (Self::CharConstant(_), Self::StringConstant(_)) => true, _ => false, @@ -1702,7 +1712,11 @@ fn parse_index_chain( let idx_expr = parse_index_chain(input, state, lib, idx_expr, settings.level_up())?; // Indexing binds to right - Ok(Expr::Index(Box::new((lhs, idx_expr, prev_pos)))) + Ok(Expr::Index(Box::new(BinaryExpr { + lhs, + rhs: idx_expr, + pos: prev_pos, + }))) } // Otherwise terminate the indexing chain _ => { @@ -1710,10 +1724,18 @@ fn parse_index_chain( // Terminate with an `Expr::Expr` wrapper to prevent the last index expression // inside brackets to be mis-parsed as another level of indexing, or a // dot expression/function call to be mis-parsed as following the indexing chain. - Expr::Index(_) | Expr::Dot(_) | Expr::FnCall(_) => Ok(Expr::Index( - Box::new((lhs, Expr::Expr(Box::new(idx_expr)), settings.pos)), - )), - _ => Ok(Expr::Index(Box::new((lhs, idx_expr, settings.pos)))), + Expr::Index(_) | Expr::Dot(_) | Expr::FnCall(_) => { + Ok(Expr::Index(Box::new(BinaryExpr { + lhs, + rhs: Expr::Expr(Box::new(idx_expr)), + pos: settings.pos, + }))) + } + _ => Ok(Expr::Index(Box::new(BinaryExpr { + lhs, + rhs: idx_expr, + pos: settings.pos, + }))), } } } @@ -2247,7 +2269,7 @@ fn make_assignment_stmt<'a>( } } // xxx[???] = rhs, xxx.??? = rhs - Expr::Index(x) | Expr::Dot(x) => match &x.0 { + Expr::Index(x) | Expr::Dot(x) => match &x.lhs { // var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs Expr::Variable(x) if x.3.is_none() => { Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos)))) @@ -2266,7 +2288,7 @@ fn make_assignment_stmt<'a>( } } // expr[???] = rhs, expr.??? = rhs - _ => Err(PERR::AssignmentToCopy.into_err(x.0.position())), + _ => Err(PERR::AssignmentToCopy.into_err(x.lhs.position())), }, // const_expr = rhs expr if expr.is_constant() => { @@ -2324,13 +2346,9 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { - let (idx_lhs, idx_expr, pos) = *x; - Expr::Index(Box::new(( - idx_lhs, - make_dot_expr(idx_expr, rhs, op_pos)?, - pos, - ))) + (Expr::Index(mut x), rhs) => { + x.rhs = make_dot_expr(x.rhs, rhs, op_pos)?; + Expr::Index(x) } // lhs.id (lhs, Expr::Variable(x)) if x.1.is_none() => { @@ -2340,31 +2358,47 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].1)); } // lhs.prop - (lhs, prop @ Expr::Property(_)) => Expr::Dot(Box::new((lhs, prop, op_pos))), + (lhs, prop @ Expr::Property(_)) => Expr::Dot(Box::new(BinaryExpr { + lhs, + rhs: prop, + pos: op_pos, + })), // lhs.dot_lhs.dot_rhs (lhs, Expr::Dot(x)) => { - let (dot_lhs, dot_rhs, pos) = *x; - Expr::Dot(Box::new(( + let rhs = Expr::Dot(Box::new(BinaryExpr { + lhs: x.lhs.into_property(), + rhs: x.rhs, + pos: x.pos, + })); + Expr::Dot(Box::new(BinaryExpr { lhs, - Expr::Dot(Box::new((dot_lhs.into_property(), dot_rhs, pos))), - op_pos, - ))) + rhs, + pos: op_pos, + })) } // lhs.idx_lhs[idx_rhs] (lhs, Expr::Index(x)) => { - let (dot_lhs, dot_rhs, pos) = *x; - Expr::Dot(Box::new(( + let rhs = Expr::Index(Box::new(BinaryExpr { + lhs: x.lhs.into_property(), + rhs: x.rhs, + pos: x.pos, + })); + Expr::Dot(Box::new(BinaryExpr { lhs, - Expr::Index(Box::new((dot_lhs.into_property(), dot_rhs, pos))), - op_pos, - ))) + rhs, + pos: op_pos, + })) } // lhs.Fn() or lhs.eval() (_, Expr::FnCall(x)) @@ -2385,7 +2419,11 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result Expr::Dot(Box::new((lhs, func, op_pos))), + (lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new(BinaryExpr { + lhs, + rhs: func, + pos: op_pos, + })), // lhs.rhs (_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())), }) @@ -2538,7 +2576,11 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), } - Ok(Expr::In(Box::new((lhs, rhs, op_pos)))) + Ok(Expr::In(Box::new(BinaryExpr { + lhs, + rhs, + pos: op_pos, + }))) } /// Parse a binary expression. @@ -2653,12 +2695,20 @@ fn parse_binary_op( Token::Or => { let rhs = args.pop().unwrap(); let current_lhs = args.pop().unwrap(); - Expr::Or(Box::new((current_lhs, rhs, pos))) + Expr::Or(Box::new(BinaryExpr { + lhs: current_lhs, + rhs, + pos, + })) } Token::And => { let rhs = args.pop().unwrap(); let current_lhs = args.pop().unwrap(); - Expr::And(Box::new((current_lhs, rhs, pos))) + Expr::And(Box::new(BinaryExpr { + lhs: current_lhs, + rhs, + pos, + })) } Token::In => { let rhs = args.pop().unwrap(); @@ -2764,10 +2814,11 @@ fn parse_custom_syntax( } } - Ok(Expr::Custom(Box::new(( - CustomExpr(exprs, syntax.func.clone()), + Ok(Expr::Custom(Box::new(CustomExpr { + keywords: exprs, + func: syntax.func.clone(), pos, - )))) + }))) } /// Parse an expression.