From 89811c8dfcb4261cdad56780a5c21c01376616f0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 31 Oct 2020 14:13:45 +0800 Subject: [PATCH] Pack Stmt and Expr some more. --- src/ast.rs | 148 ++++++++++++++++++-------------- src/engine.rs | 140 +++++++++++++++++++----------- src/lib.rs | 3 +- src/optimize.rs | 62 +++++++------- src/parser.rs | 220 +++++++++++++++++++++++++++--------------------- 5 files changed, 334 insertions(+), 239 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index c9d0d96c..5de058f4 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 @@ -820,14 +829,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,22 +844,50 @@ 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, + /// Position of the expression. + 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, + /// Position of the function call. pub pos: Position, } @@ -867,12 +903,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,23 +916,13 @@ 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), /// lhs.rhs Dot(Box), /// expr[expr] @@ -936,10 +962,10 @@ 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(_) => { @@ -964,10 +990,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(), @@ -1011,18 +1037,18 @@ 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::Property(x) => (x.0).pos, + Self::Stmt(_, pos) => *pos, Self::Variable(x) => (x.0).pos, - Self::FnCall(x) => (x.0).3, + Self::FnCall(x) => x.pos, Self::And(x) | Self::Or(x) | Self::In(x) => x.pos, @@ -1042,18 +1068,18 @@ 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::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::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::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos, Self::Dot(x) | Self::Index(x) => x.pos = new_pos, @@ -1076,7 +1102,7 @@ impl Expr { 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 +1125,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(_) @@ -1118,7 +1144,7 @@ impl Expr { // Check in expression 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 +1158,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(_) @@ -1151,7 +1177,7 @@ impl Expr { // Check in expression Self::In(x) => match (&x.lhs, &x.rhs) { (Self::StringConstant(_), Self::StringConstant(_)) - | (Self::CharConstant(_), Self::StringConstant(_)) => true, + | (Self::CharConstant(_, _), Self::StringConstant(_)) => true, _ => false, }, @@ -1165,10 +1191,10 @@ 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(_) @@ -1178,7 +1204,7 @@ impl Expr { | Self::Unit(_) => false, Self::StringConstant(_) - | Self::Stmt(_) + | Self::Stmt(_, _) | Self::FnCall(_) | Self::Dot(_) | Self::Index(_) @@ -1215,10 +1241,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..aaad7178 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}; @@ -968,12 +968,20 @@ 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) if x.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + pos, + 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)) } @@ -981,8 +989,8 @@ impl Engine { 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 +999,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 +1009,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 +1021,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, @@ -1026,20 +1034,27 @@ impl Engine { Expr::Index(x) | Expr::Dot(x) 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) if x.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + pos, + 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))?; @@ -1062,7 +1077,7 @@ impl Engine { 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 @@ -1110,13 +1125,20 @@ 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) if f.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + pos, + 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))?; @@ -1250,13 +1272,14 @@ 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()); } @@ -1268,14 +1291,14 @@ impl Engine { // 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() - .map(|arg_expr| { - self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) - }) - .collect::, _>>()? - .into() - } + 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)? @@ -1491,11 +1514,11 @@ impl Engine { let result = match expr { Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x.as_ref(), 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::FloatConstant(x, _) => Ok(x.0.into()), Expr::StringConstant(x) => Ok(x.name.to_string().into()), - Expr::CharConstant(x) => Ok(x.0.into()), + Expr::CharConstant(x, _) => Ok((*x).into()), Expr::FnPointer(x) => { Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into()) } @@ -1514,7 +1537,7 @@ 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"))] @@ -1546,21 +1569,38 @@ 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) if x.namespace.is_none() => { + let FnCallInfo { + name, + native_only: native, + capture: cap_scope, + pos, + 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) if x.namespace.is_some() => { + let FnCallInfo { + name, + pos, + 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)) @@ -2090,8 +2130,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..22787524 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,7 +446,7 @@ 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 @@ -454,7 +454,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { Expr::Dot(x) => match (x.lhs, x.rhs) { // map.string (Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => { - let ((prop, _, _), _) = p.as_ref(); + 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(); @@ -475,13 +475,13 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { #[cfg(not(feature = "no_index"))] Expr::Index(x) => match (x.lhs, x.rhs) { // array[int] - (Expr::Array(mut a), Expr::IntegerConstant(i)) - if i.0 >= 0 && (i.0 as usize) < a.0.len() && a.0.iter().all(Expr::is_pure) => + (Expr::Array(mut a), Expr::IntegerConstant(i, _)) + if i >= 0 && (i as usize) < a.0.len() && a.0.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); + let mut expr = a.0.remove(i as usize); expr.set_position(a.1); expr } @@ -496,10 +496,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> 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 { @@ -526,9 +526,9 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { 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)) => { @@ -540,14 +540,14 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } } // '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) + Expr::True(pos) } else { - Expr::False(a.1) + Expr::False(pos) } } // lhs in rhs @@ -607,20 +607,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { }, // 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(mut x) 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) } // Call built-in operators Expr::FnCall(mut x) - if x.1.is_none() // Non-qualified + 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, pos, 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(); @@ -636,17 +636,17 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } } - x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); + x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); Expr::FnCall(x) } // Eagerly call functions Expr::FnCall(mut x) - if x.1.is_none() // Non-qualified + 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, pos, args, def_value, .. } = x.as_mut(); // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] @@ -682,13 +682,13 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } } - x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); + x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); Expr::FnCall(x) } // id(args ..) -> optimize function call arguments Expr::FnCall(mut x) => { - x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); + x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); Expr::FnCall(x) } diff --git a/src/parser.rs b/src/parser.rs index d2a9d2f1..89e49d2b 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,15 @@ 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, + return Ok(Expr::FnCall(Box::new(FnCallInfo { + name: id.into(), + capture, + namespace, + hash: hash_script, args, - None, - )))); + pos: settings.pos, + ..Default::default() + }))); } // id... _ => (), @@ -331,7 +335,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 +352,15 @@ 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, + return Ok(Expr::FnCall(Box::new(FnCallInfo { + name: id.into(), + capture, + namespace, + hash: hash_script, args, - None, - )))); + pos: settings.pos, + ..Default::default() + }))); } // id(...args, (Token::Comma, _) => { @@ -400,32 +406,32 @@ 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::IntegerConstant(_, pos) => match lhs { Expr::Array(_) | Expr::StringConstant(_) => (), 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::CharConstant(_, _) | Expr::And(_) | Expr::Or(_) | Expr::In(_) @@ -453,14 +459,14 @@ 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::CharConstant(_, _) | Expr::And(_) | Expr::Or(_) | Expr::In(_) @@ -478,14 +484,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(), ) @@ -754,7 +760,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 +769,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 +952,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 +984,15 @@ fn parse_unary( let mut args = StaticVec::new(); args.push(expr); - Ok(Expr::FnCall(Box::new(( - (op.into(), true, false, pos), - None, + Ok(Expr::FnCall(Box::new(FnCallInfo { + name: op.into(), + native_only: true, + namespace: None, hash, args, - None, - )))) + pos, + ..Default::default() + }))) } } } @@ -1008,13 +1011,15 @@ 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, + Ok(Expr::FnCall(Box::new(FnCallInfo { + name: op.into(), + native_only: true, hash, args, - Some(false), // NOT operator, when operating on invalid operand, defaults to false - )))) + def_value: Some(false), // NOT operator, when operating on invalid operand, defaults to false + pos, + ..Default::default() + }))) } // | ... #[cfg(not(feature = "no_function"))] @@ -1184,11 +1189,10 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { - let Ident { name, pos } = x.0; - - let getter = make_getter(&name); - let setter = make_setter(&name); - let rhs = Expr::Property(Box::new(((name.into(), getter, setter), pos))); + 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)))); Expr::Dot(Box::new(BinaryExpr { lhs, @@ -1234,21 +1238,20 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result + 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(x.pos)); } // lhs.func!(...) - (_, Expr::FnCall(x)) if (x.0).2 => { + (_, Expr::FnCall(x)) if x.capture => { return Err(PERR::MalformedCapture( "method-call style does not support capturing".into(), ) - .into_err((x.0).3)) + .into_err(x.pos)); } // lhs.func(...) (lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new(BinaryExpr { @@ -1264,7 +1267,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result Result { match (&lhs, &rhs) { - (_, x @ Expr::IntegerConstant(_)) + (_, x @ Expr::IntegerConstant(_, _)) | (_, x @ Expr::And(_)) | (_, x @ Expr::Or(_)) | (_, x @ Expr::In(_)) @@ -1278,7 +1281,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 +1290,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(), ) @@ -1339,18 +1342,18 @@ 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(), ) @@ -1480,7 +1483,14 @@ 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, + pos, + ..Default::default() + }; let mut args = StaticVec::new(); args.push(root); @@ -1497,17 +1507,31 @@ 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 + })), // '!=' 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 + })), // 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 + })), Token::Or => { let rhs = args.pop().unwrap(); @@ -1542,8 +1566,12 @@ 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 + })) } op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)), @@ -1617,7 +1645,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() { @@ -2464,13 +2492,13 @@ 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, + let expr = Expr::FnCall(Box::new(FnCallInfo { + name: KEYWORD_FN_PTR_CURRY.into(), hash, args, - None, - ))); + pos, + ..Default::default() + })); // If there are captured variables, convert the entire expression into a statement block, // then insert the relevant `Share` statements. @@ -2479,10 +2507,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 +2783,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)),