Pack Stmt and Expr some more.

This commit is contained in:
Stephen Chung 2020-10-31 14:13:45 +08:00
parent 1e16cb03ce
commit 89811c8dfc
5 changed files with 334 additions and 239 deletions

View File

@ -559,6 +559,15 @@ pub struct IdentX {
pub pos: Position, pub pos: Position,
} }
impl From<Ident> for IdentX {
fn from(value: Ident) -> Self {
Self {
name: value.name.into(),
pos: value.pos,
}
}
}
impl IdentX { impl IdentX {
/// Create a new `Identifier`. /// Create a new `Identifier`.
pub fn new(name: impl Into<ImmutableString>, pos: Position) -> Self { pub fn new(name: impl Into<ImmutableString>, pos: Position) -> Self {
@ -626,7 +635,7 @@ pub enum Stmt {
Export(Vec<(Ident, Option<Ident>)>, Position), Export(Vec<(Ident, Option<Ident>)>, Position),
/// Convert a variable to shared. /// Convert a variable to shared.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Share(Ident), Share(Box<Ident>),
} }
impl Default for Stmt { impl Default for Stmt {
@ -670,7 +679,7 @@ impl Stmt {
Self::Export(_, pos) => *pos, Self::Export(_, pos) => *pos,
#[cfg(not(feature = "no_closure"))] #[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, Self::Export(_, pos) => *pos = new_pos,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(Ident { pos, .. }) => *pos = new_pos, Self::Share(x) => x.pos = new_pos,
} }
self self
@ -820,14 +829,13 @@ impl CustomExpr {
/// This type is volatile and may change. /// This type is volatile and may change.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[derive(Debug, PartialEq, PartialOrd, Clone)] #[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct FloatWrapper(pub FLOAT, pub Position); pub struct FloatWrapper(pub FLOAT);
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
impl Hash for FloatWrapper { impl Hash for FloatWrapper {
#[inline(always)] #[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.0.to_le_bytes()); state.write(&self.0.to_le_bytes());
self.1.hash(state);
} }
} }
@ -836,22 +844,50 @@ impl Neg for FloatWrapper {
type Output = Self; type Output = Self;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
Self(-self.0, self.1) Self(-self.0)
} }
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
impl From<(INT, Position)> for FloatWrapper { impl From<INT> for FloatWrapper {
fn from((value, pos): (INT, Position)) -> Self { fn from(value: INT) -> Self {
Self(value as FLOAT, pos) Self(value as FLOAT)
} }
} }
/// A binary expression structure. /// A binary expression structure.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub struct BinaryExpr { pub struct BinaryExpr {
/// LHS expression.
pub lhs: Expr, pub lhs: Expr,
/// RHS expression.
pub rhs: Expr, 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<Box<ModuleRef>>,
/// 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<Expr>,
/// 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<bool>,
/// Position of the function call.
pub pos: Position, pub pos: Position,
} }
@ -867,12 +903,12 @@ pub struct BinaryExpr {
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Expr { pub enum Expr {
/// Integer constant. /// Integer constant.
IntegerConstant(Box<(INT, Position)>), IntegerConstant(INT, Position),
/// Floating-point constant. /// Floating-point constant.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
FloatConstant(Box<FloatWrapper>), FloatConstant(FloatWrapper, Position),
/// Character constant. /// Character constant.
CharConstant(Box<(char, Position)>), CharConstant(char, Position),
/// String constant. /// String constant.
StringConstant(Box<IdentX>), StringConstant(Box<IdentX>),
/// FnPtr constant. /// FnPtr constant.
@ -880,23 +916,13 @@ pub enum Expr {
/// Variable access - ((variable name, position), optional modules, hash, optional index) /// Variable access - ((variable name, position), optional modules, hash, optional index)
Variable(Box<(Ident, Option<Box<ModuleRef>>, u64, Option<NonZeroUsize>)>), Variable(Box<(Ident, Option<Box<ModuleRef>>, u64, Option<NonZeroUsize>)>),
/// Property access. /// Property access.
Property(Box<((ImmutableString, String, String), Position)>), Property(Box<(IdentX, (String, String))>),
/// { stmt } /// { stmt }
Stmt(Box<(Stmt, Position)>), Stmt(Box<Stmt>, Position),
/// Wrapped expression - should not be optimized away. /// Wrapped expression - should not be optimized away.
Expr(Box<Expr>), Expr(Box<Expr>),
/// func(expr, ... ) - ((function name, native_only, capture, position), optional modules, hash, arguments, optional default value) /// func(expr, ... )
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls FnCall(Box<FnCallInfo>),
/// and the function names are predictable, so no need to allocate a new `String`.
FnCall(
Box<(
(Cow<'static, str>, bool, bool, Position),
Option<Box<ModuleRef>>,
u64,
StaticVec<Expr>,
Option<bool>, // Default value is `bool` in order for `Expr` to be `Hash`.
)>,
),
/// lhs.rhs /// lhs.rhs
Dot(Box<BinaryExpr>), Dot(Box<BinaryExpr>),
/// expr[expr] /// expr[expr]
@ -936,10 +962,10 @@ impl Expr {
Some(match self { Some(match self {
Self::Expr(x) => return x.get_type_id(), Self::Expr(x) => return x.get_type_id(),
Self::IntegerConstant(_) => TypeId::of::<INT>(), Self::IntegerConstant(_, _) => TypeId::of::<INT>(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => TypeId::of::<FLOAT>(), Self::FloatConstant(_, _) => TypeId::of::<FLOAT>(),
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(_) => {
@ -964,10 +990,10 @@ impl Expr {
Some(match self { Some(match self {
Self::Expr(x) => return x.get_constant_value(), Self::Expr(x) => return x.get_constant_value(),
Self::IntegerConstant(x) => x.0.into(), Self::IntegerConstant(x, _) => (*x).into(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(x) => x.0.into(), Self::FloatConstant(x, _) => x.0.into(),
Self::CharConstant(x) => x.0.into(), Self::CharConstant(x, _) => (*x).into(),
Self::StringConstant(x) => x.name.clone().into(), Self::StringConstant(x) => x.name.clone().into(),
Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked( Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
x.name.clone(), x.name.clone(),
@ -1011,18 +1037,18 @@ impl Expr {
Self::Expr(x) => x.position(), Self::Expr(x) => x.position(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(x) => x.1, Self::FloatConstant(_, pos) => *pos,
Self::IntegerConstant(x) => x.1, Self::IntegerConstant(_, pos) => *pos,
Self::CharConstant(x) => x.1, 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(x) => x.1,
Self::Map(x) => x.1, Self::Map(x) => x.1,
Self::Property(x) => x.1, Self::Property(x) => (x.0).pos,
Self::Stmt(x) => x.1, Self::Stmt(_, pos) => *pos,
Self::Variable(x) => (x.0).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, Self::And(x) | Self::Or(x) | Self::In(x) => x.pos,
@ -1042,18 +1068,18 @@ impl Expr {
} }
#[cfg(not(feature = "no_float"))] #[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::IntegerConstant(_, pos) => *pos = new_pos,
Self::CharConstant(x) => x.1 = 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(x) => x.1 = new_pos,
Self::Map(x) => x.1 = new_pos, Self::Map(x) => x.1 = new_pos,
Self::Variable(x) => (x.0).pos = new_pos, Self::Variable(x) => (x.0).pos = new_pos,
Self::Property(x) => x.1 = new_pos, Self::Property(x) => (x.0).pos = new_pos,
Self::Stmt(x) => x.1 = new_pos, Self::Stmt(_, pos) => *pos = new_pos,
Self::FnCall(x) => (x.0).3 = new_pos, Self::FnCall(x) => x.pos = new_pos,
Self::And(x) | Self::Or(x) | Self::In(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::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos,
Self::Dot(x) | Self::Index(x) => x.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() x.lhs.is_pure() && x.rhs.is_pure()
} }
Self::Stmt(x) => x.0.is_pure(), Self::Stmt(x, _) => x.is_pure(),
Self::Variable(_) => true, Self::Variable(_) => true,
@ -1099,10 +1125,10 @@ impl Expr {
Self::Expr(x) => x.is_literal(), Self::Expr(x) => x.is_literal(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => true, Self::FloatConstant(_, _) => true,
Self::IntegerConstant(_) Self::IntegerConstant(_, _)
| Self::CharConstant(_) | Self::CharConstant(_, _)
| Self::StringConstant(_) | Self::StringConstant(_)
| Self::FnPointer(_) | Self::FnPointer(_)
| Self::True(_) | Self::True(_)
@ -1118,7 +1144,7 @@ impl Expr {
// 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,
}, },
@ -1132,10 +1158,10 @@ impl Expr {
Self::Expr(x) => x.is_constant(), Self::Expr(x) => x.is_constant(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => true, Self::FloatConstant(_, _) => true,
Self::IntegerConstant(_) Self::IntegerConstant(_, _)
| Self::CharConstant(_) | Self::CharConstant(_, _)
| Self::StringConstant(_) | Self::StringConstant(_)
| Self::FnPointer(_) | Self::FnPointer(_)
| Self::True(_) | Self::True(_)
@ -1151,7 +1177,7 @@ impl Expr {
// 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,
}, },
@ -1165,10 +1191,10 @@ impl Expr {
Self::Expr(x) => x.is_valid_postfix(token), Self::Expr(x) => x.is_valid_postfix(token),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => false, Self::FloatConstant(_, _) => false,
Self::IntegerConstant(_) Self::IntegerConstant(_, _)
| Self::CharConstant(_) | Self::CharConstant(_, _)
| Self::FnPointer(_) | Self::FnPointer(_)
| Self::In(_) | Self::In(_)
| Self::And(_) | Self::And(_)
@ -1178,7 +1204,7 @@ impl Expr {
| 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(_)
@ -1215,10 +1241,10 @@ impl Expr {
pub(crate) fn into_property(self) -> Self { pub(crate) fn into_property(self) -> Self {
match self { match self {
Self::Variable(x) if x.1.is_none() => { Self::Variable(x) if x.1.is_none() => {
let Ident { name, pos } = x.0; let ident = x.0;
let getter = make_getter(&name); let getter = make_getter(&ident.name);
let setter = make_setter(&name); let setter = make_setter(&ident.name);
Self::Property(Box::new(((name.into(), getter, setter), pos))) Self::Property(Box::new((ident.into(), (getter, setter))))
} }
_ => self, _ => self,
} }

View File

@ -1,6 +1,6 @@
//! Main module defining the script evaluation `Engine`. //! 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::dynamic::{map_std_type_name, Dynamic, Union, Variant};
use crate::fn_call::run_builtin_op_assignment; use crate::fn_call::run_builtin_op_assignment;
use crate::fn_native::{Callback, FnPtr, OnVarCallback}; use crate::fn_native::{Callback, FnPtr, OnVarCallback};
@ -968,12 +968,20 @@ 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.1.is_none() => { Expr::FnCall(x) if x.namespace.is_none() => {
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref(); let FnCallInfo {
let def_val = def_val.map(Into::<Dynamic>::into); name,
native_only: native,
pos,
hash,
def_value,
..
} = x.as_ref();
let def_value = def_value.map(Into::<Dynamic>::into);
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
self.make_method_call( 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)) .map_err(|err| err.fill_position(*pos))
} }
@ -981,8 +989,8 @@ impl Engine {
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 ((prop, _, _), pos) = x.as_ref(); let IdentX { name, pos } = &x.0;
let index = prop.clone().into(); let index = name.clone().into();
let mut val = self let mut val = self
.get_indexed_mut(state, lib, target, index, *pos, true, false, level)?; .get_indexed_mut(state, lib, target, index, *pos, true, false, level)?;
@ -991,8 +999,8 @@ impl Engine {
} }
// {xxx:map}.id // {xxx:map}.id
Expr::Property(x) if target.is::<Map>() => { Expr::Property(x) if target.is::<Map>() => {
let ((prop, _, _), pos) = x.as_ref(); let IdentX { name, pos } = &x.0;
let index = prop.clone().into(); let index = name.clone().into();
let val = self.get_indexed_mut( let val = self.get_indexed_mut(
state, lib, target, index, *pos, false, false, level, state, lib, target, index, *pos, false, false, level,
)?; )?;
@ -1001,7 +1009,7 @@ impl Engine {
} }
// xxx.id = ??? // xxx.id = ???
Expr::Property(x) if new_val.is_some() => { 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 new_val = new_val;
let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0]; let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0];
self.exec_fn_call( self.exec_fn_call(
@ -1013,7 +1021,7 @@ impl Engine {
} }
// xxx.id // xxx.id
Expr::Property(x) => { Expr::Property(x) => {
let ((_, getter, _), pos) = x.as_ref(); let (IdentX { pos, .. }, (getter, _)) = x.as_ref();
let mut args = [target.as_mut()]; let mut args = [target.as_mut()];
self.exec_fn_call( self.exec_fn_call(
state, lib, getter, 0, &mut args, is_ref, true, false, None, &None, 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::<Map>() => { Expr::Index(x) | Expr::Dot(x) if target.is::<Map>() => {
let mut val = match &x.lhs { let mut val = match &x.lhs {
Expr::Property(p) => { Expr::Property(p) => {
let ((prop, _, _), pos) = p.as_ref(); let IdentX { name, pos } = &p.0;
let index = prop.clone().into(); let index = name.clone().into();
self.get_indexed_mut( self.get_indexed_mut(
state, lib, target, index, *pos, false, true, level, state, lib, target, index, *pos, false, true, level,
)? )?
} }
// {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.1.is_none() => { Expr::FnCall(x) if x.namespace.is_none() => {
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref(); let FnCallInfo {
let def_val = def_val.map(Into::<Dynamic>::into); name,
native_only: native,
pos,
hash,
def_value,
..
} = x.as_ref();
let def_value = def_value.map(Into::<Dynamic>::into);
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
let (val, _) = self let (val, _) = self
.make_method_call( .make_method_call(
state, lib, name, *hash, target, args, &def_val, *native, state, lib, name, *hash, target, args, &def_value, *native,
false, level, false, level,
) )
.map_err(|err| err.fill_position(*pos))?; .map_err(|err| err.fill_position(*pos))?;
@ -1062,7 +1077,7 @@ impl Engine {
match &x.lhs { match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => { 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 arg_values = &mut [target.as_mut(), &mut Default::default()];
let args = &mut arg_values[..1]; let args = &mut arg_values[..1];
let (mut val, updated) = self let (mut val, updated) = self
@ -1110,13 +1125,20 @@ 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.1.is_none() => { Expr::FnCall(f) if f.namespace.is_none() => {
let ((name, native, _, pos), _, hash, _, def_val) = f.as_ref(); let FnCallInfo {
let def_val = def_val.map(Into::<Dynamic>::into); name,
native_only: native,
pos,
hash,
def_value,
..
} = f.as_ref();
let def_value = def_value.map(Into::<Dynamic>::into);
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
let (mut val, _) = self let (mut val, _) = self
.make_method_call( .make_method_call(
state, lib, name, *hash, target, args, &def_val, *native, state, lib, name, *hash, target, args, &def_value, *native,
false, level, false, level,
) )
.map_err(|err| err.fill_position(*pos))?; .map_err(|err| err.fill_position(*pos))?;
@ -1250,9 +1272,10 @@ 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.1.is_none() => { Expr::FnCall(x) if x.namespace.is_none() => {
let arg_values = let arg_values = x
x.3.iter() .args
.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)
}) })
@ -1268,14 +1291,14 @@ impl Engine {
// 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.1.is_none() => { Expr::FnCall(x) if chain_type == ChainType::Dot && x.namespace.is_none() => x
x.3.iter() .args
.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)?
@ -1491,11 +1514,11 @@ impl Engine {
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.as_ref(), level),
Expr::IntegerConstant(x) => Ok(x.0.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.to_string().into()),
Expr::CharConstant(x) => Ok(x.0.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())
} }
@ -1514,7 +1537,7 @@ impl Engine {
Expr::Property(_) => unreachable!(), Expr::Property(_) => unreachable!(),
// Statement block // 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] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -1546,21 +1569,38 @@ impl Engine {
)))), )))),
// Normal function call // Normal function call
Expr::FnCall(x) if x.1.is_none() => { Expr::FnCall(x) if x.namespace.is_none() => {
let ((name, native, cap_scope, pos), _, hash, args_expr, def_val) = x.as_ref(); let FnCallInfo {
let def_val = def_val.map(Into::<Dynamic>::into); name,
native_only: native,
capture: cap_scope,
pos,
hash,
args,
def_value,
..
} = x.as_ref();
let def_value = def_value.map(Into::<Dynamic>::into);
self.make_function_call( 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, false, *cap_scope, level,
) )
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
} }
// Module-qualified function call // Module-qualified function call
Expr::FnCall(x) if x.1.is_some() => { Expr::FnCall(x) if x.namespace.is_some() => {
let ((name, _, _, pos), modules, hash, args_expr, def_val) = x.as_ref(); let FnCallInfo {
name,
pos,
namespace,
hash,
args,
def_value,
..
} = x.as_ref();
self.make_qualified_function_call( 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, level,
) )
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
@ -2090,8 +2130,8 @@ impl Engine {
// Share statement // Share statement
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Stmt::Share(Ident { name: var_name, .. }) => { Stmt::Share(x) => {
match scope.get_index(var_name) { match scope.get_index(&x.name) {
Some((index, ScopeEntryType::Normal)) => { Some((index, ScopeEntryType::Normal)) => {
let (val, _) = scope.get_mut(index); let (val, _) = scope.get_mut(index);

View File

@ -165,7 +165,8 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")] #[deprecated(note = "this type is volatile and may change")]
pub use ast::{ 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")] #[cfg(feature = "internals")]

View File

@ -1,6 +1,6 @@
//! Module implementing the AST optimizer. //! 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::dynamic::Dynamic;
use crate::engine::{ use crate::engine::{
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, 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; // 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(); state.set_dirty();
optimize_stmt(x.0, state, preserve_result) optimize_stmt(*x, state, preserve_result)
} }
// expr; // expr;
Stmt::Expr(expr) => Stmt::Expr(optimize_expr(expr, state)), 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 - 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))), Expr::Expr(x) => Expr::Expr(Box::new(optimize_expr(*x, state))),
// { stmt } // { stmt }
Expr::Stmt(x) => match x.0 { Expr::Stmt(x, pos) => match *x {
// {} -> () // {} -> ()
Stmt::Noop(_) => { Stmt::Noop(_) => {
state.set_dirty(); state.set_dirty();
Expr::Unit(x.1) Expr::Unit(pos)
} }
// { expr } -> expr // { expr } -> expr
Stmt::Expr(expr) => { Stmt::Expr(expr) => {
@ -446,7 +446,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
optimize_expr(expr, state) optimize_expr(expr, state)
} }
// { stmt } // { 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 // lhs.rhs
@ -454,7 +454,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
Expr::Dot(x) => match (x.lhs, x.rhs) { Expr::Dot(x) => 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), 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. // 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();
@ -475,13 +475,13 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(x) => match (x.lhs, x.rhs) { Expr::Index(x) => match (x.lhs, x.rhs) {
// array[int] // array[int]
(Expr::Array(mut a), Expr::IntegerConstant(i)) (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) => 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. // 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.0 as usize); let mut expr = a.0.remove(i as usize);
expr.set_position(a.1); expr.set_position(a.1);
expr expr
} }
@ -496,10 +496,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
// string[int] // 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 // String literal indexing - get the character
state.set_dirty(); 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]
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr { (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) } if b.name.contains(a.name.as_str()) { Expr::True(a.pos) } else { Expr::False(a.pos) }
} }
// 'x' in "xxxxx" // 'x' in "xxxxx"
(Expr::CharConstant(a), Expr::StringConstant(b)) => { (Expr::CharConstant(a, pos), Expr::StringConstant(b)) => {
state.set_dirty(); 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 #{...} // "xxx" in #{...}
(Expr::StringConstant(a), Expr::Map(b)) => { (Expr::StringConstant(a), Expr::Map(b)) => {
@ -540,14 +540,14 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
} }
} }
// 'x' in #{...} // 'x' in #{...}
(Expr::CharConstant(a), Expr::Map(b)) => { (Expr::CharConstant(a, pos), Expr::Map(b)) => {
state.set_dirty(); 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() { if b.0.iter().find(|(x, _)| x.name == &ch).is_some() {
Expr::True(a.1) Expr::True(pos)
} else { } else {
Expr::False(a.1) Expr::False(pos)
} }
} }
// lhs in rhs // lhs in rhs
@ -607,20 +607,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
}, },
// Do not call some special keywords // Do not call some special keywords
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref()) => { Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => {
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) Expr::FnCall(x)
} }
// Call built-in operators // Call built-in operators
Expr::FnCall(mut x) 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 && state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.3.len() == 2 // binary call && x.args.len() == 2 // binary call
&& x.3.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
&& !is_valid_identifier((x.0).0.chars()) // cannot be scripted && !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_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();
@ -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) Expr::FnCall(x)
} }
// Eagerly call functions // Eagerly call functions
Expr::FnCall(mut x) 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 && 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) // First search for script-defined functions (can override built-in)
#[cfg(not(feature = "no_function"))] #[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) Expr::FnCall(x)
} }
// id(args ..) -> optimize function call arguments // id(args ..) -> optimize function call arguments
Expr::FnCall(mut x) => { 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) Expr::FnCall(x)
} }

View File

@ -1,6 +1,8 @@
//! Main module defining the lexer and parser. //! 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::dynamic::{Dynamic, Union};
use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
use crate::module::ModuleRef; use crate::module::ModuleRef;
@ -263,7 +265,7 @@ fn parse_fn_call(
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
id: String, id: String,
capture: bool, capture: bool,
mut modules: Option<Box<ModuleRef>>, mut namespace: Option<Box<ModuleRef>>,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
@ -288,7 +290,7 @@ fn parse_fn_call(
Token::RightParen => { Token::RightParen => {
eat_token(input, 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"))] #[cfg(not(feature = "no_module"))]
modules.set_index(state.find_module(&modules[0].0)); modules.set_index(state.find_module(&modules[0].0));
@ -305,13 +307,15 @@ fn parse_fn_call(
calc_script_fn_hash(empty(), &id, 0) calc_script_fn_hash(empty(), &id, 0)
}; };
return Ok(Expr::FnCall(Box::new(( return Ok(Expr::FnCall(Box::new(FnCallInfo {
(id.into(), false, capture, settings.pos), name: id.into(),
modules, capture,
hash_script, namespace,
hash: hash_script,
args, args,
None, pos: settings.pos,
)))); ..Default::default()
})));
} }
// id... // id...
_ => (), _ => (),
@ -331,7 +335,7 @@ fn parse_fn_call(
(Token::RightParen, _) => { (Token::RightParen, _) => {
eat_token(input, 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"))] #[cfg(not(feature = "no_module"))]
modules.set_index(state.find_module(&modules[0].0)); 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()) calc_script_fn_hash(empty(), &id, args.len())
}; };
return Ok(Expr::FnCall(Box::new(( return Ok(Expr::FnCall(Box::new(FnCallInfo {
(id.into(), false, capture, settings.pos), name: id.into(),
modules, capture,
hash_script, namespace,
hash: hash_script,
args, args,
None, pos: settings.pos,
)))); ..Default::default()
})));
} }
// id(...args, // id(...args,
(Token::Comma, _) => { (Token::Comma, _) => {
@ -400,32 +406,32 @@ fn parse_index_chain(
// Check type of indexing - must be integer or string // Check type of indexing - must be integer or string
match &idx_expr { match &idx_expr {
// lhs[int] // lhs[int]
Expr::IntegerConstant(x) if x.0 < 0 => { Expr::IntegerConstant(x, pos) if *x < 0 => {
return Err(PERR::MalformedIndexExpr(format!( return Err(PERR::MalformedIndexExpr(format!(
"Array access expects non-negative index: {} < 0", "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::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(),
) )
.into_err(x.1)) .into_err(*pos))
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_) => { Expr::FloatConstant(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Only arrays, object maps and strings can be indexed".into(),
) )
.into_err(lhs.position())) .into_err(lhs.position()))
} }
Expr::CharConstant(_) Expr::CharConstant(_, _)
| Expr::And(_) | Expr::And(_)
| Expr::Or(_) | Expr::Or(_)
| Expr::In(_) | Expr::In(_)
@ -453,14 +459,14 @@ fn parse_index_chain(
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_) => { Expr::FloatConstant(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Only arrays, object maps and strings can be indexed".into(),
) )
.into_err(lhs.position())) .into_err(lhs.position()))
} }
Expr::CharConstant(_) Expr::CharConstant(_, _)
| Expr::And(_) | Expr::And(_)
| Expr::Or(_) | Expr::Or(_)
| Expr::In(_) | Expr::In(_)
@ -478,14 +484,14 @@ fn parse_index_chain(
// lhs[float] // lhs[float]
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
x @ Expr::FloatConstant(_) => { x @ Expr::FloatConstant(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a float".into(), "Array access expects integer index, not a float".into(),
) )
.into_err(x.position())) .into_err(x.position()))
} }
// lhs[char] // lhs[char]
x @ Expr::CharConstant(_) => { x @ Expr::CharConstant(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a character".into(), "Array access expects integer index, not a character".into(),
) )
@ -754,7 +760,7 @@ fn parse_primary(
// { - block statement as expression // { - block statement as expression
Token::LeftBrace if settings.allow_stmt_expr => { Token::LeftBrace if settings.allow_stmt_expr => {
return parse_block(input, state, lib, settings.level_up()) 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)), Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)),
_ => input.next().unwrap(), _ => input.next().unwrap(),
@ -763,10 +769,10 @@ fn parse_primary(
let (next_token, _) = input.peek().unwrap(); let (next_token, _) = input.peek().unwrap();
let mut root_expr = match token { 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"))] #[cfg(not(feature = "no_float"))]
Token::FloatConstant(x) => Expr::FloatConstant(Box::new(FloatWrapper(x, settings.pos))), Token::FloatConstant(x) => Expr::FloatConstant(FloatWrapper(x), settings.pos),
Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos),
Token::StringConstant(s) => Expr::StringConstant(Box::new(IdentX::new(s, settings.pos))), Token::StringConstant(s) => Expr::StringConstant(Box::new(IdentX::new(s, settings.pos))),
// Function call // Function call
@ -946,35 +952,30 @@ fn parse_unary(
match token { match token {
// If statement is allowed to act as expressions // If statement is allowed to act as expressions
Token::If if settings.allow_if_expr => Ok(Expr::Stmt(Box::new(( Token::If if settings.allow_if_expr => Ok(Expr::Stmt(
parse_if(input, state, lib, settings.level_up())?, Box::new(parse_if(input, state, lib, settings.level_up())?),
settings.pos, settings.pos,
)))), )),
// -expr // -expr
Token::UnaryMinus => { Token::UnaryMinus => {
let pos = eat_token(input, Token::UnaryMinus); let pos = eat_token(input, Token::UnaryMinus);
match parse_unary(input, state, lib, settings.level_up())? { match parse_unary(input, state, lib, settings.level_up())? {
// Negative integer // Negative integer
Expr::IntegerConstant(x) => { Expr::IntegerConstant(num, pos) => num
let (num, pos) = *x; .checked_neg()
.map(|i| Expr::IntegerConstant(i, pos))
num.checked_neg()
.map(|i| Expr::IntegerConstant(Box::new((i, pos))))
.or_else(|| { .or_else(|| {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return Some(Expr::FloatConstant(Box::new( return Some(Expr::FloatConstant(-Into::<FloatWrapper>::into(num), pos));
-Into::<FloatWrapper>::into(*x),
)));
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]
return None; return None;
}) })
.ok_or_else(|| LexError::MalformedNumber(format!("-{}", x.0)).into_err(pos)) .ok_or_else(|| LexError::MalformedNumber(format!("-{}", num)).into_err(pos)),
}
// Negative float // Negative float
#[cfg(not(feature = "no_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 // Call negative function
expr => { expr => {
@ -983,13 +984,15 @@ fn parse_unary(
let mut args = StaticVec::new(); let mut args = StaticVec::new();
args.push(expr); args.push(expr);
Ok(Expr::FnCall(Box::new(( Ok(Expr::FnCall(Box::new(FnCallInfo {
(op.into(), true, false, pos), name: op.into(),
None, native_only: true,
namespace: None,
hash, hash,
args, args,
None, pos,
)))) ..Default::default()
})))
} }
} }
} }
@ -1008,13 +1011,15 @@ 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(( Ok(Expr::FnCall(Box::new(FnCallInfo {
(op.into(), true, false, pos), name: op.into(),
None, native_only: true,
hash, hash,
args, 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"))] #[cfg(not(feature = "no_function"))]
@ -1184,11 +1189,10 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
} }
// lhs.id // lhs.id
(lhs, Expr::Variable(x)) if x.1.is_none() => { (lhs, Expr::Variable(x)) if x.1.is_none() => {
let Ident { name, pos } = x.0; let ident = x.0;
let getter = make_getter(&ident.name);
let getter = make_getter(&name); let setter = make_setter(&ident.name);
let setter = make_setter(&name); let rhs = Expr::Property(Box::new((ident.into(), (getter, setter))));
let rhs = Expr::Property(Box::new(((name.into(), getter, setter), pos)));
Expr::Dot(Box::new(BinaryExpr { Expr::Dot(Box::new(BinaryExpr {
lhs, lhs,
@ -1234,21 +1238,20 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
} }
// lhs.Fn() or lhs.eval() // lhs.Fn() or lhs.eval()
(_, Expr::FnCall(x)) (_, Expr::FnCall(x))
if x.3.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&(x.0).0.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.0).0, x.name, x.name
(x.0).0
)) ))
.into_err((x.0).3)); .into_err(x.pos));
} }
// lhs.func!(...) // lhs.func!(...)
(_, Expr::FnCall(x)) if (x.0).2 => { (_, Expr::FnCall(x)) 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.0).3)) .into_err(x.pos));
} }
// lhs.func(...) // lhs.func(...)
(lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new(BinaryExpr { (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<Expr, ParseEr
/// Make an 'in' expression. /// Make an 'in' expression.
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(_))
@ -1278,7 +1281,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
(_, x @ Expr::FloatConstant(_)) => { (_, x @ Expr::FloatConstant(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(), "'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, ParseErr
// "xxx" in "xxxx", 'x' in "xxxx" - OK! // "xxx" in "xxxx", 'x' in "xxxx" - OK!
(Expr::StringConstant(_), Expr::StringConstant(_)) (Expr::StringConstant(_), Expr::StringConstant(_))
| (Expr::CharConstant(_), Expr::StringConstant(_)) => (), | (Expr::CharConstant(_, _), Expr::StringConstant(_)) => (),
// 123.456 in "xxxx" // 123.456 in "xxxx"
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
(x @ Expr::FloatConstant(_), Expr::StringConstant(_)) => { (x @ Expr::FloatConstant(_, _), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a float".into(), "'in' expression for a string expects a string, not a float".into(),
) )
.into_err(x.position())) .into_err(x.position()))
} }
// 123 in "xxxx" // 123 in "xxxx"
(x @ Expr::IntegerConstant(_), Expr::StringConstant(_)) => { (x @ Expr::IntegerConstant(_, _), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a number".into(), "'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, 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(),
) )
@ -1480,7 +1483,14 @@ fn parse_binary_op(
let cmp_def = Some(false); let cmp_def = Some(false);
let op = op_token.syntax(); let op = op_token.syntax();
let hash = calc_script_fn_hash(empty(), &op, 2); 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(); let mut args = StaticVec::new();
args.push(root); args.push(root);
@ -1497,17 +1507,31 @@ fn parse_binary_op(
| Token::PowerOf | Token::PowerOf
| Token::Ampersand | Token::Ampersand
| Token::Pipe | 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 // '!=' 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 // 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((op, None, hash, args, cmp_def))), | Token::GreaterThanEqualsTo => Expr::FnCall(Box::new(FnCallInfo {
hash,
args,
def_value: cmp_def,
..op_base
})),
Token::Or => { Token::Or => {
let rhs = args.pop().unwrap(); let rhs = args.pop().unwrap();
@ -1542,8 +1566,12 @@ 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
let op = (op.0, false, op.2, op.3); Expr::FnCall(Box::new(FnCallInfo {
Expr::FnCall(Box::new((op, None, hash, args, None))) hash,
args,
native_only: false,
..op_base
}))
} }
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)), op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
@ -1617,7 +1645,7 @@ fn parse_custom_syntax(
MARKER_BLOCK => { MARKER_BLOCK => {
let stmt = parse_block(input, state, lib, settings)?; let stmt = parse_block(input, state, lib, settings)?;
let pos = stmt.position(); 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()); segments.push(MARKER_BLOCK.into());
} }
s => match input.next().unwrap() { s => match input.next().unwrap() {
@ -2464,13 +2492,13 @@ 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(( let expr = Expr::FnCall(Box::new(FnCallInfo {
(KEYWORD_FN_PTR_CURRY.into(), false, false, pos), name: KEYWORD_FN_PTR_CURRY.into(),
None,
hash, hash,
args, args,
None, 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.
@ -2479,10 +2507,10 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
// Statement block // Statement block
let mut statements: Vec<_> = Default::default(); let mut statements: Vec<_> = Default::default();
// Insert `Share` statements // 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 // Final expression
statements.push(Stmt::Expr(expr)); 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")] #[cfg(feature = "no_closure")]
@ -2755,11 +2783,11 @@ impl Engine {
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> { pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
match value.0 { match value.0 {
#[cfg(not(feature = "no_float"))] #[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::Unit(_) => Some(Expr::Unit(pos)),
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))), Union::Int(value) => Some(Expr::IntegerConstant(value, pos)),
Union::Char(value) => Some(Expr::CharConstant(Box::new((value, pos)))), Union::Char(value) => Some(Expr::CharConstant(value, pos)),
Union::Str(value) => Some(Expr::StringConstant(Box::new(IdentX::new(value, pos)))), Union::Str(value) => Some(Expr::StringConstant(Box::new(IdentX::new(value, pos)))),
Union::Bool(true) => Some(Expr::True(pos)), Union::Bool(true) => Some(Expr::True(pos)),
Union::Bool(false) => Some(Expr::False(pos)), Union::Bool(false) => Some(Expr::False(pos)),