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 c9d0d96c..4a82fb67 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -559,6 +559,15 @@ pub struct IdentX { pub pos: Position, } +impl From for IdentX { + fn from(value: Ident) -> Self { + Self { + name: value.name.into(), + pos: value.pos, + } + } +} + impl IdentX { /// Create a new `Identifier`. pub fn new(name: impl Into, pos: Position) -> Self { @@ -626,7 +635,7 @@ pub enum Stmt { Export(Vec<(Ident, Option)>, Position), /// Convert a variable to shared. #[cfg(not(feature = "no_closure"))] - Share(Ident), + Share(Box), } impl Default for Stmt { @@ -670,7 +679,7 @@ impl Stmt { Self::Export(_, pos) => *pos, #[cfg(not(feature = "no_closure"))] - Self::Share(Ident { pos, .. }) => *pos, + Self::Share(x) => x.pos, } } @@ -701,7 +710,7 @@ impl Stmt { Self::Export(_, pos) => *pos = new_pos, #[cfg(not(feature = "no_closure"))] - Self::Share(Ident { pos, .. }) => *pos = new_pos, + Self::Share(x) => x.pos = new_pos, } self @@ -774,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 { @@ -802,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. @@ -820,14 +823,13 @@ impl CustomExpr { /// This type is volatile and may change. #[cfg(not(feature = "no_float"))] #[derive(Debug, PartialEq, PartialOrd, Clone)] -pub struct FloatWrapper(pub FLOAT, pub Position); +pub struct FloatWrapper(pub FLOAT); #[cfg(not(feature = "no_float"))] impl Hash for FloatWrapper { #[inline(always)] fn hash(&self, state: &mut H) { state.write(&self.0.to_le_bytes()); - self.1.hash(state); } } @@ -836,23 +838,47 @@ impl Neg for FloatWrapper { type Output = Self; fn neg(self) -> Self::Output { - Self(-self.0, self.1) + Self(-self.0) } } #[cfg(not(feature = "no_float"))] -impl From<(INT, Position)> for FloatWrapper { - fn from((value, pos): (INT, Position)) -> Self { - Self(value as FLOAT, pos) +impl From for FloatWrapper { + fn from(value: INT) -> Self { + Self(value as FLOAT) } } /// A binary expression structure. #[derive(Debug, Clone, Hash)] pub struct BinaryExpr { + /// LHS expression. pub lhs: Expr, + /// RHS expression. pub rhs: Expr, - pub pos: Position, +} + +/// A function call. +#[derive(Debug, Clone, Hash, Default)] +pub struct FnCallInfo { + /// Function name. + /// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls + /// and the function names are predictable, so no need to allocate a new `String`. + pub name: Cow<'static, str>, + /// Namespace of the function, if any. + pub namespace: Option>, + /// Call native functions only? Set to `true` to skip searching for script-defined function overrides + /// when it is certain that the function must be native (e.g. an operator). + pub native_only: bool, + /// Does this function call capture the parent scope? + pub capture: bool, + /// Pre-calculated hash for a script-defined function of the same name and number of parameters. + pub hash: u64, + /// List of function call arguments. + pub args: StaticVec, + /// 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, } /// _[INTERNALS]_ An expression sub-tree. @@ -867,12 +893,12 @@ pub struct BinaryExpr { #[derive(Debug, Clone, Hash)] pub enum Expr { /// Integer constant. - IntegerConstant(Box<(INT, Position)>), + IntegerConstant(INT, Position), /// Floating-point constant. #[cfg(not(feature = "no_float"))] - FloatConstant(Box), + FloatConstant(FloatWrapper, Position), /// Character constant. - CharConstant(Box<(char, Position)>), + CharConstant(char, Position), /// String constant. StringConstant(Box), /// FnPtr constant. @@ -880,37 +906,27 @@ pub enum Expr { /// Variable access - ((variable name, position), optional modules, hash, optional index) Variable(Box<(Ident, Option>, u64, Option)>), /// Property access. - Property(Box<((ImmutableString, String, String), Position)>), + Property(Box<(IdentX, (String, String))>), /// { stmt } - Stmt(Box<(Stmt, Position)>), + Stmt(Box, Position), /// Wrapped expression - should not be optimized away. Expr(Box), - /// func(expr, ... ) - ((function name, native_only, capture, position), optional modules, hash, arguments, optional default value) - /// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls - /// and the function names are predictable, so no need to allocate a new `String`. - FnCall( - Box<( - (Cow<'static, str>, bool, bool, Position), - Option>, - u64, - StaticVec, - Option, // Default value is `bool` in order for `Expr` to be `Hash`. - )>, - ), + /// func(expr, ... ) + 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 @@ -918,7 +934,7 @@ pub enum Expr { /// () Unit(Position), /// Custom syntax - Custom(Box), + Custom(Box, Position), } impl Default for Expr { @@ -936,22 +952,22 @@ impl Expr { Some(match self { Self::Expr(x) => return x.get_type_id(), - Self::IntegerConstant(_) => TypeId::of::(), + Self::IntegerConstant(_, _) => TypeId::of::(), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_) => TypeId::of::(), - Self::CharConstant(_) => TypeId::of::(), + Self::FloatConstant(_, _) => TypeId::of::(), + 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, }) @@ -964,10 +980,10 @@ impl Expr { Some(match self { Self::Expr(x) => return x.get_constant_value(), - Self::IntegerConstant(x) => x.0.into(), + Self::IntegerConstant(x, _) => (*x).into(), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(x) => x.0.into(), - Self::CharConstant(x) => x.0.into(), + Self::FloatConstant(x, _) => x.0.into(), + Self::CharConstant(x, _) => (*x).into(), Self::StringConstant(x) => x.name.clone().into(), Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked( x.name.clone(), @@ -978,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(), ))) @@ -1011,26 +1025,26 @@ impl Expr { Self::Expr(x) => x.position(), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(x) => x.1, + Self::FloatConstant(_, pos) => *pos, - Self::IntegerConstant(x) => x.1, - Self::CharConstant(x) => x.1, + Self::IntegerConstant(_, pos) => *pos, + 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::Property(x) => x.1, - Self::Stmt(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.0).3, + 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, } } @@ -1042,22 +1056,22 @@ impl Expr { } #[cfg(not(feature = "no_float"))] - Self::FloatConstant(x) => x.1 = new_pos, + Self::FloatConstant(_, pos) => *pos = new_pos, - Self::IntegerConstant(x) => x.1 = new_pos, - Self::CharConstant(x) => x.1 = new_pos, + Self::IntegerConstant(_, pos) => *pos = new_pos, + 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.1 = new_pos, - Self::Stmt(x) => x.1 = new_pos, - Self::FnCall(x) => (x.0).3 = new_pos, - Self::And(x) | Self::Or(x) | Self::In(x) => x.pos = new_pos, + Self::Property(x) => (x.0).pos = new_pos, + Self::Stmt(_, pos) => *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 @@ -1070,13 +1084,15 @@ 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() } - Self::Stmt(x) => x.0.is_pure(), + Self::Stmt(x, _) => x.is_pure(), Self::Variable(_) => true, @@ -1099,10 +1115,10 @@ impl Expr { Self::Expr(x) => x.is_literal(), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_) => true, + Self::FloatConstant(_, _) => true, - Self::IntegerConstant(_) - | Self::CharConstant(_) + Self::IntegerConstant(_, _) + | Self::CharConstant(_, _) | Self::StringConstant(_) | Self::FnPointer(_) | Self::True(_) @@ -1110,15 +1126,15 @@ 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, + | (Self::CharConstant(_, _), Self::StringConstant(_)) => true, _ => false, }, @@ -1132,10 +1148,10 @@ impl Expr { Self::Expr(x) => x.is_constant(), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_) => true, + Self::FloatConstant(_, _) => true, - Self::IntegerConstant(_) - | Self::CharConstant(_) + Self::IntegerConstant(_, _) + | Self::CharConstant(_, _) | Self::StringConstant(_) | Self::FnPointer(_) | Self::True(_) @@ -1143,15 +1159,15 @@ 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, + | (Self::CharConstant(_, _), Self::StringConstant(_)) => true, _ => false, }, @@ -1165,25 +1181,25 @@ impl Expr { Self::Expr(x) => x.is_valid_postfix(token), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_) => false, + Self::FloatConstant(_, _) => false, - Self::IntegerConstant(_) - | Self::CharConstant(_) + 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::Stmt(_, _) + | Self::FnCall(_, _) + | Self::Dot(_, _) + | Self::Index(_, _) + | Self::Array(_, _) + | Self::Map(_, _) => match token { #[cfg(not(feature = "no_index"))] Token::LeftBracket => true, _ => false, @@ -1205,7 +1221,7 @@ impl Expr { _ => false, }, - Self::Custom(_) => false, + Self::Custom(_, _) => false, } } @@ -1215,10 +1231,10 @@ impl Expr { pub(crate) fn into_property(self) -> Self { match self { Self::Variable(x) if x.1.is_none() => { - let Ident { name, pos } = x.0; - let getter = make_getter(&name); - let setter = make_setter(&name); - Self::Property(Box::new(((name.into(), getter, setter), pos))) + let ident = x.0; + let getter = make_getter(&ident.name); + let setter = make_setter(&ident.name); + Self::Property(Box::new((ident.into(), (getter, setter)))) } _ => self, } diff --git a/src/engine.rs b/src/engine.rs index fc82de5c..b08164ba 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation `Engine`. -use crate::ast::{BinaryExpr, Expr, Ident, ReturnType, Stmt}; +use crate::ast::{BinaryExpr, Expr, FnCallInfo, Ident, IdentX, ReturnType, Stmt}; use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant}; use crate::fn_call::run_builtin_op_assignment; use crate::fn_native::{Callback, FnPtr, OnVarCallback}; @@ -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,21 +968,28 @@ impl Engine { ChainType::Dot => { match rhs { // xxx.fn_name(arg_expr_list) - Expr::FnCall(x) if x.1.is_none() => { - let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref(); - let def_val = def_val.map(Into::::into); + Expr::FnCall(x, pos) if x.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + hash, + def_value, + .. + } = x.as_ref(); + let def_value = def_value.map(Into::::into); let args = idx_val.as_fn_call_args(); self.make_method_call( - state, lib, name, *hash, target, args, &def_val, *native, false, level, + state, lib, name, *hash, target, args, &def_value, *native, false, + level, ) .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 ((prop, _, _), pos) = x.as_ref(); - let index = prop.clone().into(); + let IdentX { name, pos } = &x.0; + let index = name.clone().into(); let mut val = self .get_indexed_mut(state, lib, target, index, *pos, true, false, level)?; @@ -991,8 +998,8 @@ impl Engine { } // {xxx:map}.id Expr::Property(x) if target.is::() => { - let ((prop, _, _), pos) = x.as_ref(); - let index = prop.clone().into(); + let IdentX { name, pos } = &x.0; + let index = name.clone().into(); let val = self.get_indexed_mut( state, lib, target, index, *pos, false, false, level, )?; @@ -1001,7 +1008,7 @@ impl Engine { } // xxx.id = ??? Expr::Property(x) if new_val.is_some() => { - let ((_, _, setter), pos) = x.as_ref(); + let (IdentX { pos, .. }, (_, setter)) = x.as_ref(); let mut new_val = new_val; let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0]; self.exec_fn_call( @@ -1013,7 +1020,7 @@ impl Engine { } // xxx.id Expr::Property(x) => { - let ((_, getter, _), pos) = x.as_ref(); + let (IdentX { pos, .. }, (getter, _)) = x.as_ref(); let mut args = [target.as_mut()]; self.exec_fn_call( state, lib, getter, 0, &mut args, is_ref, true, false, None, &None, @@ -1023,30 +1030,36 @@ 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 ((prop, _, _), pos) = p.as_ref(); - let index = prop.clone().into(); + let IdentX { name, pos } = &p.0; + let index = name.clone().into(); self.get_indexed_mut( state, lib, target, index, *pos, false, true, level, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr - Expr::FnCall(x) if x.1.is_none() => { - let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref(); - let def_val = def_val.map(Into::::into); + Expr::FnCall(x, pos) if x.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + hash, + def_value, + .. + } = x.as_ref(); + let def_value = def_value.map(Into::::into); let args = idx_val.as_fn_call_args(); let (val, _) = self .make_method_call( - state, lib, name, *hash, target, args, &def_val, *native, + state, lib, name, *hash, target, args, &def_value, *native, false, level, ) .map_err(|err| err.fill_position(*pos))?; val.into() } // {xxx:map}.module::fn_name(...) - syntax error - Expr::FnCall(_) => unreachable!(), + Expr::FnCall(_, _) => unreachable!(), // Others - syntax error _ => unreachable!(), }; @@ -1055,14 +1068,14 @@ 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) => { - let ((_, getter, setter), pos) = p.as_ref(); + let (IdentX { pos, .. }, (getter, setter)) = p.as_ref(); let arg_values = &mut [target.as_mut(), &mut Default::default()]; let args = &mut arg_values[..1]; let (mut val, updated) = self @@ -1086,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 { @@ -1102,7 +1115,7 @@ impl Engine { EvalAltResult::ErrorDotExpr(_, _) => { Ok(Default::default()) } - _ => Err(err.fill_position(x.pos)), + _ => Err(err.fill_position(*x_pos)), }, )?; } @@ -1110,13 +1123,19 @@ 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.1.is_none() => { - let ((name, native, _, pos), _, hash, _, def_val) = f.as_ref(); - let def_val = def_val.map(Into::::into); + Expr::FnCall(f, pos) if f.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + hash, + def_value, + .. + } = f.as_ref(); + let def_value = def_value.map(Into::::into); let args = idx_val.as_fn_call_args(); let (mut val, _) = self .make_method_call( - state, lib, name, *hash, target, args, &def_val, *native, + state, lib, name, *hash, target, args, &def_value, *native, false, level, ) .map_err(|err| err.fill_position(*pos))?; @@ -1130,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!(), } @@ -1161,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!(), }; @@ -1213,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!(), @@ -1225,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)) } } } @@ -1250,33 +1269,35 @@ impl Engine { .map_err(|err| err.fill_position(expr.position()))?; match expr { - Expr::FnCall(x) if x.1.is_none() => { - let arg_values = - x.3.iter() - .map(|arg_expr| { - self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) - }) - .collect::, _>>()?; + Expr::FnCall(x, _) if x.namespace.is_none() => { + let arg_values = x + .args + .iter() + .map(|arg_expr| { + self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) + }) + .collect::, _>>()?; 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.1.is_none() => { - x.3.iter() + 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(_, _) => unreachable!(), _ => self .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)? .into(), @@ -1284,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( @@ -1433,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()); @@ -1442,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() @@ -1459,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(), @@ -1489,13 +1511,13 @@ 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.0.into()), + 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::CharConstant(x) => Ok(x.0.into()), + Expr::FloatConstant(x, _) => Ok(x.0.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()) } @@ -1514,30 +1536,30 @@ impl Engine { Expr::Property(_) => unreachable!(), // Statement block - Expr::Stmt(x) => self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level), + Expr::Stmt(x, _) => self.eval_stmt(scope, mods, state, lib, this_ptr, x, level), // 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)) @@ -1546,31 +1568,46 @@ impl Engine { )))), // Normal function call - Expr::FnCall(x) if x.1.is_none() => { - let ((name, native, cap_scope, pos), _, hash, args_expr, def_val) = x.as_ref(); - let def_val = def_val.map(Into::::into); + Expr::FnCall(x, pos) if x.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + capture: cap_scope, + hash, + args, + def_value, + .. + } = x.as_ref(); + let def_value = def_value.map(Into::::into); self.make_function_call( - scope, mods, state, lib, this_ptr, name, args_expr, &def_val, *hash, *native, + scope, mods, state, lib, this_ptr, name, args, &def_value, *hash, *native, false, *cap_scope, level, ) .map_err(|err| err.fill_position(*pos)) } // Module-qualified function call - Expr::FnCall(x) if x.1.is_some() => { - let ((name, _, _, pos), modules, hash, args_expr, def_val) = x.as_ref(); + Expr::FnCall(x, pos) if x.namespace.is_some() => { + let FnCallInfo { + name, + namespace, + hash, + args, + def_value, + .. + } = x.as_ref(); self.make_qualified_function_call( - scope, mods, state, lib, this_ptr, modules, name, args_expr, *def_val, *hash, + scope, mods, state, lib, this_ptr, namespace, name, args, *def_value, *hash, level, ) .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() @@ -1583,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() @@ -1600,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() @@ -1616,7 +1652,7 @@ impl Engine { this_ptr, level, }; - func(&mut context, &expressions) + (custom.func)(&mut context, &expressions) } _ => unreachable!(), @@ -1785,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, )?; @@ -1793,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, )?; @@ -2090,8 +2126,8 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] - Stmt::Share(Ident { name: var_name, .. }) => { - match scope.get_index(var_name) { + Stmt::Share(x) => { + match scope.get_index(&x.name) { Some((index, ScopeEntryType::Normal)) => { let (val, _) = scope.get_mut(index); diff --git a/src/lib.rs b/src/lib.rs index b2dc3c10..7889c553 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -165,7 +165,8 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] pub use ast::{ - BinaryExpr, CustomExpr, Expr, FloatWrapper, Ident, IdentX, ReturnType, ScriptFnDef, Stmt, + BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallInfo, Ident, IdentX, ReturnType, ScriptFnDef, + Stmt, }; #[cfg(feature = "internals")] diff --git a/src/optimize.rs b/src/optimize.rs index e72dba20..e482c805 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,6 +1,6 @@ //! Module implementing the AST optimizer. -use crate::ast::{BinaryExpr, CustomExpr, Expr, ScriptFnDef, Stmt, AST}; +use crate::ast::{BinaryExpr, CustomExpr, Expr, FnCallInfo, ScriptFnDef, Stmt, AST}; use crate::dynamic::Dynamic; use crate::engine::{ Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, @@ -404,9 +404,9 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { ))) } // expr; - Stmt::Expr(Expr::Stmt(x)) if matches!(x.0, Stmt::Expr(_)) => { + Stmt::Expr(Expr::Stmt(x, _)) if matches!(*x, Stmt::Expr(_)) => { state.set_dirty(); - optimize_stmt(x.0, state, preserve_result) + optimize_stmt(*x, state, preserve_result) } // expr; Stmt::Expr(expr) => Stmt::Expr(optimize_expr(expr, state)), @@ -434,11 +434,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { // expr - do not promote because there is a reason it is wrapped in an `Expr::Expr` Expr::Expr(x) => Expr::Expr(Box::new(optimize_expr(*x, state))), // { stmt } - Expr::Stmt(x) => match x.0 { + Expr::Stmt(x, pos) => match *x { // {} -> () Stmt::Noop(_) => { state.set_dirty(); - Expr::Unit(x.1) + Expr::Unit(pos) } // { expr } -> expr Stmt::Expr(expr) => { @@ -446,20 +446,19 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { optimize_expr(expr, state) } // { stmt } - stmt => Expr::Stmt(Box::new((optimize_stmt(stmt, state, true), x.1))), + stmt => Expr::Stmt(Box::new(optimize_stmt(stmt, state, true)), pos), }, // 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()) => { - let ((prop, _, _), _) = p.as_ref(); + (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,98 +466,94 @@ 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 >= 0 && (i.0 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.0 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)) } // string[int] - (Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.name.chars().count() => { + (Expr::StringConstant(s), Expr::IntegerConstant(i, _)) if i >= 0 && (i as usize) < s.name.chars().count() => { // String literal indexing - get the character state.set_dirty(); - Expr::CharConstant(Box::new((s.name.chars().nth(i.0 as usize).unwrap(), s.pos))) + Expr::CharConstant(s.name.chars().nth(i as usize).unwrap(), s.pos) } // lhs[rhs] (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(); if b.name.contains(a.name.as_str()) { Expr::True(a.pos) } else { Expr::False(a.pos) } } // 'x' in "xxxxx" - (Expr::CharConstant(a), Expr::StringConstant(b)) => { + (Expr::CharConstant(a, pos), Expr::StringConstant(b)) => { state.set_dirty(); - if b.name.contains(a.0) { Expr::True(a.1) } else { Expr::False(a.1) } + 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), Expr::Map(b)) => { + (Expr::CharConstant(a, pos), Expr::Map(b, _)) => { state.set_dirty(); - let ch = a.0.to_string(); + let ch = a.to_string(); - if b.0.iter().find(|(x, _)| x.name == &ch).is_some() { - Expr::True(a.1) + if b.iter().find(|(x, _)| x.name == &ch).is_some() { + Expr::True(pos) } else { - Expr::False(a.1) + Expr::False(pos) } } // lhs in rhs (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.0).0.as_ref()) => { - x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + 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, pos) } // Call built-in operators - Expr::FnCall(mut x) - if x.1.is_none() // Non-qualified + Expr::FnCall(mut x, pos) + if x.namespace.is_none() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations - && x.3.len() == 2 // binary call - && x.3.iter().all(Expr::is_constant) // all arguments are constants - && !is_valid_identifier((x.0).0.chars()) // cannot be scripted + && 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 ((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,24 +622,24 @@ 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; } } - x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); + Expr::FnCall(x, pos) } // Eagerly call functions - Expr::FnCall(mut x) - if x.1.is_none() // Non-qualified + Expr::FnCall(mut x, pos) + if x.namespace.is_none() // Non-qualified && state.optimization_level == OptimizationLevel::Full // full optimizations - && x.3.iter().all(Expr::is_constant) // all arguments are constants + && x.args.iter().all(Expr::is_constant) // all arguments are constants => { - let ((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,21 +668,21 @@ 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; } } - x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); + Expr::FnCall(x, pos) } // id(args ..) -> optimize function call arguments - Expr::FnCall(mut x) => { - x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); - Expr::FnCall(x) + Expr::FnCall(mut x, pos) => { + x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); + 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 d2a9d2f1..ebdbe625 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,8 @@ //! Main module defining the lexer and parser. -use crate::ast::{BinaryExpr, CustomExpr, Expr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt, AST}; +use crate::ast::{ + BinaryExpr, CustomExpr, Expr, FnCallInfo, Ident, IdentX, ReturnType, ScriptFnDef, Stmt, AST, +}; use crate::dynamic::{Dynamic, Union}; use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; use crate::module::ModuleRef; @@ -263,7 +265,7 @@ fn parse_fn_call( lib: &mut FunctionsLib, id: String, capture: bool, - mut modules: Option>, + mut namespace: Option>, settings: ParseSettings, ) -> Result { let (token, token_pos) = input.peek().unwrap(); @@ -288,7 +290,7 @@ fn parse_fn_call( Token::RightParen => { eat_token(input, Token::RightParen); - let hash_script = if let Some(modules) = modules.as_mut() { + let hash_script = if let Some(modules) = namespace.as_mut() { #[cfg(not(feature = "no_module"))] modules.set_index(state.find_module(&modules[0].0)); @@ -305,13 +307,17 @@ fn parse_fn_call( calc_script_fn_hash(empty(), &id, 0) }; - return Ok(Expr::FnCall(Box::new(( - (id.into(), false, capture, settings.pos), - modules, - hash_script, - args, - None, - )))); + return Ok(Expr::FnCall( + Box::new(FnCallInfo { + name: id.into(), + capture, + namespace, + hash: hash_script, + args, + ..Default::default() + }), + settings.pos, + )); } // id... _ => (), @@ -331,7 +337,7 @@ fn parse_fn_call( (Token::RightParen, _) => { eat_token(input, Token::RightParen); - let hash_script = if let Some(modules) = modules.as_mut() { + let hash_script = if let Some(modules) = namespace.as_mut() { #[cfg(not(feature = "no_module"))] modules.set_index(state.find_module(&modules[0].0)); @@ -348,13 +354,17 @@ fn parse_fn_call( calc_script_fn_hash(empty(), &id, args.len()) }; - return Ok(Expr::FnCall(Box::new(( - (id.into(), false, capture, settings.pos), - modules, - hash_script, - args, - None, - )))); + return Ok(Expr::FnCall( + Box::new(FnCallInfo { + name: id.into(), + capture, + namespace, + hash: hash_script, + args, + ..Default::default() + }), + settings.pos, + )); } // id(...args, (Token::Comma, _) => { @@ -400,35 +410,35 @@ fn parse_index_chain( // Check type of indexing - must be integer or string match &idx_expr { // lhs[int] - Expr::IntegerConstant(x) if x.0 < 0 => { + Expr::IntegerConstant(x, pos) if *x < 0 => { return Err(PERR::MalformedIndexExpr(format!( "Array access expects non-negative index: {} < 0", - x.0 + *x )) - .into_err(x.1)) + .into_err(*pos)) } - Expr::IntegerConstant(x) => match lhs { - Expr::Array(_) | Expr::StringConstant(_) => (), + Expr::IntegerConstant(_, pos) => match lhs { + Expr::Array(_, _) | Expr::StringConstant(_) => (), - Expr::Map(_) => { + Expr::Map(_, _) => { return Err(PERR::MalformedIndexExpr( "Object map access expects string index, not a number".into(), ) - .into_err(x.1)) + .into_err(*pos)) } #[cfg(not(feature = "no_float"))] - Expr::FloatConstant(_) => { + Expr::FloatConstant(_, _) => { return Err(PERR::MalformedIndexExpr( "Only arrays, object maps and strings can be indexed".into(), ) .into_err(lhs.position())) } - Expr::CharConstant(_) - | Expr::And(_) - | Expr::Or(_) - | Expr::In(_) + Expr::CharConstant(_, _) + | Expr::And(_, _) + | Expr::Or(_, _) + | Expr::In(_, _) | Expr::True(_) | Expr::False(_) | Expr::Unit(_) => { @@ -443,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(), ) @@ -453,17 +463,17 @@ fn parse_index_chain( } #[cfg(not(feature = "no_float"))] - Expr::FloatConstant(_) => { + Expr::FloatConstant(_, _) => { return Err(PERR::MalformedIndexExpr( "Only arrays, object maps and strings can be indexed".into(), ) .into_err(lhs.position())) } - Expr::CharConstant(_) - | Expr::And(_) - | Expr::Or(_) - | Expr::In(_) + Expr::CharConstant(_, _) + | Expr::And(_, _) + | Expr::Or(_, _) + | Expr::In(_, _) | Expr::True(_) | Expr::False(_) | Expr::Unit(_) => { @@ -478,14 +488,14 @@ fn parse_index_chain( // lhs[float] #[cfg(not(feature = "no_float"))] - x @ Expr::FloatConstant(_) => { + x @ Expr::FloatConstant(_, _) => { return Err(PERR::MalformedIndexExpr( "Array access expects integer index, not a float".into(), ) .into_err(x.position())) } // lhs[char] - x @ Expr::CharConstant(_) => { + x @ Expr::CharConstant(_, _) => { return Err(PERR::MalformedIndexExpr( "Array access expects integer index, not a character".into(), ) @@ -499,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(), ) @@ -531,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 _ => { @@ -543,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, + )), } } } @@ -625,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. @@ -734,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. @@ -754,7 +764,7 @@ fn parse_primary( // { - block statement as expression Token::LeftBrace if settings.allow_stmt_expr => { return parse_block(input, state, lib, settings.level_up()) - .map(|block| Expr::Stmt(Box::new((block, settings.pos)))) + .map(|block| Expr::Stmt(Box::new(block), settings.pos)) } Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)), _ => input.next().unwrap(), @@ -763,10 +773,10 @@ fn parse_primary( let (next_token, _) = input.peek().unwrap(); let mut root_expr = match token { - Token::IntegerConstant(x) => Expr::IntegerConstant(Box::new((x, settings.pos))), + Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), #[cfg(not(feature = "no_float"))] - Token::FloatConstant(x) => Expr::FloatConstant(Box::new(FloatWrapper(x, settings.pos))), - Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))), + Token::FloatConstant(x) => Expr::FloatConstant(FloatWrapper(x), settings.pos), + Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::StringConstant(s) => Expr::StringConstant(Box::new(IdentX::new(s, settings.pos))), // Function call @@ -946,35 +956,30 @@ fn parse_unary( match token { // If statement is allowed to act as expressions - Token::If if settings.allow_if_expr => Ok(Expr::Stmt(Box::new(( - parse_if(input, state, lib, settings.level_up())?, + Token::If if settings.allow_if_expr => Ok(Expr::Stmt( + Box::new(parse_if(input, state, lib, settings.level_up())?), settings.pos, - )))), + )), // -expr Token::UnaryMinus => { let pos = eat_token(input, Token::UnaryMinus); match parse_unary(input, state, lib, settings.level_up())? { // Negative integer - Expr::IntegerConstant(x) => { - let (num, pos) = *x; - - num.checked_neg() - .map(|i| Expr::IntegerConstant(Box::new((i, pos)))) - .or_else(|| { - #[cfg(not(feature = "no_float"))] - return Some(Expr::FloatConstant(Box::new( - -Into::::into(*x), - ))); - #[cfg(feature = "no_float")] - return None; - }) - .ok_or_else(|| LexError::MalformedNumber(format!("-{}", x.0)).into_err(pos)) - } + Expr::IntegerConstant(num, pos) => num + .checked_neg() + .map(|i| Expr::IntegerConstant(i, pos)) + .or_else(|| { + #[cfg(not(feature = "no_float"))] + return Some(Expr::FloatConstant(-Into::::into(num), pos)); + #[cfg(feature = "no_float")] + return None; + }) + .ok_or_else(|| LexError::MalformedNumber(format!("-{}", num)).into_err(pos)), // Negative float #[cfg(not(feature = "no_float"))] - Expr::FloatConstant(x) => Ok(Expr::FloatConstant(Box::new(-(*x)))), + Expr::FloatConstant(x, pos) => Ok(Expr::FloatConstant(-x, pos)), // Call negative function expr => { @@ -983,13 +988,17 @@ fn parse_unary( let mut args = StaticVec::new(); args.push(expr); - Ok(Expr::FnCall(Box::new(( - (op.into(), true, false, pos), - None, - hash, - args, - None, - )))) + Ok(Expr::FnCall( + Box::new(FnCallInfo { + name: op.into(), + native_only: true, + namespace: None, + hash, + args, + ..Default::default() + }), + pos, + )) } } } @@ -1008,13 +1017,17 @@ fn parse_unary( let op = "!"; let hash = calc_script_fn_hash(empty(), op, 1); - Ok(Expr::FnCall(Box::new(( - (op.into(), true, false, pos), - None, - hash, - args, - 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, + )) } // | ... #[cfg(not(feature = "no_function"))] @@ -1093,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)) @@ -1127,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 @@ -1178,84 +1191,70 @@ 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() => { - let Ident { name, pos } = x.0; + let ident = x.0; + let getter = make_getter(&ident.name); + let setter = make_setter(&ident.name); + let rhs = Expr::Property(Box::new((ident.into(), (getter, setter)))); - let getter = make_getter(&name); - let setter = make_setter(&name); - let rhs = Expr::Property(Box::new(((name.into(), getter, setter), pos))); - - Expr::Dot(Box::new(BinaryExpr { - lhs, - rhs, - pos: op_pos, - })) + Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) } // lhs.module::id - syntax error (_, Expr::Variable(x)) if x.1.is_some() => { 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)) - if x.3.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&(x.0).0.as_ref()) => + (_, 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.0).0, - (x.0).0 + x.name, x.name )) - .into_err((x.0).3)); + .into_err(pos)); } // lhs.func!(...) - (_, Expr::FnCall(x)) if (x.0).2 => { + (_, Expr::FnCall(x, pos)) if x.capture => { return Err(PERR::MalformedCapture( "method-call style does not support capturing".into(), ) - .into_err((x.0).3)) + .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())), }) @@ -1264,10 +1263,10 @@ 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::IntegerConstant(_, _)) + | (_, x @ Expr::And(_, _)) + | (_, x @ Expr::Or(_, _)) + | (_, x @ Expr::In(_, _)) | (_, x @ Expr::True(_)) | (_, x @ Expr::False(_)) | (_, x @ Expr::Unit(_)) => { @@ -1278,7 +1277,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + (_, x @ Expr::FloatConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression expects a string, array or object map".into(), ) @@ -1287,18 +1286,18 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), + | (Expr::CharConstant(_, _), Expr::StringConstant(_)) => (), // 123.456 in "xxxx" #[cfg(not(feature = "no_float"))] - (x @ Expr::FloatConstant(_), Expr::StringConstant(_)) => { + (x @ Expr::FloatConstant(_, _), Expr::StringConstant(_)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not a float".into(), ) .into_err(x.position())) } // 123 in "xxxx" - (x @ Expr::IntegerConstant(_), Expr::StringConstant(_)) => { + (x @ Expr::IntegerConstant(_, _), Expr::StringConstant(_)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not a number".into(), ) @@ -1306,9 +1305,9 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { return Err(PERR::MalformedInExpr( @@ -1317,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(), ) @@ -1339,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(), ) @@ -1358,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(), ) @@ -1393,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. @@ -1480,7 +1476,13 @@ fn parse_binary_op( let cmp_def = Some(false); let op = op_token.syntax(); let hash = calc_script_fn_hash(empty(), &op, 2); - let op = (op, true, false, pos); + + let op_base = FnCallInfo { + name: op, + native_only: true, + capture: false, + ..Default::default() + }; let mut args = StaticVec::new(); args.push(root); @@ -1497,35 +1499,62 @@ fn parse_binary_op( | Token::PowerOf | Token::Ampersand | Token::Pipe - | Token::XOr => Expr::FnCall(Box::new((op, None, hash, args, None))), + | 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((op, None, hash, args, Some(true)))), + 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((op, None, hash, args, cmp_def))), + | 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(); @@ -1542,8 +1571,15 @@ fn parse_binary_op( Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => { // Accept non-native functions for custom operators - let op = (op.0, false, op.2, op.3); - Expr::FnCall(Box::new((op, None, hash, args, None))) + 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)), @@ -1617,7 +1653,7 @@ fn parse_custom_syntax( MARKER_BLOCK => { let stmt = parse_block(input, state, lib, settings)?; let pos = stmt.position(); - exprs.push(Expr::Stmt(Box::new((stmt, pos)))); + exprs.push(Expr::Stmt(Box::new(stmt), pos)); segments.push(MARKER_BLOCK.into()); } s => match input.next().unwrap() { @@ -1636,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. @@ -2464,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(( - (KEYWORD_FN_PTR_CURRY.into(), false, false, pos), - None, - hash, - args, - None, - ))); + let expr = Expr::FnCall( + Box::new(FnCallInfo { + name: KEYWORD_FN_PTR_CURRY.into(), + hash, + args, + ..Default::default() + }), + pos, + ); // If there are captured variables, convert the entire expression into a statement block, // then insert the relevant `Share` statements. @@ -2479,10 +2519,10 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po // Statement block let mut statements: Vec<_> = Default::default(); // Insert `Share` statements - statements.extend(externals.into_iter().map(Stmt::Share)); + statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x)))); // Final expression statements.push(Stmt::Expr(expr)); - Expr::Stmt(Box::new((Stmt::Block(statements, pos), pos))) + Expr::Stmt(Box::new(Stmt::Block(statements, pos)), pos) } #[cfg(feature = "no_closure")] @@ -2755,11 +2795,11 @@ impl Engine { pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { match value.0 { #[cfg(not(feature = "no_float"))] - Union::Float(value) => Some(Expr::FloatConstant(Box::new(FloatWrapper(value, pos)))), + Union::Float(value) => Some(Expr::FloatConstant(FloatWrapper(value), pos)), Union::Unit(_) => Some(Expr::Unit(pos)), - Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))), - Union::Char(value) => Some(Expr::CharConstant(Box::new((value, pos)))), + Union::Int(value) => Some(Expr::IntegerConstant(value, pos)), + Union::Char(value) => Some(Expr::CharConstant(value, pos)), Union::Str(value) => Some(Expr::StringConstant(Box::new(IdentX::new(value, pos)))), Union::Bool(true) => Some(Expr::True(pos)), Union::Bool(false) => Some(Expr::False(pos)), @@ -2771,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 } @@ -2787,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 }