Optimize Expr.

This commit is contained in:
Stephen Chung 2020-10-31 23:26:21 +08:00
parent 89811c8dfc
commit 42eac410b7
5 changed files with 346 additions and 352 deletions

View File

@ -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. 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. This version also adds a low-level API for more flexibility when defining custom syntax.
@ -29,7 +30,7 @@ New features
Enhancements 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 Version 0.19.3

View File

@ -783,7 +783,6 @@ impl Stmt {
pub struct CustomExpr { pub struct CustomExpr {
pub(crate) keywords: StaticVec<Expr>, pub(crate) keywords: StaticVec<Expr>,
pub(crate) func: Shared<FnCustomSyntaxEval>, pub(crate) func: Shared<FnCustomSyntaxEval>,
pub(crate) pos: Position,
} }
impl fmt::Debug for CustomExpr { impl fmt::Debug for CustomExpr {
@ -811,11 +810,6 @@ impl CustomExpr {
pub fn func(&self) -> &FnCustomSyntaxEval { pub fn func(&self) -> &FnCustomSyntaxEval {
self.func.as_ref() 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. /// _[INTERNALS]_ A type wrapping a floating-point number.
@ -862,8 +856,6 @@ pub struct BinaryExpr {
pub lhs: Expr, pub lhs: Expr,
/// RHS expression. /// RHS expression.
pub rhs: Expr, pub rhs: Expr,
/// Position of the expression.
pub pos: Position,
} }
/// A function call. /// A function call.
@ -887,8 +879,6 @@ pub struct FnCallInfo {
/// Default value when the function is not found, mostly used to provide a default for comparison functions. /// 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` /// Type is `bool` in order for `FnCallInfo` to be `Hash`
pub def_value: Option<bool>, pub def_value: Option<bool>,
/// Position of the function call.
pub pos: Position,
} }
/// _[INTERNALS]_ An expression sub-tree. /// _[INTERNALS]_ An expression sub-tree.
@ -922,21 +912,21 @@ pub enum Expr {
/// Wrapped expression - should not be optimized away. /// Wrapped expression - should not be optimized away.
Expr(Box<Expr>), Expr(Box<Expr>),
/// func(expr, ... ) /// func(expr, ... )
FnCall(Box<FnCallInfo>), FnCall(Box<FnCallInfo>, Position),
/// lhs.rhs /// lhs.rhs
Dot(Box<BinaryExpr>), Dot(Box<BinaryExpr>, Position),
/// expr[expr] /// expr[expr]
Index(Box<BinaryExpr>), Index(Box<BinaryExpr>, Position),
/// [ expr, ... ] /// [ expr, ... ]
Array(Box<(StaticVec<Expr>, Position)>), Array(Box<StaticVec<Expr>>, Position),
/// #{ name:expr, ... } /// #{ name:expr, ... }
Map(Box<(StaticVec<(IdentX, Expr)>, Position)>), Map(Box<StaticVec<(IdentX, Expr)>>, Position),
/// lhs in rhs /// lhs in rhs
In(Box<BinaryExpr>), In(Box<BinaryExpr>, Position),
/// lhs && rhs /// lhs && rhs
And(Box<BinaryExpr>), And(Box<BinaryExpr>, Position),
/// lhs || rhs /// lhs || rhs
Or(Box<BinaryExpr>), Or(Box<BinaryExpr>, Position),
/// true /// true
True(Position), True(Position),
/// false /// false
@ -944,7 +934,7 @@ pub enum Expr {
/// () /// ()
Unit(Position), Unit(Position),
/// Custom syntax /// Custom syntax
Custom(Box<CustomExpr>), Custom(Box<CustomExpr>, Position),
} }
impl Default for Expr { impl Default for Expr {
@ -968,16 +958,16 @@ impl Expr {
Self::CharConstant(_, _) => TypeId::of::<char>(), Self::CharConstant(_, _) => TypeId::of::<char>(),
Self::StringConstant(_) => TypeId::of::<ImmutableString>(), Self::StringConstant(_) => TypeId::of::<ImmutableString>(),
Self::FnPointer(_) => TypeId::of::<FnPtr>(), Self::FnPointer(_) => TypeId::of::<FnPtr>(),
Self::True(_) | Self::False(_) | Self::In(_) | Self::And(_) | Self::Or(_) => { Self::True(_) | Self::False(_) | Self::In(_, _) | Self::And(_, _) | Self::Or(_, _) => {
TypeId::of::<bool>() TypeId::of::<bool>()
} }
Self::Unit(_) => TypeId::of::<()>(), Self::Unit(_) => TypeId::of::<()>(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::Array(_) => TypeId::of::<Array>(), Self::Array(_, _) => TypeId::of::<Array>(),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Self::Map(_) => TypeId::of::<Map>(), Self::Map(_, _) => TypeId::of::<Map>(),
_ => return None, _ => return None,
}) })
@ -1004,16 +994,14 @@ impl Expr {
Self::Unit(_) => ().into(), Self::Unit(_) => ().into(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::Array(x) if x.0.iter().all(Self::is_constant) => Dynamic(Union::Array(Box::new( Self::Array(x, _) if x.iter().all(Self::is_constant) => Dynamic(Union::Array(
x.0.iter() Box::new(x.iter().map(|v| v.get_constant_value().unwrap()).collect()),
.map(|v| v.get_constant_value().unwrap()) )),
.collect(),
))),
#[cfg(not(feature = "no_object"))] #[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( Dynamic(Union::Map(Box::new(
x.0.iter() x.iter()
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())) .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap()))
.collect(), .collect(),
))) )))
@ -1043,20 +1031,20 @@ impl Expr {
Self::CharConstant(_, pos) => *pos, Self::CharConstant(_, pos) => *pos,
Self::StringConstant(x) => x.pos, Self::StringConstant(x) => x.pos,
Self::FnPointer(x) => x.pos, Self::FnPointer(x) => x.pos,
Self::Array(x) => x.1, Self::Array(_, pos) => *pos,
Self::Map(x) => x.1, Self::Map(_, pos) => *pos,
Self::Property(x) => (x.0).pos, Self::Property(x) => (x.0).pos,
Self::Stmt(_, pos) => *pos, Self::Stmt(_, pos) => *pos,
Self::Variable(x) => (x.0).pos, Self::Variable(x) => (x.0).pos,
Self::FnCall(x) => x.pos, Self::FnCall(_, pos) => *pos,
Self::And(x) | Self::Or(x) | Self::In(x) => x.pos, Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos, Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos,
Self::Dot(x) | Self::Index(x) => x.lhs.position(), Self::Dot(x, _) | Self::Index(x, _) => x.lhs.position(),
Self::Custom(x) => x.pos, Self::Custom(_, pos) => *pos,
} }
} }
@ -1074,16 +1062,16 @@ impl Expr {
Self::CharConstant(_, pos) => *pos = new_pos, Self::CharConstant(_, pos) => *pos = new_pos,
Self::StringConstant(x) => x.pos = new_pos, Self::StringConstant(x) => x.pos = new_pos,
Self::FnPointer(x) => x.pos = new_pos, Self::FnPointer(x) => x.pos = new_pos,
Self::Array(x) => x.1 = new_pos, Self::Array(_, pos) => *pos = new_pos,
Self::Map(x) => x.1 = new_pos, Self::Map(_, pos) => *pos = new_pos,
Self::Variable(x) => (x.0).pos = new_pos, Self::Variable(x) => (x.0).pos = new_pos,
Self::Property(x) => (x.0).pos = new_pos, Self::Property(x) => (x.0).pos = new_pos,
Self::Stmt(_, pos) => *pos = new_pos, Self::Stmt(_, pos) => *pos = new_pos,
Self::FnCall(x) => x.pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos,
Self::And(x) | Self::Or(x) | Self::In(x) => x.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::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos,
Self::Dot(x) | Self::Index(x) => x.pos = new_pos, Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
Self::Custom(x) => x.pos = new_pos, Self::Custom(_, pos) => *pos = new_pos,
} }
self self
@ -1096,9 +1084,11 @@ impl Expr {
match self { match self {
Self::Expr(x) => x.is_pure(), 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() x.lhs.is_pure() && x.rhs.is_pure()
} }
@ -1136,13 +1126,13 @@ impl Expr {
| Self::Unit(_) => true, | Self::Unit(_) => true,
// An array literal is literal if all items are literals // 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 // 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 // Check in expression
Self::In(x) => match (&x.lhs, &x.rhs) { Self::In(x, _) => match (&x.lhs, &x.rhs) {
(Self::StringConstant(_), Self::StringConstant(_)) (Self::StringConstant(_), Self::StringConstant(_))
| (Self::CharConstant(_, _), Self::StringConstant(_)) => true, | (Self::CharConstant(_, _), Self::StringConstant(_)) => true,
_ => false, _ => false,
@ -1169,13 +1159,13 @@ impl Expr {
| Self::Unit(_) => true, | Self::Unit(_) => true,
// An array literal is constant if all items are constant // 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 // 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 // Check in expression
Self::In(x) => match (&x.lhs, &x.rhs) { Self::In(x, _) => match (&x.lhs, &x.rhs) {
(Self::StringConstant(_), Self::StringConstant(_)) (Self::StringConstant(_), Self::StringConstant(_))
| (Self::CharConstant(_, _), Self::StringConstant(_)) => true, | (Self::CharConstant(_, _), Self::StringConstant(_)) => true,
_ => false, _ => false,
@ -1196,20 +1186,20 @@ impl Expr {
Self::IntegerConstant(_, _) Self::IntegerConstant(_, _)
| Self::CharConstant(_, _) | Self::CharConstant(_, _)
| Self::FnPointer(_) | Self::FnPointer(_)
| Self::In(_) | Self::In(_, _)
| Self::And(_) | Self::And(_, _)
| Self::Or(_) | Self::Or(_, _)
| Self::True(_) | Self::True(_)
| Self::False(_) | Self::False(_)
| Self::Unit(_) => false, | Self::Unit(_) => false,
Self::StringConstant(_) Self::StringConstant(_)
| Self::Stmt(_, _) | Self::Stmt(_, _)
| Self::FnCall(_) | Self::FnCall(_, _)
| Self::Dot(_) | Self::Dot(_, _)
| Self::Index(_) | Self::Index(_, _)
| Self::Array(_) | Self::Array(_, _)
| Self::Map(_) => match token { | Self::Map(_, _) => match token {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => true, Token::LeftBracket => true,
_ => false, _ => false,
@ -1231,7 +1221,7 @@ impl Expr {
_ => false, _ => false,
}, },
Self::Custom(_) => false, Self::Custom(_, _) => false,
} }
} }

View File

@ -879,8 +879,8 @@ impl Engine {
let is_ref = target.is_ref(); let is_ref = target.is_ref();
let next_chain = match rhs { let next_chain = match rhs {
Expr::Index(_) => ChainType::Index, Expr::Index(_, _) => ChainType::Index,
Expr::Dot(_) => ChainType::Dot, Expr::Dot(_, _) => ChainType::Dot,
_ => ChainType::None, _ => ChainType::None,
}; };
@ -894,7 +894,7 @@ impl Engine {
match rhs { match rhs {
// xxx[idx].expr... | xxx[idx][expr]... // 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_pos = x.lhs.position();
let idx_val = idx_val.as_value(); let idx_val = idx_val.as_value();
let obj_ptr = &mut self.get_indexed_mut( 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, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain, level,
new_val, new_val,
) )
.map_err(|err| err.fill_position(x.pos)) .map_err(|err| err.fill_position(*x_pos))
} }
// xxx[rhs] = new_val // xxx[rhs] = new_val
_ if new_val.is_some() => { _ if new_val.is_some() => {
@ -968,11 +968,10 @@ impl Engine {
ChainType::Dot => { ChainType::Dot => {
match rhs { match rhs {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(x) if x.namespace.is_none() => { Expr::FnCall(x, pos) if x.namespace.is_none() => {
let FnCallInfo { let FnCallInfo {
name, name,
native_only: native, native_only: native,
pos,
hash, hash,
def_value, def_value,
.. ..
@ -986,7 +985,7 @@ impl Engine {
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(), Expr::FnCall(_, _) => unreachable!(),
// {xxx:map}.id = ??? // {xxx:map}.id = ???
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => { Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
let IdentX { name, pos } = &x.0; let IdentX { name, pos } = &x.0;
@ -1031,7 +1030,7 @@ impl Engine {
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
} }
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
Expr::Index(x) | Expr::Dot(x) if target.is::<Map>() => { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
let mut val = match &x.lhs { let mut val = match &x.lhs {
Expr::Property(p) => { Expr::Property(p) => {
let IdentX { name, pos } = &p.0; let IdentX { name, pos } = &p.0;
@ -1041,11 +1040,10 @@ impl Engine {
)? )?
} }
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
Expr::FnCall(x) if x.namespace.is_none() => { Expr::FnCall(x, pos) if x.namespace.is_none() => {
let FnCallInfo { let FnCallInfo {
name, name,
native_only: native, native_only: native,
pos,
hash, hash,
def_value, def_value,
.. ..
@ -1061,7 +1059,7 @@ impl Engine {
val.into() val.into()
} }
// {xxx:map}.module::fn_name(...) - syntax error // {xxx:map}.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(), Expr::FnCall(_, _) => unreachable!(),
// Others - syntax error // Others - syntax error
_ => unreachable!(), _ => unreachable!(),
}; };
@ -1070,10 +1068,10 @@ impl Engine {
state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, level, state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, level,
new_val, 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 // 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 { match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => { Expr::Property(p) => {
@ -1101,7 +1099,7 @@ impl Engine {
level, level,
new_val, 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 // Feed the value back via a setter just in case it has been updated
if updated || may_be_changed { if updated || may_be_changed {
@ -1117,7 +1115,7 @@ impl Engine {
EvalAltResult::ErrorDotExpr(_, _) => { EvalAltResult::ErrorDotExpr(_, _) => {
Ok(Default::default()) Ok(Default::default())
} }
_ => Err(err.fill_position(x.pos)), _ => Err(err.fill_position(*x_pos)),
}, },
)?; )?;
} }
@ -1125,11 +1123,10 @@ impl Engine {
Ok((result, may_be_changed)) Ok((result, may_be_changed))
} }
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f) if f.namespace.is_none() => { Expr::FnCall(f, pos) if f.namespace.is_none() => {
let FnCallInfo { let FnCallInfo {
name, name,
native_only: native, native_only: native,
pos,
hash, hash,
def_value, def_value,
.. ..
@ -1152,7 +1149,7 @@ impl Engine {
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(), Expr::FnCall(_, _) => unreachable!(),
// Others - syntax error // Others - syntax error
_ => unreachable!(), _ => unreachable!(),
} }
@ -1183,12 +1180,12 @@ impl Engine {
BinaryExpr { BinaryExpr {
lhs: dot_lhs, lhs: dot_lhs,
rhs: dot_rhs, rhs: dot_rhs,
pos: op_pos,
}, },
chain_type, chain_type,
op_pos,
) = match expr { ) = match expr {
Expr::Index(x) => (x.as_ref(), ChainType::Index), Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
Expr::Dot(x) => (x.as_ref(), ChainType::Dot), Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -1235,7 +1232,7 @@ impl Engine {
state, lib, &mut None, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val, state, lib, &mut None, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| err.fill_position(*op_pos)) .map_err(|err| err.fill_position(op_pos))
} }
// {expr}.??? = ??? or {expr}[???] = ??? // {expr}.??? = ??? or {expr}[???] = ???
_ if new_val.is_some() => unreachable!(), _ if new_val.is_some() => unreachable!(),
@ -1247,7 +1244,7 @@ impl Engine {
state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val, state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| err.fill_position(*op_pos)) .map_err(|err| err.fill_position(op_pos))
} }
} }
} }
@ -1272,7 +1269,7 @@ impl Engine {
.map_err(|err| err.fill_position(expr.position()))?; .map_err(|err| err.fill_position(expr.position()))?;
match expr { match expr {
Expr::FnCall(x) if x.namespace.is_none() => { Expr::FnCall(x, _) if x.namespace.is_none() => {
let arg_values = x let arg_values = x
.args .args
.iter() .iter()
@ -1283,23 +1280,24 @@ impl Engine {
idx_values.push(arg_values.into()); idx_values.push(arg_values.into());
} }
Expr::FnCall(_) => unreachable!(), Expr::FnCall(_, _) => unreachable!(),
Expr::Property(_) => idx_values.push(IndexChainValue::None), 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(); let BinaryExpr { lhs, rhs, .. } = x.as_ref();
// Evaluate in left-to-right order // Evaluate in left-to-right order
let lhs_val = match lhs { let lhs_val = match lhs {
Expr::Property(_) => IndexChainValue::None, Expr::Property(_) => IndexChainValue::None,
Expr::FnCall(x) if chain_type == ChainType::Dot && x.namespace.is_none() => x Expr::FnCall(x, _) if chain_type == ChainType::Dot && x.namespace.is_none() => {
.args x.args
.iter() .iter()
.map(|arg_expr| { .map(|arg_expr| {
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
}) })
.collect::<Result<StaticVec<Dynamic>, _>>()? .collect::<Result<StaticVec<Dynamic>, _>>()?
.into(), .into()
Expr::FnCall(_) => unreachable!(), }
Expr::FnCall(_, _) => unreachable!(),
_ => self _ => self
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?
.into(), .into(),
@ -1307,8 +1305,8 @@ impl Engine {
// Push in reverse order // Push in reverse order
let chain_type = match expr { let chain_type = match expr {
Expr::Index(_) => ChainType::Index, Expr::Index(_, _) => ChainType::Index,
Expr::Dot(_) => ChainType::Dot, Expr::Dot(_, _) => ChainType::Dot,
_ => unreachable!(), _ => unreachable!(),
}; };
self.eval_indexed_chain( self.eval_indexed_chain(
@ -1456,7 +1454,7 @@ impl Engine {
match rhs_value { match rhs_value {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(mut rhs_value)) => { Dynamic(Union::Array(mut rhs_value)) => {
let op = "=="; const OP_FUNC: &str = "==";
// Call the `==` operator to compare each value // Call the `==` operator to compare each value
let def_value = Some(false.into()); let def_value = Some(false.into());
@ -1465,10 +1463,11 @@ impl Engine {
let args = &mut [&mut lhs_value.clone(), value]; let args = &mut [&mut lhs_value.clone(), value];
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. // 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 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()))? .map_err(|err| err.fill_position(rhs.position()))?
.0 .0
.as_bool() .as_bool()
@ -1482,13 +1481,13 @@ impl Engine {
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(rhs_value)) => match lhs_value { 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::Str(s)) => Ok(rhs_value.contains_key(&s).into()),
Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()),
_ => EvalAltResult::ErrorInExpr(lhs.position()).into(), _ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
}, },
Dynamic(Union::Str(rhs_value)) => match lhs_value { 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::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()),
Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()), Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()),
_ => EvalAltResult::ErrorInExpr(lhs.position()).into(), _ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
@ -1512,12 +1511,12 @@ impl Engine {
.map_err(|err| err.fill_position(expr.position()))?; .map_err(|err| err.fill_position(expr.position()))?;
let result = match expr { let result = match expr {
Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x.as_ref(), level), Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x, level),
Expr::IntegerConstant(x, _) => Ok((*x).into()), Expr::IntegerConstant(x, _) => Ok((*x).into()),
#[cfg(not(feature = "no_float"))] #[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::StringConstant(x) => Ok(x.name.clone().into()),
Expr::CharConstant(x, _) => Ok((*x).into()), Expr::CharConstant(x, _) => Ok((*x).into()),
Expr::FnPointer(x) => { Expr::FnPointer(x) => {
Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into()) Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into())
@ -1541,26 +1540,26 @@ impl Engine {
// lhs[idx_expr] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_) => { Expr::Index(_, _) => {
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
} }
// lhs.dot_rhs // lhs.dot_rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_) => { Expr::Dot(_, _) => {
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(x) => Ok(Dynamic(Union::Array(Box::new( Expr::Array(x, _) => Ok(Dynamic(Union::Array(Box::new(
x.0.iter() x.iter()
.map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level)) .map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level))
.collect::<Result<Vec<_>, _>>()?, .collect::<Result<Vec<_>, _>>()?,
)))), )))),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new( Expr::Map(x, _) => Ok(Dynamic(Union::Map(Box::new(
x.0.iter() x.iter()
.map(|(key, expr)| { .map(|(key, expr)| {
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
.map(|val| (key.name.clone(), val)) .map(|val| (key.name.clone(), val))
@ -1569,12 +1568,11 @@ impl Engine {
)))), )))),
// Normal function call // Normal function call
Expr::FnCall(x) if x.namespace.is_none() => { Expr::FnCall(x, pos) if x.namespace.is_none() => {
let FnCallInfo { let FnCallInfo {
name, name,
native_only: native, native_only: native,
capture: cap_scope, capture: cap_scope,
pos,
hash, hash,
args, args,
def_value, def_value,
@ -1589,10 +1587,9 @@ impl Engine {
} }
// Module-qualified function call // Module-qualified function call
Expr::FnCall(x) if x.namespace.is_some() => { Expr::FnCall(x, pos) if x.namespace.is_some() => {
let FnCallInfo { let FnCallInfo {
name, name,
pos,
namespace, namespace,
hash, hash,
args, args,
@ -1606,11 +1603,11 @@ impl Engine {
.map_err(|err| err.fill_position(*pos)) .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) self.eval_in_expr(scope, mods, state, lib, this_ptr, &x.lhs, &x.rhs, level)
} }
Expr::And(x) => { Expr::And(x, _) => {
Ok((self Ok((self
.eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)?
.as_bool() .as_bool()
@ -1623,7 +1620,7 @@ impl Engine {
.into()) .into())
} }
Expr::Or(x) => { Expr::Or(x, _) => {
Ok((self Ok((self
.eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)?
.as_bool() .as_bool()
@ -1640,8 +1637,7 @@ impl Engine {
Expr::False(_) => Ok(false.into()), Expr::False(_) => Ok(false.into()),
Expr::Unit(_) => Ok(().into()), Expr::Unit(_) => Ok(().into()),
Expr::Custom(custom) => { Expr::Custom(custom, _) => {
let func = custom.func();
let expressions = custom let expressions = custom
.keywords() .keywords()
.iter() .iter()
@ -1656,7 +1652,7 @@ impl Engine {
this_ptr, this_ptr,
level, level,
}; };
func(&mut context, &expressions) (custom.func)(&mut context, &expressions)
} }
_ => unreachable!(), _ => unreachable!(),
@ -1825,7 +1821,7 @@ impl Engine {
Expr::Variable(_) => unreachable!(), Expr::Variable(_) => unreachable!(),
// idx_lhs[idx_expr] op= rhs // idx_lhs[idx_expr] op= rhs
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_) => { Expr::Index(_, _) => {
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
)?; )?;
@ -1833,7 +1829,7 @@ impl Engine {
} }
// dot_lhs.dot_rhs op= rhs // dot_lhs.dot_rhs op= rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_) => { Expr::Dot(_, _) => {
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
)?; )?;

View File

@ -451,15 +451,14 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
// lhs.rhs // lhs.rhs
#[cfg(not(feature = "no_object"))] #[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 // map.string
(Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => { (Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
let prop = &p.0.name; let prop = &p.0.name;
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
let pos = m.1; m.into_iter().find(|(x, _)| &x.name == prop)
m.0.into_iter().find(|(x, _)| &x.name == prop)
.map(|(_, mut expr)| { expr.set_position(pos); expr }) .map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
@ -467,31 +466,29 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
(lhs, rhs) => Expr::Dot(Box::new(BinaryExpr { (lhs, rhs) => Expr::Dot(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state), lhs: optimize_expr(lhs, state),
rhs: optimize_expr(rhs, state), rhs: optimize_expr(rhs, state),
pos: x.pos }), dot_pos)
}))
} }
// lhs[rhs] // lhs[rhs]
#[cfg(not(feature = "no_index"))] #[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] // array[int]
(Expr::Array(mut a), Expr::IntegerConstant(i, _)) (Expr::Array(mut a, pos), Expr::IntegerConstant(i, _))
if i >= 0 && (i as usize) < a.0.len() && a.0.iter().all(Expr::is_pure) => if i >= 0 && (i as usize) < a.len() && a.iter().all(Expr::is_pure) =>
{ {
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
let mut expr = a.0.remove(i as usize); let mut expr = a.remove(i as usize);
expr.set_position(a.1); expr.set_position(pos);
expr expr
} }
// map[string] // 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. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
let pos = m.1; m.into_iter().find(|(x, _)| x.name == s.name)
m.0.into_iter().find(|(x, _)| x.name == s.name)
.map(|(_, mut expr)| { expr.set_position(pos); expr }) .map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
@ -505,21 +502,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr { (lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state), lhs: optimize_expr(lhs, state),
rhs: optimize_expr(rhs, state), rhs: optimize_expr(rhs, state),
pos: x.pos }), idx_pos),
})),
}, },
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_index"))] #[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)) .into_iter().map(|expr| optimize_expr(expr, state))
.collect(), a.1))), .collect()), pos),
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(m) => Expr::Map(Box::new((m.0 Expr::Map(m, pos) => Expr::Map(Box::new(m
.into_iter().map(|(key, expr)| (key, optimize_expr(expr, state))) .into_iter().map(|(key, expr)| (key, optimize_expr(expr, state)))
.collect(), m.1))), .collect()), pos),
// lhs in rhs // lhs in rhs
Expr::In(x) => match (x.lhs, x.rhs) { Expr::In(x, in_pos) => match (x.lhs, x.rhs) {
// "xxx" in "xxxxx" // "xxx" in "xxxxx"
(Expr::StringConstant(a), Expr::StringConstant(b)) => { (Expr::StringConstant(a), Expr::StringConstant(b)) => {
state.set_dirty(); state.set_dirty();
@ -531,20 +527,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
if b.name.contains(a) { Expr::True(pos) } else { Expr::False(pos) } if b.name.contains(a) { Expr::True(pos) } else { Expr::False(pos) }
} }
// "xxx" in #{...} // "xxx" in #{...}
(Expr::StringConstant(a), Expr::Map(b)) => { (Expr::StringConstant(a), Expr::Map(b, _)) => {
state.set_dirty(); 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) Expr::True(a.pos)
} else { } else {
Expr::False(a.pos) Expr::False(a.pos)
} }
} }
// 'x' in #{...} // 'x' in #{...}
(Expr::CharConstant(a, pos), Expr::Map(b)) => { (Expr::CharConstant(a, pos), Expr::Map(b, _)) => {
state.set_dirty(); state.set_dirty();
let ch = a.to_string(); let ch = a.to_string();
if b.0.iter().find(|(x, _)| x.name == &ch).is_some() { if b.iter().find(|(x, _)| x.name == &ch).is_some() {
Expr::True(pos) Expr::True(pos)
} else { } else {
Expr::False(pos) Expr::False(pos)
@ -554,11 +550,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
(lhs, rhs) => Expr::In(Box::new(BinaryExpr { (lhs, rhs) => Expr::In(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state), lhs: optimize_expr(lhs, state),
rhs: optimize_expr(rhs, state), rhs: optimize_expr(rhs, state),
pos: x.pos }), in_pos),
})),
}, },
// lhs && rhs // lhs && rhs
Expr::And(x) => match (x.lhs, x.rhs) { Expr::And(x, and_pos) => match (x.lhs, x.rhs) {
// true && rhs -> rhs // true && rhs -> rhs
(Expr::True(_), rhs) => { (Expr::True(_), rhs) => {
state.set_dirty(); state.set_dirty();
@ -578,11 +573,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
(lhs, rhs) => Expr::And(Box::new(BinaryExpr { (lhs, rhs) => Expr::And(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state), lhs: optimize_expr(lhs, state),
rhs: optimize_expr(rhs, state), rhs: optimize_expr(rhs, state),
pos: x.pos }), and_pos),
})),
}, },
// lhs || rhs // lhs || rhs
Expr::Or(x) => match (x.lhs, x.rhs) { Expr::Or(x, or_pos) => match (x.lhs, x.rhs) {
// false || rhs -> rhs // false || rhs -> rhs
(Expr::False(_), rhs) => { (Expr::False(_), rhs) => {
state.set_dirty(); state.set_dirty();
@ -602,25 +596,24 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
(lhs, rhs) => Expr::Or(Box::new(BinaryExpr { (lhs, rhs) => Expr::Or(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state), lhs: optimize_expr(lhs, state),
rhs: optimize_expr(rhs, state), rhs: optimize_expr(rhs, state),
pos: x.pos }), or_pos),
})),
}, },
// Do not call some special keywords // Do not call some special keywords
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => { Expr::FnCall(mut x, pos) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => {
x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x) Expr::FnCall(x, pos)
} }
// Call built-in operators // Call built-in operators
Expr::FnCall(mut x) Expr::FnCall(mut x, pos)
if x.namespace.is_none() // Non-qualified if x.namespace.is_none() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations && state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.args.len() == 2 // binary call && x.args.len() == 2 // binary call
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
&& !is_valid_identifier(x.name.chars()) // cannot be scripted && !is_valid_identifier(x.name.chars()) // cannot be scripted
=> { => {
let FnCallInfo { name, pos, args, .. } = x.as_mut(); let FnCallInfo { name, args, .. } = x.as_mut();
let arg_values: StaticVec<_> = args.iter().map(|e| e.get_constant_value().unwrap()).collect(); let arg_values: StaticVec<_> = args.iter().map(|e| e.get_constant_value().unwrap()).collect();
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
@ -629,7 +622,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
if !state.engine.has_override_by_name_and_arguments(state.lib, name, arg_types.as_ref(), false) { if !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]) if let Some(expr) = run_builtin_binary_op(name, &arg_values[0], &arg_values[1])
.ok().flatten() .ok().flatten()
.and_then(|result| map_dynamic_to_expr(result, *pos)) .and_then(|result| map_dynamic_to_expr(result, pos))
{ {
state.set_dirty(); state.set_dirty();
return expr; return expr;
@ -637,16 +630,16 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
} }
x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x) Expr::FnCall(x, pos)
} }
// Eagerly call functions // Eagerly call functions
Expr::FnCall(mut x) Expr::FnCall(mut x, pos)
if x.namespace.is_none() // Non-qualified if x.namespace.is_none() // Non-qualified
&& state.optimization_level == OptimizationLevel::Full // full optimizations && state.optimization_level == OptimizationLevel::Full // full optimizations
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
=> { => {
let FnCallInfo { name, pos, args, def_value, .. } = x.as_mut(); let FnCallInfo { name, args, def_value, .. } = x.as_mut();
// First search for script-defined functions (can override built-in) // First search for script-defined functions (can override built-in)
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -675,7 +668,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
def_value.map(|v| v.into()) 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(); state.set_dirty();
return expr; return expr;
@ -683,13 +676,13 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
} }
x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x) Expr::FnCall(x, pos)
} }
// id(args ..) -> optimize function call arguments // id(args ..) -> optimize function call arguments
Expr::FnCall(mut x) => { Expr::FnCall(mut x, pos) => {
x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect(); x.args = x.args.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x) Expr::FnCall(x, pos)
} }
// constant-name // constant-name
@ -703,10 +696,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
} }
// Custom syntax // 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(), keywords: x.keywords.into_iter().map(|expr| optimize_expr(expr, state)).collect(),
..*x ..*x
})), }), pos),
// All other expressions - skip // All other expressions - skip
expr => expr, expr => expr,

View File

@ -307,15 +307,17 @@ fn parse_fn_call(
calc_script_fn_hash(empty(), &id, 0) calc_script_fn_hash(empty(), &id, 0)
}; };
return Ok(Expr::FnCall(Box::new(FnCallInfo { return Ok(Expr::FnCall(
name: id.into(), Box::new(FnCallInfo {
capture, name: id.into(),
namespace, capture,
hash: hash_script, namespace,
args, hash: hash_script,
pos: settings.pos, args,
..Default::default() ..Default::default()
}))); }),
settings.pos,
));
} }
// id... // id...
_ => (), _ => (),
@ -352,15 +354,17 @@ fn parse_fn_call(
calc_script_fn_hash(empty(), &id, args.len()) calc_script_fn_hash(empty(), &id, args.len())
}; };
return Ok(Expr::FnCall(Box::new(FnCallInfo { return Ok(Expr::FnCall(
name: id.into(), Box::new(FnCallInfo {
capture, name: id.into(),
namespace, capture,
hash: hash_script, namespace,
args, hash: hash_script,
pos: settings.pos, args,
..Default::default() ..Default::default()
}))); }),
settings.pos,
));
} }
// id(...args, // id(...args,
(Token::Comma, _) => { (Token::Comma, _) => {
@ -414,9 +418,9 @@ fn parse_index_chain(
.into_err(*pos)) .into_err(*pos))
} }
Expr::IntegerConstant(_, pos) => match lhs { Expr::IntegerConstant(_, pos) => match lhs {
Expr::Array(_) | Expr::StringConstant(_) => (), Expr::Array(_, _) | Expr::StringConstant(_) => (),
Expr::Map(_) => { Expr::Map(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Object map access expects string index, not a number".into(), "Object map access expects string index, not a number".into(),
) )
@ -432,9 +436,9 @@ fn parse_index_chain(
} }
Expr::CharConstant(_, _) Expr::CharConstant(_, _)
| Expr::And(_) | Expr::And(_, _)
| Expr::Or(_) | Expr::Or(_, _)
| Expr::In(_) | Expr::In(_, _)
| Expr::True(_) | Expr::True(_)
| Expr::False(_) | Expr::False(_)
| Expr::Unit(_) => { | Expr::Unit(_) => {
@ -449,9 +453,9 @@ fn parse_index_chain(
// lhs[string] // lhs[string]
Expr::StringConstant(x) => match lhs { Expr::StringConstant(x) => match lhs {
Expr::Map(_) => (), Expr::Map(_, _) => (),
Expr::Array(_) | Expr::StringConstant(_) => { Expr::Array(_, _) | Expr::StringConstant(_) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array or string expects numeric index, not a string".into(), "Array or string expects numeric index, not a string".into(),
) )
@ -467,9 +471,9 @@ fn parse_index_chain(
} }
Expr::CharConstant(_, _) Expr::CharConstant(_, _)
| Expr::And(_) | Expr::And(_, _)
| Expr::Or(_) | Expr::Or(_, _)
| Expr::In(_) | Expr::In(_, _)
| Expr::True(_) | Expr::True(_)
| Expr::False(_) | Expr::False(_)
| Expr::Unit(_) => { | Expr::Unit(_) => {
@ -505,7 +509,7 @@ fn parse_index_chain(
.into_err(x.position())) .into_err(x.position()))
} }
// lhs[??? && ???], lhs[??? || ???], lhs[??? in ???] // 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( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(), "Array access expects integer index, not a boolean".into(),
) )
@ -537,11 +541,10 @@ fn parse_index_chain(
let idx_expr = let idx_expr =
parse_index_chain(input, state, lib, idx_expr, settings.level_up())?; parse_index_chain(input, state, lib, idx_expr, settings.level_up())?;
// Indexing binds to right // Indexing binds to right
Ok(Expr::Index(Box::new(BinaryExpr { Ok(Expr::Index(
lhs, Box::new(BinaryExpr { lhs, rhs: idx_expr }),
rhs: idx_expr, prev_pos,
pos: prev_pos, ))
})))
} }
// Otherwise terminate the indexing chain // Otherwise terminate the indexing chain
_ => { _ => {
@ -549,18 +552,19 @@ fn parse_index_chain(
// Terminate with an `Expr::Expr` wrapper to prevent the last index expression // 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 // 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. // dot expression/function call to be mis-parsed as following the indexing chain.
Expr::Index(_) | Expr::Dot(_) | Expr::FnCall(_) => { Expr::Index(_, _) | Expr::Dot(_, _) | Expr::FnCall(_, _) => {
Ok(Expr::Index(Box::new(BinaryExpr { Ok(Expr::Index(
lhs, Box::new(BinaryExpr {
rhs: Expr::Expr(Box::new(idx_expr)), lhs,
pos: settings.pos, rhs: Expr::Expr(Box::new(idx_expr)),
}))) }),
settings.pos,
))
} }
_ => Ok(Expr::Index(Box::new(BinaryExpr { _ => Ok(Expr::Index(
lhs, Box::new(BinaryExpr { lhs, rhs: idx_expr }),
rhs: idx_expr, settings.pos,
pos: settings.pos, )),
}))),
} }
} }
} }
@ -631,7 +635,7 @@ fn parse_array_literal(
}; };
} }
Ok(Expr::Array(Box::new((arr, settings.pos)))) Ok(Expr::Array(Box::new(arr), settings.pos))
} }
/// Parse a map literal. /// Parse a map literal.
@ -740,7 +744,7 @@ fn parse_map_literal(
}) })
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?; .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. /// Parse a primary expression.
@ -984,15 +988,17 @@ fn parse_unary(
let mut args = StaticVec::new(); let mut args = StaticVec::new();
args.push(expr); args.push(expr);
Ok(Expr::FnCall(Box::new(FnCallInfo { Ok(Expr::FnCall(
name: op.into(), Box::new(FnCallInfo {
native_only: true, name: op.into(),
namespace: None, native_only: true,
hash, namespace: None,
args, hash,
args,
..Default::default()
}),
pos, pos,
..Default::default() ))
})))
} }
} }
} }
@ -1011,15 +1017,17 @@ fn parse_unary(
let op = "!"; let op = "!";
let hash = calc_script_fn_hash(empty(), op, 1); let hash = calc_script_fn_hash(empty(), op, 1);
Ok(Expr::FnCall(Box::new(FnCallInfo { Ok(Expr::FnCall(
name: op.into(), Box::new(FnCallInfo {
native_only: true, name: op.into(),
hash, native_only: true,
args, hash,
def_value: Some(false), // NOT operator, when operating on invalid operand, defaults to false args,
def_value: Some(false), // NOT operator, when operating on invalid operand, defaults to false
..Default::default()
}),
pos, pos,
..Default::default() ))
})))
} }
// | ... // | ...
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -1098,7 +1106,7 @@ fn make_assignment_stmt<'a>(
} }
} }
// xxx[???] = rhs, xxx.??? = rhs // 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 // var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(x) if x.3.is_none() => { Expr::Variable(x) if x.3.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
@ -1132,7 +1140,7 @@ fn make_assignment_stmt<'a>(
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position())) Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
} }
// ??? && ??? = rhs, ??? || ??? = rhs // ??? && ??? = rhs, ??? || ??? = rhs
Expr::And(_) | Expr::Or(_) => { Expr::And(_, _) | Expr::Or(_, _) => {
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos)) Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos))
} }
// expr = rhs // expr = rhs
@ -1183,9 +1191,9 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
// idx_lhs[idx_expr].rhs // idx_lhs[idx_expr].rhs
// Attach dot chain to the bottom level of indexing chain // Attach dot chain to the bottom level of indexing chain
(Expr::Index(mut x), rhs) => { (Expr::Index(mut x, pos), rhs) => {
x.rhs = make_dot_expr(x.rhs, rhs, op_pos)?; x.rhs = make_dot_expr(x.rhs, rhs, op_pos)?;
Expr::Index(x) Expr::Index(x, pos)
} }
// lhs.id // lhs.id
(lhs, Expr::Variable(x)) if x.1.is_none() => { (lhs, Expr::Variable(x)) if x.1.is_none() => {
@ -1194,71 +1202,59 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
let setter = make_setter(&ident.name); let setter = make_setter(&ident.name);
let rhs = Expr::Property(Box::new((ident.into(), (getter, setter)))); let rhs = Expr::Property(Box::new((ident.into(), (getter, setter))));
Expr::Dot(Box::new(BinaryExpr { Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
lhs,
rhs,
pos: op_pos,
}))
} }
// lhs.module::id - syntax error // lhs.module::id - syntax error
(_, Expr::Variable(x)) if x.1.is_some() => { (_, Expr::Variable(x)) if x.1.is_some() => {
return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].1)); return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].1));
} }
// lhs.prop // lhs.prop
(lhs, prop @ Expr::Property(_)) => Expr::Dot(Box::new(BinaryExpr { (lhs, prop @ Expr::Property(_)) => {
lhs, Expr::Dot(Box::new(BinaryExpr { lhs, rhs: prop }), op_pos)
rhs: prop, }
pos: op_pos,
})),
// lhs.dot_lhs.dot_rhs // lhs.dot_lhs.dot_rhs
(lhs, Expr::Dot(x)) => { (lhs, Expr::Dot(x, pos)) => {
let rhs = Expr::Dot(Box::new(BinaryExpr { let rhs = Expr::Dot(
lhs: x.lhs.into_property(), Box::new(BinaryExpr {
rhs: x.rhs, lhs: x.lhs.into_property(),
pos: x.pos, rhs: x.rhs,
})); }),
Expr::Dot(Box::new(BinaryExpr { pos,
lhs, );
rhs, Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
pos: op_pos,
}))
} }
// lhs.idx_lhs[idx_rhs] // lhs.idx_lhs[idx_rhs]
(lhs, Expr::Index(x)) => { (lhs, Expr::Index(x, pos)) => {
let rhs = Expr::Index(Box::new(BinaryExpr { let rhs = Expr::Index(
lhs: x.lhs.into_property(), Box::new(BinaryExpr {
rhs: x.rhs, lhs: x.lhs.into_property(),
pos: x.pos, rhs: x.rhs,
})); }),
Expr::Dot(Box::new(BinaryExpr { pos,
lhs, );
rhs, Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
pos: op_pos,
}))
} }
// lhs.Fn() or lhs.eval() // lhs.Fn() or lhs.eval()
(_, Expr::FnCall(x)) (_, Expr::FnCall(x, pos))
if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) => if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) =>
{ {
return Err(PERR::BadInput(format!( return Err(PERR::BadInput(format!(
"'{}' should not be called in method style. Try {}(...);", "'{}' should not be called in method style. Try {}(...);",
x.name, x.name x.name, x.name
)) ))
.into_err(x.pos)); .into_err(pos));
} }
// lhs.func!(...) // lhs.func!(...)
(_, Expr::FnCall(x)) if x.capture => { (_, Expr::FnCall(x, pos)) if x.capture => {
return Err(PERR::MalformedCapture( return Err(PERR::MalformedCapture(
"method-call style does not support capturing".into(), "method-call style does not support capturing".into(),
) )
.into_err(x.pos)); .into_err(pos));
} }
// lhs.func(...) // lhs.func(...)
(lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new(BinaryExpr { (lhs, func @ Expr::FnCall(_, _)) => {
lhs, Expr::Dot(Box::new(BinaryExpr { lhs, rhs: func }), op_pos)
rhs: func, }
pos: op_pos,
})),
// lhs.rhs // lhs.rhs
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())), (_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
}) })
@ -1268,9 +1264,9 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> { fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
match (&lhs, &rhs) { match (&lhs, &rhs) {
(_, x @ Expr::IntegerConstant(_, _)) (_, x @ Expr::IntegerConstant(_, _))
| (_, x @ Expr::And(_)) | (_, x @ Expr::And(_, _))
| (_, x @ Expr::Or(_)) | (_, x @ Expr::Or(_, _))
| (_, x @ Expr::In(_)) | (_, x @ Expr::In(_, _))
| (_, x @ Expr::True(_)) | (_, x @ Expr::True(_))
| (_, x @ Expr::False(_)) | (_, x @ Expr::False(_))
| (_, x @ Expr::Unit(_)) => { | (_, x @ Expr::Unit(_)) => {
@ -1309,9 +1305,9 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
} }
// (??? && ???) in "xxxx", (??? || ???) in "xxxx", (??? in ???) in "xxxx", // (??? && ???) in "xxxx", (??? || ???) in "xxxx", (??? in ???) in "xxxx",
// true in "xxxx", false in "xxxx" // true in "xxxx", false in "xxxx"
(x @ Expr::And(_), Expr::StringConstant(_)) (x @ Expr::And(_, _), Expr::StringConstant(_))
| (x @ Expr::Or(_), Expr::StringConstant(_)) | (x @ Expr::Or(_, _), Expr::StringConstant(_))
| (x @ Expr::In(_), Expr::StringConstant(_)) | (x @ Expr::In(_, _), Expr::StringConstant(_))
| (x @ Expr::True(_), Expr::StringConstant(_)) | (x @ Expr::True(_), Expr::StringConstant(_))
| (x @ Expr::False(_), Expr::StringConstant(_)) => { | (x @ Expr::False(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
@ -1320,14 +1316,14 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
.into_err(x.position())) .into_err(x.position()))
} }
// [???, ???, ???] in "xxxx" // [???, ???, ???] in "xxxx"
(x @ Expr::Array(_), Expr::StringConstant(_)) => { (x @ Expr::Array(_, _), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not an array".into(), "'in' expression for a string expects a string, not an array".into(),
) )
.into_err(x.position())) .into_err(x.position()))
} }
// #{...} in "xxxx" // #{...} in "xxxx"
(x @ Expr::Map(_), Expr::StringConstant(_)) => { (x @ Expr::Map(_, _), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not an object map".into(), "'in' expression for a string expects a string, not an object map".into(),
) )
@ -1342,18 +1338,19 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
} }
// "xxx" in #{...}, 'x' in #{...} - OK! // "xxx" in #{...}, 'x' in #{...} - OK!
(Expr::StringConstant(_), Expr::Map(_)) | (Expr::CharConstant(_, _), Expr::Map(_)) => (), (Expr::StringConstant(_), Expr::Map(_, _))
| (Expr::CharConstant(_, _), Expr::Map(_, _)) => (),
// 123.456 in #{...} // 123.456 in #{...}
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
(x @ Expr::FloatConstant(_, _), Expr::Map(_)) => { (x @ Expr::FloatConstant(_, _), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a float".into(), "'in' expression for an object map expects a string, not a float".into(),
) )
.into_err(x.position())) .into_err(x.position()))
} }
// 123 in #{...} // 123 in #{...}
(x @ Expr::IntegerConstant(_, _), Expr::Map(_)) => { (x @ Expr::IntegerConstant(_, _), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a number".into(), "'in' expression for an object map expects a string, not a number".into(),
) )
@ -1361,32 +1358,32 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
} }
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...}, // (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
// true in #{...}, false in #{...} // true in #{...}, false in #{...}
(x @ Expr::And(_), Expr::Map(_)) (x @ Expr::And(_, _), Expr::Map(_, _))
| (x @ Expr::Or(_), Expr::Map(_)) | (x @ Expr::Or(_, _), Expr::Map(_, _))
| (x @ Expr::In(_), Expr::Map(_)) | (x @ Expr::In(_, _), Expr::Map(_, _))
| (x @ Expr::True(_), Expr::Map(_)) | (x @ Expr::True(_), Expr::Map(_, _))
| (x @ Expr::False(_), Expr::Map(_)) => { | (x @ Expr::False(_), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a boolean".into(), "'in' expression for an object map expects a string, not a boolean".into(),
) )
.into_err(x.position())) .into_err(x.position()))
} }
// [???, ???, ???] in #{..} // [???, ???, ???] in #{..}
(x @ Expr::Array(_), Expr::Map(_)) => { (x @ Expr::Array(_, _), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not an array".into(), "'in' expression for an object map expects a string, not an array".into(),
) )
.into_err(x.position())) .into_err(x.position()))
} }
// #{...} in #{..} // #{...} in #{..}
(x @ Expr::Map(_), Expr::Map(_)) => { (x @ Expr::Map(_, _), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not an object map".into(), "'in' expression for an object map expects a string, not an object map".into(),
) )
.into_err(x.position())) .into_err(x.position()))
} }
// () in #{...} // () in #{...}
(x @ Expr::Unit(_), Expr::Map(_)) => { (x @ Expr::Unit(_), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not ()".into(), "'in' expression for an object map expects a string, not ()".into(),
) )
@ -1396,11 +1393,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
_ => (), _ => (),
} }
Ok(Expr::In(Box::new(BinaryExpr { Ok(Expr::In(Box::new(BinaryExpr { lhs, rhs }), op_pos))
lhs,
rhs,
pos: op_pos,
})))
} }
/// Parse a binary expression. /// Parse a binary expression.
@ -1488,7 +1481,6 @@ fn parse_binary_op(
name: op, name: op,
native_only: true, native_only: true,
capture: false, capture: false,
pos,
..Default::default() ..Default::default()
}; };
@ -1507,49 +1499,62 @@ fn parse_binary_op(
| Token::PowerOf | Token::PowerOf
| Token::Ampersand | Token::Ampersand
| Token::Pipe | Token::Pipe
| Token::XOr => Expr::FnCall(Box::new(FnCallInfo { | Token::XOr => Expr::FnCall(
hash, Box::new(FnCallInfo {
args, hash,
..op_base args,
})), ..op_base
}),
pos,
),
// '!=' defaults to true when passed invalid operands // '!=' defaults to true when passed invalid operands
Token::NotEqualsTo => Expr::FnCall(Box::new(FnCallInfo { Token::NotEqualsTo => Expr::FnCall(
hash, Box::new(FnCallInfo {
args, hash,
def_value: Some(true), args,
..op_base def_value: Some(true),
})), ..op_base
}),
pos,
),
// Comparison operators default to false when passed invalid operands // Comparison operators default to false when passed invalid operands
Token::EqualsTo Token::EqualsTo
| Token::LessThan | Token::LessThan
| Token::LessThanEqualsTo | Token::LessThanEqualsTo
| Token::GreaterThan | Token::GreaterThan
| Token::GreaterThanEqualsTo => Expr::FnCall(Box::new(FnCallInfo { | Token::GreaterThanEqualsTo => Expr::FnCall(
hash, Box::new(FnCallInfo {
args, hash,
def_value: cmp_def, args,
..op_base def_value: cmp_def,
})), ..op_base
}),
pos,
),
Token::Or => { Token::Or => {
let rhs = args.pop().unwrap(); let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap(); let current_lhs = args.pop().unwrap();
Expr::Or(Box::new(BinaryExpr { Expr::Or(
lhs: current_lhs, Box::new(BinaryExpr {
rhs, lhs: current_lhs,
rhs,
}),
pos, pos,
})) )
} }
Token::And => { Token::And => {
let rhs = args.pop().unwrap(); let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap(); let current_lhs = args.pop().unwrap();
Expr::And(Box::new(BinaryExpr { Expr::And(
lhs: current_lhs, Box::new(BinaryExpr {
rhs, lhs: current_lhs,
rhs,
}),
pos, pos,
})) )
} }
Token::In => { Token::In => {
let rhs = args.pop().unwrap(); let rhs = args.pop().unwrap();
@ -1566,12 +1571,15 @@ fn parse_binary_op(
Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => { Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => {
// Accept non-native functions for custom operators // Accept non-native functions for custom operators
Expr::FnCall(Box::new(FnCallInfo { Expr::FnCall(
hash, Box::new(FnCallInfo {
args, hash,
native_only: false, args,
..op_base native_only: false,
})) ..op_base
}),
pos,
)
} }
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)), op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
@ -1664,11 +1672,13 @@ fn parse_custom_syntax(
} }
} }
Ok(Expr::Custom(Box::new(CustomExpr { Ok(Expr::Custom(
keywords: exprs, Box::new(CustomExpr {
func: syntax.func.clone(), keywords: exprs,
func: syntax.func.clone(),
}),
pos, pos,
}))) ))
} }
/// Parse an expression. /// Parse an expression.
@ -2492,13 +2502,15 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1); let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1);
let expr = Expr::FnCall(Box::new(FnCallInfo { let expr = Expr::FnCall(
name: KEYWORD_FN_PTR_CURRY.into(), Box::new(FnCallInfo {
hash, name: KEYWORD_FN_PTR_CURRY.into(),
args, hash,
args,
..Default::default()
}),
pos, pos,
..Default::default() );
}));
// If there are captured variables, convert the entire expression into a statement block, // If there are captured variables, convert the entire expression into a statement block,
// then insert the relevant `Share` statements. // then insert the relevant `Share` statements.
@ -2799,10 +2811,10 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
.collect(); .collect();
if items.iter().all(Option::is_some) { if items.iter().all(Option::is_some) {
Some(Expr::Array(Box::new(( Some(Expr::Array(
items.into_iter().map(Option::unwrap).collect(), Box::new(items.into_iter().map(Option::unwrap).collect()),
pos, pos,
)))) ))
} else { } else {
None None
} }
@ -2815,13 +2827,15 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
.collect(); .collect();
if items.iter().all(|(_, expr)| expr.is_some()) { if items.iter().all(|(_, expr)| expr.is_some()) {
Some(Expr::Map(Box::new(( Some(Expr::Map(
items Box::new(
.into_iter() items
.map(|(k, expr)| (k, expr.unwrap())) .into_iter()
.collect(), .map(|(k, expr)| (k, expr.unwrap()))
.collect(),
),
pos, pos,
)))) ))
} else { } else {
None None
} }