From 42eac410b7d49c5d42524d34d1def07060e1f435 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 31 Oct 2020 23:26:21 +0800 Subject: [PATCH] Optimize Expr. --- RELEASES.md | 3 +- src/ast.rs | 106 ++++++-------- src/engine.rs | 120 ++++++++------- src/optimize.rs | 89 ++++++------ src/parser.rs | 380 +++++++++++++++++++++++++----------------------- 5 files changed, 346 insertions(+), 352 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 38e49c8c..c849496b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,6 +6,7 @@ Version 0.19.4 ============== This version basically cleans up the code structure in preparation for a potential `1.0` release in the future. +Most scripts should see a material speed increase. This version also adds a low-level API for more flexibility when defining custom syntax. @@ -29,7 +30,7 @@ New features Enhancements ------------ -* AST data structures are optimized to maximize cache friendliness. This may have speed impacts on large, complex scripts (benchmarks wanted!). +* Essential AST structures like `Expr` and `Stmt` are packed into smaller sizes (16 bytes and 32 bytes on 64-bit), stored inline for more cache friendliness, and de-`Box`ed as much as possible. Version 0.19.3 diff --git a/src/ast.rs b/src/ast.rs index 5de058f4..4a82fb67 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -783,7 +783,6 @@ impl Stmt { pub struct CustomExpr { pub(crate) keywords: StaticVec, pub(crate) func: Shared, - pub(crate) pos: Position, } impl fmt::Debug for CustomExpr { @@ -811,11 +810,6 @@ impl CustomExpr { pub fn func(&self) -> &FnCustomSyntaxEval { self.func.as_ref() } - /// Get the position of this `CustomExpr`. - #[inline(always)] - pub fn position(&self) -> Position { - self.pos - } } /// _[INTERNALS]_ A type wrapping a floating-point number. @@ -862,8 +856,6 @@ pub struct BinaryExpr { pub lhs: Expr, /// RHS expression. pub rhs: Expr, - /// Position of the expression. - pub pos: Position, } /// A function call. @@ -887,8 +879,6 @@ pub struct FnCallInfo { /// Default value when the function is not found, mostly used to provide a default for comparison functions. /// Type is `bool` in order for `FnCallInfo` to be `Hash` pub def_value: Option, - /// Position of the function call. - pub pos: Position, } /// _[INTERNALS]_ An expression sub-tree. @@ -922,21 +912,21 @@ pub enum Expr { /// Wrapped expression - should not be optimized away. Expr(Box), /// func(expr, ... ) - FnCall(Box), + FnCall(Box, Position), /// lhs.rhs - Dot(Box), + Dot(Box, Position), /// expr[expr] - Index(Box), + Index(Box, Position), /// [ expr, ... ] - Array(Box<(StaticVec, Position)>), + Array(Box>, Position), /// #{ name:expr, ... } - Map(Box<(StaticVec<(IdentX, Expr)>, Position)>), + Map(Box>, Position), /// lhs in rhs - In(Box), + In(Box, Position), /// lhs && rhs - And(Box), + And(Box, Position), /// lhs || rhs - Or(Box), + Or(Box, Position), /// true True(Position), /// false @@ -944,7 +934,7 @@ pub enum Expr { /// () Unit(Position), /// Custom syntax - Custom(Box), + Custom(Box, Position), } impl Default for Expr { @@ -968,16 +958,16 @@ impl Expr { Self::CharConstant(_, _) => TypeId::of::(), Self::StringConstant(_) => TypeId::of::(), Self::FnPointer(_) => TypeId::of::(), - Self::True(_) | Self::False(_) | Self::In(_) | Self::And(_) | Self::Or(_) => { + Self::True(_) | Self::False(_) | Self::In(_, _) | Self::And(_, _) | Self::Or(_, _) => { TypeId::of::() } Self::Unit(_) => TypeId::of::<()>(), #[cfg(not(feature = "no_index"))] - Self::Array(_) => TypeId::of::(), + Self::Array(_, _) => TypeId::of::(), #[cfg(not(feature = "no_object"))] - Self::Map(_) => TypeId::of::(), + Self::Map(_, _) => TypeId::of::(), _ => return None, }) @@ -1004,16 +994,14 @@ impl Expr { Self::Unit(_) => ().into(), #[cfg(not(feature = "no_index"))] - Self::Array(x) if x.0.iter().all(Self::is_constant) => Dynamic(Union::Array(Box::new( - x.0.iter() - .map(|v| v.get_constant_value().unwrap()) - .collect(), - ))), + Self::Array(x, _) if x.iter().all(Self::is_constant) => Dynamic(Union::Array( + Box::new(x.iter().map(|v| v.get_constant_value().unwrap()).collect()), + )), #[cfg(not(feature = "no_object"))] - Self::Map(x) if x.0.iter().all(|(_, v)| v.is_constant()) => { + Self::Map(x, _) if x.iter().all(|(_, v)| v.is_constant()) => { Dynamic(Union::Map(Box::new( - x.0.iter() + x.iter() .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())) .collect(), ))) @@ -1043,20 +1031,20 @@ impl Expr { Self::CharConstant(_, pos) => *pos, Self::StringConstant(x) => x.pos, Self::FnPointer(x) => x.pos, - Self::Array(x) => x.1, - Self::Map(x) => x.1, + Self::Array(_, pos) => *pos, + Self::Map(_, pos) => *pos, Self::Property(x) => (x.0).pos, Self::Stmt(_, pos) => *pos, Self::Variable(x) => (x.0).pos, - Self::FnCall(x) => x.pos, + Self::FnCall(_, pos) => *pos, - Self::And(x) | Self::Or(x) | Self::In(x) => x.pos, + Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(), Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos, - Self::Dot(x) | Self::Index(x) => x.lhs.position(), + Self::Dot(x, _) | Self::Index(x, _) => x.lhs.position(), - Self::Custom(x) => x.pos, + Self::Custom(_, pos) => *pos, } } @@ -1074,16 +1062,16 @@ impl Expr { Self::CharConstant(_, pos) => *pos = new_pos, Self::StringConstant(x) => x.pos = new_pos, Self::FnPointer(x) => x.pos = new_pos, - Self::Array(x) => x.1 = new_pos, - Self::Map(x) => x.1 = new_pos, + Self::Array(_, pos) => *pos = new_pos, + Self::Map(_, pos) => *pos = new_pos, Self::Variable(x) => (x.0).pos = new_pos, Self::Property(x) => (x.0).pos = new_pos, Self::Stmt(_, pos) => *pos = new_pos, - Self::FnCall(x) => x.pos = new_pos, - Self::And(x) | Self::Or(x) | Self::In(x) => x.pos = new_pos, + Self::FnCall(_, pos) => *pos = new_pos, + Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos, Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos, - Self::Dot(x) | Self::Index(x) => x.pos = new_pos, - Self::Custom(x) => x.pos = new_pos, + Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos, + Self::Custom(_, pos) => *pos = new_pos, } self @@ -1096,9 +1084,11 @@ impl Expr { match self { Self::Expr(x) => x.is_pure(), - Self::Array(x) => x.0.iter().all(Self::is_pure), + Self::Array(x, _) => x.iter().all(Self::is_pure), - Self::Index(x) | Self::And(x) | Self::Or(x) | Self::In(x) => { + Self::Map(x, _) => x.iter().map(|(_, v)| v).all(Self::is_pure), + + Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => { x.lhs.is_pure() && x.rhs.is_pure() } @@ -1136,13 +1126,13 @@ impl Expr { | Self::Unit(_) => true, // An array literal is literal if all items are literals - Self::Array(x) => x.0.iter().all(Self::is_literal), + Self::Array(x, _) => x.iter().all(Self::is_literal), // An map literal is literal if all items are literals - Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_literal), + Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_literal), // Check in expression - Self::In(x) => match (&x.lhs, &x.rhs) { + Self::In(x, _) => match (&x.lhs, &x.rhs) { (Self::StringConstant(_), Self::StringConstant(_)) | (Self::CharConstant(_, _), Self::StringConstant(_)) => true, _ => false, @@ -1169,13 +1159,13 @@ impl Expr { | Self::Unit(_) => true, // An array literal is constant if all items are constant - Self::Array(x) => x.0.iter().all(Self::is_constant), + Self::Array(x, _) => x.iter().all(Self::is_constant), // An map literal is constant if all items are constant - Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant), + Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_constant), // Check in expression - Self::In(x) => match (&x.lhs, &x.rhs) { + Self::In(x, _) => match (&x.lhs, &x.rhs) { (Self::StringConstant(_), Self::StringConstant(_)) | (Self::CharConstant(_, _), Self::StringConstant(_)) => true, _ => false, @@ -1196,20 +1186,20 @@ impl Expr { Self::IntegerConstant(_, _) | Self::CharConstant(_, _) | Self::FnPointer(_) - | Self::In(_) - | Self::And(_) - | Self::Or(_) + | Self::In(_, _) + | Self::And(_, _) + | Self::Or(_, _) | Self::True(_) | Self::False(_) | Self::Unit(_) => false, Self::StringConstant(_) | Self::Stmt(_, _) - | Self::FnCall(_) - | Self::Dot(_) - | Self::Index(_) - | Self::Array(_) - | Self::Map(_) => match token { + | Self::FnCall(_, _) + | Self::Dot(_, _) + | Self::Index(_, _) + | Self::Array(_, _) + | Self::Map(_, _) => match token { #[cfg(not(feature = "no_index"))] Token::LeftBracket => true, _ => false, @@ -1231,7 +1221,7 @@ impl Expr { _ => false, }, - Self::Custom(_) => false, + Self::Custom(_, _) => false, } } diff --git a/src/engine.rs b/src/engine.rs index aaad7178..b08164ba 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -879,8 +879,8 @@ impl Engine { let is_ref = target.is_ref(); let next_chain = match rhs { - Expr::Index(_) => ChainType::Index, - Expr::Dot(_) => ChainType::Dot, + Expr::Index(_, _) => ChainType::Index, + Expr::Dot(_, _) => ChainType::Dot, _ => ChainType::None, }; @@ -894,7 +894,7 @@ impl Engine { match rhs { // xxx[idx].expr... | xxx[idx][expr]... - Expr::Dot(x) | Expr::Index(x) => { + Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => { let idx_pos = x.lhs.position(); let idx_val = idx_val.as_value(); let obj_ptr = &mut self.get_indexed_mut( @@ -905,7 +905,7 @@ impl Engine { state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain, level, new_val, ) - .map_err(|err| err.fill_position(x.pos)) + .map_err(|err| err.fill_position(*x_pos)) } // xxx[rhs] = new_val _ if new_val.is_some() => { @@ -968,11 +968,10 @@ impl Engine { ChainType::Dot => { match rhs { // xxx.fn_name(arg_expr_list) - Expr::FnCall(x) if x.namespace.is_none() => { + Expr::FnCall(x, pos) if x.namespace.is_none() => { let FnCallInfo { name, native_only: native, - pos, hash, def_value, .. @@ -986,7 +985,7 @@ impl Engine { .map_err(|err| err.fill_position(*pos)) } // xxx.module::fn_name(...) - syntax error - Expr::FnCall(_) => unreachable!(), + Expr::FnCall(_, _) => unreachable!(), // {xxx:map}.id = ??? Expr::Property(x) if target.is::() && new_val.is_some() => { let IdentX { name, pos } = &x.0; @@ -1031,7 +1030,7 @@ impl Engine { .map_err(|err| err.fill_position(*pos)) } // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr - Expr::Index(x) | Expr::Dot(x) if target.is::() => { + Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::() => { let mut val = match &x.lhs { Expr::Property(p) => { let IdentX { name, pos } = &p.0; @@ -1041,11 +1040,10 @@ impl Engine { )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr - Expr::FnCall(x) if x.namespace.is_none() => { + Expr::FnCall(x, pos) if x.namespace.is_none() => { let FnCallInfo { name, native_only: native, - pos, hash, def_value, .. @@ -1061,7 +1059,7 @@ impl Engine { val.into() } // {xxx:map}.module::fn_name(...) - syntax error - Expr::FnCall(_) => unreachable!(), + Expr::FnCall(_, _) => unreachable!(), // Others - syntax error _ => unreachable!(), }; @@ -1070,10 +1068,10 @@ impl Engine { state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, level, new_val, ) - .map_err(|err| err.fill_position(x.pos)) + .map_err(|err| err.fill_position(*x_pos)) } // xxx.sub_lhs[expr] | xxx.sub_lhs.expr - Expr::Index(x) | Expr::Dot(x) => { + Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) => { match &x.lhs { // xxx.prop[expr] | xxx.prop.expr Expr::Property(p) => { @@ -1101,7 +1099,7 @@ impl Engine { level, new_val, ) - .map_err(|err| err.fill_position(x.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 { @@ -1117,7 +1115,7 @@ impl Engine { EvalAltResult::ErrorDotExpr(_, _) => { Ok(Default::default()) } - _ => Err(err.fill_position(x.pos)), + _ => Err(err.fill_position(*x_pos)), }, )?; } @@ -1125,11 +1123,10 @@ impl Engine { Ok((result, may_be_changed)) } // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr - Expr::FnCall(f) if f.namespace.is_none() => { + Expr::FnCall(f, pos) if f.namespace.is_none() => { let FnCallInfo { name, native_only: native, - pos, hash, def_value, .. @@ -1152,7 +1149,7 @@ impl Engine { .map_err(|err| err.fill_position(*pos)) } // xxx.module::fn_name(...) - syntax error - Expr::FnCall(_) => unreachable!(), + Expr::FnCall(_, _) => unreachable!(), // Others - syntax error _ => unreachable!(), } @@ -1183,12 +1180,12 @@ impl Engine { BinaryExpr { lhs: dot_lhs, rhs: dot_rhs, - pos: op_pos, }, chain_type, + op_pos, ) = match expr { - Expr::Index(x) => (x.as_ref(), ChainType::Index), - Expr::Dot(x) => (x.as_ref(), ChainType::Dot), + Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos), + Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos), _ => unreachable!(), }; @@ -1235,7 +1232,7 @@ impl Engine { state, lib, &mut None, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val, ) .map(|(v, _)| v) - .map_err(|err| err.fill_position(*op_pos)) + .map_err(|err| err.fill_position(op_pos)) } // {expr}.??? = ??? or {expr}[???] = ??? _ if new_val.is_some() => unreachable!(), @@ -1247,7 +1244,7 @@ impl Engine { state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val, ) .map(|(v, _)| v) - .map_err(|err| err.fill_position(*op_pos)) + .map_err(|err| err.fill_position(op_pos)) } } } @@ -1272,7 +1269,7 @@ impl Engine { .map_err(|err| err.fill_position(expr.position()))?; match expr { - Expr::FnCall(x) if x.namespace.is_none() => { + Expr::FnCall(x, _) if x.namespace.is_none() => { let arg_values = x .args .iter() @@ -1283,23 +1280,24 @@ impl Engine { idx_values.push(arg_values.into()); } - Expr::FnCall(_) => unreachable!(), + Expr::FnCall(_, _) => unreachable!(), Expr::Property(_) => idx_values.push(IndexChainValue::None), - Expr::Index(x) | Expr::Dot(x) => { + Expr::Index(x, _) | Expr::Dot(x, _) => { let BinaryExpr { lhs, rhs, .. } = x.as_ref(); // Evaluate in left-to-right order let lhs_val = match lhs { Expr::Property(_) => IndexChainValue::None, - Expr::FnCall(x) if chain_type == ChainType::Dot && x.namespace.is_none() => x - .args - .iter() - .map(|arg_expr| { - self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) - }) - .collect::, _>>()? - .into(), - Expr::FnCall(_) => unreachable!(), + Expr::FnCall(x, _) if chain_type == ChainType::Dot && x.namespace.is_none() => { + x.args + .iter() + .map(|arg_expr| { + self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) + }) + .collect::, _>>()? + .into() + } + Expr::FnCall(_, _) => unreachable!(), _ => self .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)? .into(), @@ -1307,8 +1305,8 @@ impl Engine { // Push in reverse order let chain_type = match expr { - Expr::Index(_) => ChainType::Index, - Expr::Dot(_) => ChainType::Dot, + Expr::Index(_, _) => ChainType::Index, + Expr::Dot(_, _) => ChainType::Dot, _ => unreachable!(), }; self.eval_indexed_chain( @@ -1456,7 +1454,7 @@ impl Engine { match rhs_value { #[cfg(not(feature = "no_index"))] Dynamic(Union::Array(mut rhs_value)) => { - let op = "=="; + const OP_FUNC: &str = "=="; // Call the `==` operator to compare each value let def_value = Some(false.into()); @@ -1465,10 +1463,11 @@ impl Engine { let args = &mut [&mut lhs_value.clone(), value]; // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. - let hash = calc_native_fn_hash(empty(), op, args.iter().map(|a| a.type_id())); + let hash = + calc_native_fn_hash(empty(), OP_FUNC, args.iter().map(|a| a.type_id())); if self - .call_native_fn(state, lib, op, hash, args, false, false, &def_value) + .call_native_fn(state, lib, OP_FUNC, hash, args, false, false, &def_value) .map_err(|err| err.fill_position(rhs.position()))? .0 .as_bool() @@ -1482,13 +1481,13 @@ impl Engine { } #[cfg(not(feature = "no_object"))] Dynamic(Union::Map(rhs_value)) => match lhs_value { - // Only allows String or char + // Only allows string or char Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(&s).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()), _ => EvalAltResult::ErrorInExpr(lhs.position()).into(), }, Dynamic(Union::Str(rhs_value)) => match lhs_value { - // Only allows String or char + // Only allows string or char Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()), _ => EvalAltResult::ErrorInExpr(lhs.position()).into(), @@ -1512,12 +1511,12 @@ impl Engine { .map_err(|err| err.fill_position(expr.position()))?; let result = match expr { - Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x.as_ref(), level), + Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x, level), Expr::IntegerConstant(x, _) => Ok((*x).into()), #[cfg(not(feature = "no_float"))] Expr::FloatConstant(x, _) => Ok(x.0.into()), - Expr::StringConstant(x) => Ok(x.name.to_string().into()), + Expr::StringConstant(x) => Ok(x.name.clone().into()), Expr::CharConstant(x, _) => Ok((*x).into()), Expr::FnPointer(x) => { Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into()) @@ -1541,26 +1540,26 @@ impl Engine { // lhs[idx_expr] #[cfg(not(feature = "no_index"))] - Expr::Index(_) => { + Expr::Index(_, _) => { self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) } // lhs.dot_rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(_) => { + Expr::Dot(_, _) => { self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) } #[cfg(not(feature = "no_index"))] - Expr::Array(x) => Ok(Dynamic(Union::Array(Box::new( - x.0.iter() + Expr::Array(x, _) => Ok(Dynamic(Union::Array(Box::new( + x.iter() .map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level)) .collect::, _>>()?, )))), #[cfg(not(feature = "no_object"))] - Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new( - x.0.iter() + Expr::Map(x, _) => Ok(Dynamic(Union::Map(Box::new( + x.iter() .map(|(key, expr)| { self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) .map(|val| (key.name.clone(), val)) @@ -1569,12 +1568,11 @@ impl Engine { )))), // Normal function call - Expr::FnCall(x) if x.namespace.is_none() => { + Expr::FnCall(x, pos) if x.namespace.is_none() => { let FnCallInfo { name, native_only: native, capture: cap_scope, - pos, hash, args, def_value, @@ -1589,10 +1587,9 @@ impl Engine { } // Module-qualified function call - Expr::FnCall(x) if x.namespace.is_some() => { + Expr::FnCall(x, pos) if x.namespace.is_some() => { let FnCallInfo { name, - pos, namespace, hash, args, @@ -1606,11 +1603,11 @@ impl Engine { .map_err(|err| err.fill_position(*pos)) } - Expr::In(x) => { + Expr::In(x, _) => { self.eval_in_expr(scope, mods, state, lib, this_ptr, &x.lhs, &x.rhs, level) } - Expr::And(x) => { + Expr::And(x, _) => { Ok((self .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .as_bool() @@ -1623,7 +1620,7 @@ impl Engine { .into()) } - Expr::Or(x) => { + Expr::Or(x, _) => { Ok((self .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .as_bool() @@ -1640,8 +1637,7 @@ impl Engine { Expr::False(_) => Ok(false.into()), Expr::Unit(_) => Ok(().into()), - Expr::Custom(custom) => { - let func = custom.func(); + Expr::Custom(custom, _) => { let expressions = custom .keywords() .iter() @@ -1656,7 +1652,7 @@ impl Engine { this_ptr, level, }; - func(&mut context, &expressions) + (custom.func)(&mut context, &expressions) } _ => unreachable!(), @@ -1825,7 +1821,7 @@ impl Engine { Expr::Variable(_) => unreachable!(), // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(_) => { + Expr::Index(_, _) => { self.eval_dot_index_chain( scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; @@ -1833,7 +1829,7 @@ impl Engine { } // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(_) => { + Expr::Dot(_, _) => { self.eval_dot_index_chain( scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; diff --git a/src/optimize.rs b/src/optimize.rs index 22787524..e482c805 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -451,15 +451,14 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { // lhs.rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(x) => match (x.lhs, x.rhs) { + Expr::Dot(x, dot_pos) => match (x.lhs, x.rhs) { // map.string - (Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => { + (Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => { let prop = &p.0.name; // Map literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - let pos = m.1; - m.0.into_iter().find(|(x, _)| &x.name == prop) + m.into_iter().find(|(x, _)| &x.name == prop) .map(|(_, mut expr)| { expr.set_position(pos); expr }) .unwrap_or_else(|| Expr::Unit(pos)) } @@ -467,31 +466,29 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { (lhs, rhs) => Expr::Dot(Box::new(BinaryExpr { lhs: optimize_expr(lhs, state), rhs: optimize_expr(rhs, state), - pos: x.pos - })) + }), dot_pos) } // lhs[rhs] #[cfg(not(feature = "no_index"))] - Expr::Index(x) => match (x.lhs, x.rhs) { + Expr::Index(x, idx_pos) => match (x.lhs, x.rhs) { // array[int] - (Expr::Array(mut a), Expr::IntegerConstant(i, _)) - if i >= 0 && (i as usize) < a.0.len() && a.0.iter().all(Expr::is_pure) => + (Expr::Array(mut a, pos), Expr::IntegerConstant(i, _)) + if i >= 0 && (i as usize) < a.len() && a.iter().all(Expr::is_pure) => { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - let mut expr = a.0.remove(i as usize); - expr.set_position(a.1); + let mut expr = a.remove(i as usize); + expr.set_position(pos); expr } // map[string] - (Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => { + (Expr::Map(m, pos), Expr::StringConstant(s)) if m.iter().all(|(_, x)| x.is_pure()) => { // Map literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - let pos = m.1; - m.0.into_iter().find(|(x, _)| x.name == s.name) + m.into_iter().find(|(x, _)| x.name == s.name) .map(|(_, mut expr)| { expr.set_position(pos); expr }) .unwrap_or_else(|| Expr::Unit(pos)) } @@ -505,21 +502,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { (lhs, rhs) => Expr::Index(Box::new(BinaryExpr { lhs: optimize_expr(lhs, state), rhs: optimize_expr(rhs, state), - pos: x.pos - })), + }), idx_pos), }, // [ items .. ] #[cfg(not(feature = "no_index"))] - Expr::Array(a) => Expr::Array(Box::new((a.0 + Expr::Array(a, pos) => Expr::Array(Box::new(a .into_iter().map(|expr| optimize_expr(expr, state)) - .collect(), a.1))), + .collect()), pos), // [ items .. ] #[cfg(not(feature = "no_object"))] - Expr::Map(m) => Expr::Map(Box::new((m.0 - .into_iter().map(|(key, expr)| (key, optimize_expr(expr, state))) - .collect(), m.1))), + Expr::Map(m, pos) => Expr::Map(Box::new(m + .into_iter().map(|(key, expr)| (key, optimize_expr(expr, state))) + .collect()), pos), // lhs in rhs - Expr::In(x) => match (x.lhs, x.rhs) { + Expr::In(x, in_pos) => match (x.lhs, x.rhs) { // "xxx" in "xxxxx" (Expr::StringConstant(a), Expr::StringConstant(b)) => { state.set_dirty(); @@ -531,20 +527,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { if b.name.contains(a) { Expr::True(pos) } else { Expr::False(pos) } } // "xxx" in #{...} - (Expr::StringConstant(a), Expr::Map(b)) => { + (Expr::StringConstant(a), Expr::Map(b, _)) => { state.set_dirty(); - if b.0.iter().find(|(x, _)| x.name == a.name).is_some() { + if b.iter().find(|(x, _)| x.name == a.name).is_some() { Expr::True(a.pos) } else { Expr::False(a.pos) } } // 'x' in #{...} - (Expr::CharConstant(a, pos), Expr::Map(b)) => { + (Expr::CharConstant(a, pos), Expr::Map(b, _)) => { state.set_dirty(); let ch = a.to_string(); - if b.0.iter().find(|(x, _)| x.name == &ch).is_some() { + if b.iter().find(|(x, _)| x.name == &ch).is_some() { Expr::True(pos) } else { Expr::False(pos) @@ -554,11 +550,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { (lhs, rhs) => Expr::In(Box::new(BinaryExpr { lhs: optimize_expr(lhs, state), rhs: optimize_expr(rhs, state), - pos: x.pos - })), + }), in_pos), }, // lhs && rhs - Expr::And(x) => match (x.lhs, x.rhs) { + Expr::And(x, and_pos) => match (x.lhs, x.rhs) { // true && rhs -> rhs (Expr::True(_), rhs) => { state.set_dirty(); @@ -578,11 +573,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { (lhs, rhs) => Expr::And(Box::new(BinaryExpr { lhs: optimize_expr(lhs, state), rhs: optimize_expr(rhs, state), - pos: x.pos - })), + }), and_pos), }, // lhs || rhs - Expr::Or(x) => match (x.lhs, x.rhs) { + Expr::Or(x, or_pos) => match (x.lhs, x.rhs) { // false || rhs -> rhs (Expr::False(_), rhs) => { state.set_dirty(); @@ -602,25 +596,24 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { (lhs, rhs) => Expr::Or(Box::new(BinaryExpr { lhs: optimize_expr(lhs, state), rhs: optimize_expr(rhs, state), - pos: x.pos - })), + }), or_pos), }, // Do not call some special keywords - Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => { + Expr::FnCall(mut x, pos) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => { x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + Expr::FnCall(x, pos) } // Call built-in operators - Expr::FnCall(mut x) + Expr::FnCall(mut x, pos) if x.namespace.is_none() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations && x.args.len() == 2 // binary call && x.args.iter().all(Expr::is_constant) // all arguments are constants && !is_valid_identifier(x.name.chars()) // cannot be scripted => { - let FnCallInfo { name, pos, args, .. } = x.as_mut(); + let FnCallInfo { name, args, .. } = x.as_mut(); let arg_values: StaticVec<_> = args.iter().map(|e| e.get_constant_value().unwrap()).collect(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); @@ -629,7 +622,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { if !state.engine.has_override_by_name_and_arguments(state.lib, name, arg_types.as_ref(), false) { if let Some(expr) = run_builtin_binary_op(name, &arg_values[0], &arg_values[1]) .ok().flatten() - .and_then(|result| map_dynamic_to_expr(result, *pos)) + .and_then(|result| map_dynamic_to_expr(result, pos)) { state.set_dirty(); return expr; @@ -637,16 +630,16 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + Expr::FnCall(x, pos) } // Eagerly call functions - Expr::FnCall(mut x) + Expr::FnCall(mut x, pos) if x.namespace.is_none() // Non-qualified && state.optimization_level == OptimizationLevel::Full // full optimizations && x.args.iter().all(Expr::is_constant) // all arguments are constants => { - let FnCallInfo { name, pos, args, def_value, .. } = x.as_mut(); + let FnCallInfo { name, args, def_value, .. } = x.as_mut(); // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] @@ -675,7 +668,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { def_value.map(|v| v.into()) } }) - .and_then(|result| map_dynamic_to_expr(result, *pos)) + .and_then(|result| map_dynamic_to_expr(result, pos)) { state.set_dirty(); return expr; @@ -683,13 +676,13 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + Expr::FnCall(x, pos) } // id(args ..) -> optimize function call arguments - Expr::FnCall(mut x) => { + Expr::FnCall(mut x, pos) => { x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + Expr::FnCall(x, pos) } // constant-name @@ -703,10 +696,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } // Custom syntax - Expr::Custom(x) => Expr::Custom(Box::new(CustomExpr { + Expr::Custom(x, pos) => Expr::Custom(Box::new(CustomExpr { keywords: x.keywords.into_iter().map(|expr| optimize_expr(expr, state)).collect(), ..*x - })), + }), pos), // All other expressions - skip expr => expr, diff --git a/src/parser.rs b/src/parser.rs index 89e49d2b..ebdbe625 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -307,15 +307,17 @@ fn parse_fn_call( calc_script_fn_hash(empty(), &id, 0) }; - return Ok(Expr::FnCall(Box::new(FnCallInfo { - name: id.into(), - capture, - namespace, - hash: hash_script, - args, - pos: settings.pos, - ..Default::default() - }))); + return Ok(Expr::FnCall( + Box::new(FnCallInfo { + name: id.into(), + capture, + namespace, + hash: hash_script, + args, + ..Default::default() + }), + settings.pos, + )); } // id... _ => (), @@ -352,15 +354,17 @@ fn parse_fn_call( calc_script_fn_hash(empty(), &id, args.len()) }; - return Ok(Expr::FnCall(Box::new(FnCallInfo { - name: id.into(), - capture, - namespace, - hash: hash_script, - args, - pos: settings.pos, - ..Default::default() - }))); + return Ok(Expr::FnCall( + Box::new(FnCallInfo { + name: id.into(), + capture, + namespace, + hash: hash_script, + args, + ..Default::default() + }), + settings.pos, + )); } // id(...args, (Token::Comma, _) => { @@ -414,9 +418,9 @@ fn parse_index_chain( .into_err(*pos)) } Expr::IntegerConstant(_, pos) => match lhs { - Expr::Array(_) | Expr::StringConstant(_) => (), + Expr::Array(_, _) | Expr::StringConstant(_) => (), - Expr::Map(_) => { + Expr::Map(_, _) => { return Err(PERR::MalformedIndexExpr( "Object map access expects string index, not a number".into(), ) @@ -432,9 +436,9 @@ fn parse_index_chain( } Expr::CharConstant(_, _) - | Expr::And(_) - | Expr::Or(_) - | Expr::In(_) + | Expr::And(_, _) + | Expr::Or(_, _) + | Expr::In(_, _) | Expr::True(_) | Expr::False(_) | Expr::Unit(_) => { @@ -449,9 +453,9 @@ fn parse_index_chain( // lhs[string] Expr::StringConstant(x) => match lhs { - Expr::Map(_) => (), + Expr::Map(_, _) => (), - Expr::Array(_) | Expr::StringConstant(_) => { + Expr::Array(_, _) | Expr::StringConstant(_) => { return Err(PERR::MalformedIndexExpr( "Array or string expects numeric index, not a string".into(), ) @@ -467,9 +471,9 @@ fn parse_index_chain( } Expr::CharConstant(_, _) - | Expr::And(_) - | Expr::Or(_) - | Expr::In(_) + | Expr::And(_, _) + | Expr::Or(_, _) + | Expr::In(_, _) | Expr::True(_) | Expr::False(_) | Expr::Unit(_) => { @@ -505,7 +509,7 @@ fn parse_index_chain( .into_err(x.position())) } // lhs[??? && ???], lhs[??? || ???], lhs[??? in ???] - x @ Expr::And(_) | x @ Expr::Or(_) | x @ Expr::In(_) => { + x @ Expr::And(_, _) | x @ Expr::Or(_, _) | x @ Expr::In(_, _) => { return Err(PERR::MalformedIndexExpr( "Array access expects integer index, not a boolean".into(), ) @@ -537,11 +541,10 @@ 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(BinaryExpr { - lhs, - rhs: idx_expr, - pos: prev_pos, - }))) + Ok(Expr::Index( + Box::new(BinaryExpr { lhs, rhs: idx_expr }), + prev_pos, + )) } // Otherwise terminate the indexing chain _ => { @@ -549,18 +552,19 @@ 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(BinaryExpr { - lhs, - rhs: Expr::Expr(Box::new(idx_expr)), - pos: settings.pos, - }))) + Expr::Index(_, _) | Expr::Dot(_, _) | Expr::FnCall(_, _) => { + Ok(Expr::Index( + Box::new(BinaryExpr { + lhs, + rhs: Expr::Expr(Box::new(idx_expr)), + }), + settings.pos, + )) } - _ => Ok(Expr::Index(Box::new(BinaryExpr { - lhs, - rhs: idx_expr, - pos: settings.pos, - }))), + _ => Ok(Expr::Index( + Box::new(BinaryExpr { lhs, rhs: idx_expr }), + settings.pos, + )), } } } @@ -631,7 +635,7 @@ fn parse_array_literal( }; } - Ok(Expr::Array(Box::new((arr, settings.pos)))) + Ok(Expr::Array(Box::new(arr), settings.pos)) } /// Parse a map literal. @@ -740,7 +744,7 @@ fn parse_map_literal( }) .map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?; - Ok(Expr::Map(Box::new((map, settings.pos)))) + Ok(Expr::Map(Box::new(map), settings.pos)) } /// Parse a primary expression. @@ -984,15 +988,17 @@ fn parse_unary( let mut args = StaticVec::new(); args.push(expr); - Ok(Expr::FnCall(Box::new(FnCallInfo { - name: op.into(), - native_only: true, - namespace: None, - hash, - args, + Ok(Expr::FnCall( + Box::new(FnCallInfo { + name: op.into(), + native_only: true, + namespace: None, + hash, + args, + ..Default::default() + }), pos, - ..Default::default() - }))) + )) } } } @@ -1011,15 +1017,17 @@ fn parse_unary( let op = "!"; let hash = calc_script_fn_hash(empty(), op, 1); - Ok(Expr::FnCall(Box::new(FnCallInfo { - name: op.into(), - native_only: true, - hash, - args, - def_value: Some(false), // NOT operator, when operating on invalid operand, defaults to false + Ok(Expr::FnCall( + Box::new(FnCallInfo { + name: op.into(), + native_only: true, + hash, + args, + def_value: Some(false), // NOT operator, when operating on invalid operand, defaults to false + ..Default::default() + }), pos, - ..Default::default() - }))) + )) } // | ... #[cfg(not(feature = "no_function"))] @@ -1098,7 +1106,7 @@ fn make_assignment_stmt<'a>( } } // xxx[???] = rhs, xxx.??? = rhs - Expr::Index(x) | Expr::Dot(x) => match &x.lhs { + 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(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) @@ -1132,7 +1140,7 @@ fn make_assignment_stmt<'a>( Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position())) } // ??? && ??? = rhs, ??? || ??? = rhs - Expr::And(_) | Expr::Or(_) => { + Expr::And(_, _) | Expr::Or(_, _) => { Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos)) } // expr = rhs @@ -1183,9 +1191,9 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + (Expr::Index(mut x, pos), rhs) => { x.rhs = make_dot_expr(x.rhs, rhs, op_pos)?; - Expr::Index(x) + Expr::Index(x, pos) } // lhs.id (lhs, Expr::Variable(x)) if x.1.is_none() => { @@ -1194,71 +1202,59 @@ 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(BinaryExpr { - lhs, - rhs: prop, - pos: op_pos, - })), + (lhs, prop @ Expr::Property(_)) => { + Expr::Dot(Box::new(BinaryExpr { lhs, rhs: prop }), op_pos) + } // lhs.dot_lhs.dot_rhs - (lhs, Expr::Dot(x)) => { - let rhs = Expr::Dot(Box::new(BinaryExpr { - lhs: x.lhs.into_property(), - rhs: x.rhs, - pos: x.pos, - })); - Expr::Dot(Box::new(BinaryExpr { - lhs, - rhs, - pos: op_pos, - })) + (lhs, Expr::Dot(x, pos)) => { + let rhs = Expr::Dot( + Box::new(BinaryExpr { + lhs: x.lhs.into_property(), + rhs: x.rhs, + }), + pos, + ); + Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) } // lhs.idx_lhs[idx_rhs] - (lhs, Expr::Index(x)) => { - let rhs = Expr::Index(Box::new(BinaryExpr { - lhs: x.lhs.into_property(), - rhs: x.rhs, - pos: x.pos, - })); - Expr::Dot(Box::new(BinaryExpr { - lhs, - rhs, - pos: op_pos, - })) + (lhs, Expr::Index(x, pos)) => { + let rhs = Expr::Index( + Box::new(BinaryExpr { + lhs: x.lhs.into_property(), + rhs: x.rhs, + }), + pos, + ); + Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) } // lhs.Fn() or lhs.eval() - (_, Expr::FnCall(x)) + (_, Expr::FnCall(x, pos)) if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) => { return Err(PERR::BadInput(format!( "'{}' should not be called in method style. Try {}(...);", x.name, x.name )) - .into_err(x.pos)); + .into_err(pos)); } // lhs.func!(...) - (_, Expr::FnCall(x)) if x.capture => { + (_, Expr::FnCall(x, pos)) if x.capture => { return Err(PERR::MalformedCapture( "method-call style does not support capturing".into(), ) - .into_err(x.pos)); + .into_err(pos)); } // lhs.func(...) - (lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new(BinaryExpr { - lhs, - rhs: func, - pos: op_pos, - })), + (lhs, func @ Expr::FnCall(_, _)) => { + Expr::Dot(Box::new(BinaryExpr { lhs, rhs: func }), op_pos) + } // lhs.rhs (_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())), }) @@ -1268,9 +1264,9 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result Result { match (&lhs, &rhs) { (_, x @ Expr::IntegerConstant(_, _)) - | (_, x @ Expr::And(_)) - | (_, x @ Expr::Or(_)) - | (_, x @ Expr::In(_)) + | (_, x @ Expr::And(_, _)) + | (_, x @ Expr::Or(_, _)) + | (_, x @ Expr::In(_, _)) | (_, x @ Expr::True(_)) | (_, x @ Expr::False(_)) | (_, x @ Expr::Unit(_)) => { @@ -1309,9 +1305,9 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( @@ -1320,14 +1316,14 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + (x @ Expr::Array(_, _), Expr::StringConstant(_)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not an array".into(), ) .into_err(x.position())) } // #{...} in "xxxx" - (x @ Expr::Map(_), Expr::StringConstant(_)) => { + (x @ Expr::Map(_, _), Expr::StringConstant(_)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not an object map".into(), ) @@ -1342,18 +1338,19 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), + (Expr::StringConstant(_), Expr::Map(_, _)) + | (Expr::CharConstant(_, _), Expr::Map(_, _)) => (), // 123.456 in #{...} #[cfg(not(feature = "no_float"))] - (x @ Expr::FloatConstant(_, _), Expr::Map(_)) => { + (x @ Expr::FloatConstant(_, _), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not a float".into(), ) .into_err(x.position())) } // 123 in #{...} - (x @ Expr::IntegerConstant(_, _), Expr::Map(_)) => { + (x @ Expr::IntegerConstant(_, _), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not a number".into(), ) @@ -1361,32 +1358,32 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + (x @ Expr::And(_, _), Expr::Map(_, _)) + | (x @ Expr::Or(_, _), Expr::Map(_, _)) + | (x @ Expr::In(_, _), Expr::Map(_, _)) + | (x @ Expr::True(_), Expr::Map(_, _)) + | (x @ Expr::False(_), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not a boolean".into(), ) .into_err(x.position())) } // [???, ???, ???] in #{..} - (x @ Expr::Array(_), Expr::Map(_)) => { + (x @ Expr::Array(_, _), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not an array".into(), ) .into_err(x.position())) } // #{...} in #{..} - (x @ Expr::Map(_), Expr::Map(_)) => { + (x @ Expr::Map(_, _), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not an object map".into(), ) .into_err(x.position())) } // () in #{...} - (x @ Expr::Unit(_), Expr::Map(_)) => { + (x @ Expr::Unit(_), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for an object map expects a string, not ()".into(), ) @@ -1396,11 +1393,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), } - Ok(Expr::In(Box::new(BinaryExpr { - lhs, - rhs, - pos: op_pos, - }))) + Ok(Expr::In(Box::new(BinaryExpr { lhs, rhs }), op_pos)) } /// Parse a binary expression. @@ -1488,7 +1481,6 @@ fn parse_binary_op( name: op, native_only: true, capture: false, - pos, ..Default::default() }; @@ -1507,49 +1499,62 @@ fn parse_binary_op( | Token::PowerOf | Token::Ampersand | Token::Pipe - | Token::XOr => Expr::FnCall(Box::new(FnCallInfo { - hash, - args, - ..op_base - })), + | Token::XOr => Expr::FnCall( + Box::new(FnCallInfo { + hash, + args, + ..op_base + }), + pos, + ), // '!=' defaults to true when passed invalid operands - Token::NotEqualsTo => Expr::FnCall(Box::new(FnCallInfo { - hash, - args, - def_value: Some(true), - ..op_base - })), + Token::NotEqualsTo => Expr::FnCall( + Box::new(FnCallInfo { + hash, + args, + def_value: Some(true), + ..op_base + }), + pos, + ), // Comparison operators default to false when passed invalid operands Token::EqualsTo | Token::LessThan | Token::LessThanEqualsTo | Token::GreaterThan - | Token::GreaterThanEqualsTo => Expr::FnCall(Box::new(FnCallInfo { - hash, - args, - def_value: cmp_def, - ..op_base - })), + | Token::GreaterThanEqualsTo => Expr::FnCall( + Box::new(FnCallInfo { + hash, + args, + def_value: cmp_def, + ..op_base + }), + pos, + ), Token::Or => { let rhs = args.pop().unwrap(); let current_lhs = args.pop().unwrap(); - Expr::Or(Box::new(BinaryExpr { - lhs: current_lhs, - rhs, + 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(BinaryExpr { - lhs: current_lhs, - rhs, + Expr::And( + Box::new(BinaryExpr { + lhs: current_lhs, + rhs, + }), pos, - })) + ) } Token::In => { let rhs = args.pop().unwrap(); @@ -1566,12 +1571,15 @@ fn parse_binary_op( Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => { // Accept non-native functions for custom operators - Expr::FnCall(Box::new(FnCallInfo { - hash, - args, - native_only: false, - ..op_base - })) + Expr::FnCall( + Box::new(FnCallInfo { + hash, + args, + native_only: false, + ..op_base + }), + pos, + ) } op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)), @@ -1664,11 +1672,13 @@ fn parse_custom_syntax( } } - Ok(Expr::Custom(Box::new(CustomExpr { - keywords: exprs, - func: syntax.func.clone(), + Ok(Expr::Custom( + Box::new(CustomExpr { + keywords: exprs, + func: syntax.func.clone(), + }), pos, - }))) + )) } /// Parse an expression. @@ -2492,13 +2502,15 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1); - let expr = Expr::FnCall(Box::new(FnCallInfo { - name: KEYWORD_FN_PTR_CURRY.into(), - hash, - args, + let expr = Expr::FnCall( + Box::new(FnCallInfo { + name: KEYWORD_FN_PTR_CURRY.into(), + hash, + args, + ..Default::default() + }), pos, - ..Default::default() - })); + ); // If there are captured variables, convert the entire expression into a statement block, // then insert the relevant `Share` statements. @@ -2799,10 +2811,10 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { .collect(); if items.iter().all(Option::is_some) { - Some(Expr::Array(Box::new(( - items.into_iter().map(Option::unwrap).collect(), + Some(Expr::Array( + Box::new(items.into_iter().map(Option::unwrap).collect()), pos, - )))) + )) } else { None } @@ -2815,13 +2827,15 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { .collect(); if items.iter().all(|(_, expr)| expr.is_some()) { - Some(Expr::Map(Box::new(( - items - .into_iter() - .map(|(k, expr)| (k, expr.unwrap())) - .collect(), + Some(Expr::Map( + Box::new( + items + .into_iter() + .map(|(k, expr)| (k, expr.unwrap())) + .collect(), + ), pos, - )))) + )) } else { None }