Merge pull request #276 from schungx/master
Optimize Stmt/Expr some more.
This commit is contained in:
commit
c8bccd6577
@ -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
|
||||||
|
242
src/ast.rs
242
src/ast.rs
@ -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
|
||||||
@ -774,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 {
|
||||||
@ -802,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.
|
||||||
@ -820,14 +823,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,23 +838,47 @@ 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,
|
||||||
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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ An expression sub-tree.
|
/// _[INTERNALS]_ An expression sub-tree.
|
||||||
@ -867,12 +893,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,37 +906,27 @@ 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>, Position),
|
||||||
/// 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>, 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
|
||||||
@ -918,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 {
|
||||||
@ -936,22 +952,22 @@ 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(_, _) => {
|
||||||
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,
|
||||||
})
|
})
|
||||||
@ -964,10 +980,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(),
|
||||||
@ -978,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(),
|
||||||
)))
|
)))
|
||||||
@ -1011,26 +1025,26 @@ 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(_, pos) => *pos,
|
||||||
Self::Map(x) => x.1,
|
Self::Map(_, pos) => *pos,
|
||||||
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(_, 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1042,22 +1056,22 @@ 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(_, 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.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(_, 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
|
||||||
@ -1070,13 +1084,15 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Stmt(x) => x.0.is_pure(),
|
Self::Stmt(x, _) => x.is_pure(),
|
||||||
|
|
||||||
Self::Variable(_) => true,
|
Self::Variable(_) => true,
|
||||||
|
|
||||||
@ -1099,10 +1115,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(_)
|
||||||
@ -1110,15 +1126,15 @@ 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,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1132,10 +1148,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(_)
|
||||||
@ -1143,15 +1159,15 @@ 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,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1165,25 +1181,25 @@ 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(_, _)
|
||||||
| 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,
|
||||||
@ -1205,7 +1221,7 @@ impl Expr {
|
|||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Custom(_) => false,
|
Self::Custom(_, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1215,10 +1231,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,
|
||||||
}
|
}
|
||||||
|
202
src/engine.rs
202
src/engine.rs
@ -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};
|
||||||
@ -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,21 +968,28 @@ 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, pos) 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,
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
// 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 ((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 +998,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 +1008,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 +1020,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,
|
||||||
@ -1023,30 +1030,36 @@ 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 ((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, pos) 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,
|
||||||
|
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))?;
|
||||||
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!(),
|
||||||
};
|
};
|
||||||
@ -1055,14 +1068,14 @@ 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) => {
|
||||||
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
|
||||||
@ -1086,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 {
|
||||||
@ -1102,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)),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -1110,13 +1123,19 @@ 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, pos) 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,
|
||||||
|
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))?;
|
||||||
@ -1130,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!(),
|
||||||
}
|
}
|
||||||
@ -1161,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!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1213,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!(),
|
||||||
@ -1225,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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1250,9 +1269,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)
|
||||||
})
|
})
|
||||||
@ -1260,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.1.is_none() => {
|
Expr::FnCall(x, _) if chain_type == ChainType::Dot && x.namespace.is_none() => {
|
||||||
x.3.iter()
|
x.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)?
|
||||||
.into(),
|
.into(),
|
||||||
@ -1284,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(
|
||||||
@ -1433,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());
|
||||||
@ -1442,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()
|
||||||
@ -1459,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(),
|
||||||
@ -1489,13 +1511,13 @@ 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.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.clone().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,30 +1536,30 @@ 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"))]
|
||||||
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))
|
||||||
@ -1546,31 +1568,46 @@ impl Engine {
|
|||||||
)))),
|
)))),
|
||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x, pos) 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,
|
||||||
|
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, pos) if x.namespace.is_some() => {
|
||||||
let ((name, _, _, pos), modules, hash, args_expr, def_val) = x.as_ref();
|
let FnCallInfo {
|
||||||
|
name,
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
@ -1583,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()
|
||||||
@ -1600,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()
|
||||||
@ -1616,7 +1652,7 @@ impl Engine {
|
|||||||
this_ptr,
|
this_ptr,
|
||||||
level,
|
level,
|
||||||
};
|
};
|
||||||
func(&mut context, &expressions)
|
(custom.func)(&mut context, &expressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -1785,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,
|
||||||
)?;
|
)?;
|
||||||
@ -1793,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,
|
||||||
)?;
|
)?;
|
||||||
@ -2090,8 +2126,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);
|
||||||
|
|
||||||
|
@ -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")]
|
||||||
|
135
src/optimize.rs
135
src/optimize.rs
@ -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,20 +446,19 @@ 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
|
||||||
#[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.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();
|
||||||
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,98 +466,94 @@ 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 >= 0 && (i.0 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.0 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))
|
||||||
}
|
}
|
||||||
// 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 {
|
||||||
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();
|
||||||
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, _)) => {
|
||||||
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), 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.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
|
||||||
(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.0).0.as_ref()) => {
|
Expr::FnCall(mut x, pos) 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, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call built-in operators
|
// Call built-in operators
|
||||||
Expr::FnCall(mut x)
|
Expr::FnCall(mut x, pos)
|
||||||
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, 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,24 +622,24 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
if !state.engine.has_override_by_name_and_arguments(state.lib, name, arg_types.as_ref(), false) {
|
if !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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
Expr::FnCall(mut x)
|
Expr::FnCall(mut x, pos)
|
||||||
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, 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,21 +668,21 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(mut x) => {
|
Expr::FnCall(mut x, pos) => {
|
||||||
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, 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,
|
||||||
|
418
src/parser.rs
418
src/parser.rs
@ -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,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((
|
return Ok(Expr::FnCall(
|
||||||
(id.into(), false, capture, settings.pos),
|
Box::new(FnCallInfo {
|
||||||
modules,
|
name: id.into(),
|
||||||
hash_script,
|
capture,
|
||||||
|
namespace,
|
||||||
|
hash: hash_script,
|
||||||
args,
|
args,
|
||||||
None,
|
..Default::default()
|
||||||
))));
|
}),
|
||||||
|
settings.pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
// id...
|
// id...
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -331,7 +337,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 +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((
|
return Ok(Expr::FnCall(
|
||||||
(id.into(), false, capture, settings.pos),
|
Box::new(FnCallInfo {
|
||||||
modules,
|
name: id.into(),
|
||||||
hash_script,
|
capture,
|
||||||
|
namespace,
|
||||||
|
hash: hash_script,
|
||||||
args,
|
args,
|
||||||
None,
|
..Default::default()
|
||||||
))));
|
}),
|
||||||
|
settings.pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
// id(...args,
|
// id(...args,
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
@ -400,35 +410,35 @@ 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(_, _)
|
||||||
| Expr::True(_)
|
| Expr::True(_)
|
||||||
| Expr::False(_)
|
| Expr::False(_)
|
||||||
| Expr::Unit(_) => {
|
| Expr::Unit(_) => {
|
||||||
@ -443,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(),
|
||||||
)
|
)
|
||||||
@ -453,17 +463,17 @@ 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(_, _)
|
||||||
| Expr::True(_)
|
| Expr::True(_)
|
||||||
| Expr::False(_)
|
| Expr::False(_)
|
||||||
| Expr::Unit(_) => {
|
| Expr::Unit(_) => {
|
||||||
@ -478,14 +488,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(),
|
||||||
)
|
)
|
||||||
@ -499,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(),
|
||||||
)
|
)
|
||||||
@ -531,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
|
||||||
_ => {
|
_ => {
|
||||||
@ -543,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(
|
||||||
|
Box::new(BinaryExpr {
|
||||||
lhs,
|
lhs,
|
||||||
rhs: Expr::Expr(Box::new(idx_expr)),
|
rhs: Expr::Expr(Box::new(idx_expr)),
|
||||||
pos: settings.pos,
|
}),
|
||||||
})))
|
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,
|
)),
|
||||||
}))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -625,7 +635,7 @@ fn parse_array_literal(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Expr::Array(Box::new((arr, settings.pos))))
|
Ok(Expr::Array(Box::new(arr), settings.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a map literal.
|
/// Parse a map literal.
|
||||||
@ -734,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.
|
||||||
@ -754,7 +764,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 +773,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 +956,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 +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((
|
Ok(Expr::FnCall(
|
||||||
(op.into(), true, false, pos),
|
Box::new(FnCallInfo {
|
||||||
None,
|
name: op.into(),
|
||||||
|
native_only: true,
|
||||||
|
namespace: None,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
None,
|
..Default::default()
|
||||||
))))
|
}),
|
||||||
|
pos,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1008,13 +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((
|
Ok(Expr::FnCall(
|
||||||
(op.into(), true, false, pos),
|
Box::new(FnCallInfo {
|
||||||
None,
|
name: op.into(),
|
||||||
|
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
|
||||||
))))
|
..Default::default()
|
||||||
|
}),
|
||||||
|
pos,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
// | ...
|
// | ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -1093,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))
|
||||||
@ -1127,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
|
||||||
@ -1178,84 +1191,70 @@ 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() => {
|
||||||
let Ident { name, pos } = x.0;
|
let ident = x.0;
|
||||||
|
let getter = make_getter(&ident.name);
|
||||||
|
let setter = make_setter(&ident.name);
|
||||||
|
let rhs = Expr::Property(Box::new((ident.into(), (getter, setter))));
|
||||||
|
|
||||||
let getter = make_getter(&name);
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
let setter = make_setter(&name);
|
|
||||||
let rhs = Expr::Property(Box::new(((name.into(), getter, setter), pos)));
|
|
||||||
|
|
||||||
Expr::Dot(Box::new(BinaryExpr {
|
|
||||||
lhs,
|
|
||||||
rhs,
|
|
||||||
pos: op_pos,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
// 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(
|
||||||
|
Box::new(BinaryExpr {
|
||||||
lhs: x.lhs.into_property(),
|
lhs: x.lhs.into_property(),
|
||||||
rhs: x.rhs,
|
rhs: x.rhs,
|
||||||
pos: x.pos,
|
}),
|
||||||
}));
|
pos,
|
||||||
Expr::Dot(Box::new(BinaryExpr {
|
);
|
||||||
lhs,
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
rhs,
|
|
||||||
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(
|
||||||
|
Box::new(BinaryExpr {
|
||||||
lhs: x.lhs.into_property(),
|
lhs: x.lhs.into_property(),
|
||||||
rhs: x.rhs,
|
rhs: x.rhs,
|
||||||
pos: x.pos,
|
}),
|
||||||
}));
|
pos,
|
||||||
Expr::Dot(Box::new(BinaryExpr {
|
);
|
||||||
lhs,
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
rhs,
|
|
||||||
pos: op_pos,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
// lhs.Fn() or lhs.eval()
|
// lhs.Fn() or lhs.eval()
|
||||||
(_, Expr::FnCall(x))
|
(_, Expr::FnCall(x, pos))
|
||||||
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(pos));
|
||||||
}
|
}
|
||||||
// lhs.func!(...)
|
// lhs.func!(...)
|
||||||
(_, Expr::FnCall(x)) if (x.0).2 => {
|
(_, 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.0).3))
|
.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())),
|
||||||
})
|
})
|
||||||
@ -1264,10 +1263,10 @@ 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(_, _))
|
||||||
| (_, x @ Expr::True(_))
|
| (_, x @ Expr::True(_))
|
||||||
| (_, x @ Expr::False(_))
|
| (_, x @ Expr::False(_))
|
||||||
| (_, x @ Expr::Unit(_)) => {
|
| (_, x @ Expr::Unit(_)) => {
|
||||||
@ -1278,7 +1277,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 +1286,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(),
|
||||||
)
|
)
|
||||||
@ -1306,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(
|
||||||
@ -1317,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(),
|
||||||
)
|
)
|
||||||
@ -1339,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(),
|
||||||
)
|
)
|
||||||
@ -1358,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(),
|
||||||
)
|
)
|
||||||
@ -1393,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.
|
||||||
@ -1480,7 +1476,13 @@ 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,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
let mut args = StaticVec::new();
|
let mut args = StaticVec::new();
|
||||||
args.push(root);
|
args.push(root);
|
||||||
@ -1497,35 +1499,62 @@ 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
|
||||||
|
}),
|
||||||
|
pos,
|
||||||
|
),
|
||||||
|
|
||||||
// '!=' 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
|
||||||
|
}),
|
||||||
|
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((op, None, hash, args, cmp_def))),
|
| Token::GreaterThanEqualsTo => Expr::FnCall(
|
||||||
|
Box::new(FnCallInfo {
|
||||||
|
hash,
|
||||||
|
args,
|
||||||
|
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(
|
||||||
|
Box::new(BinaryExpr {
|
||||||
lhs: current_lhs,
|
lhs: current_lhs,
|
||||||
rhs,
|
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(
|
||||||
|
Box::new(BinaryExpr {
|
||||||
lhs: current_lhs,
|
lhs: current_lhs,
|
||||||
rhs,
|
rhs,
|
||||||
|
}),
|
||||||
pos,
|
pos,
|
||||||
}))
|
)
|
||||||
}
|
}
|
||||||
Token::In => {
|
Token::In => {
|
||||||
let rhs = args.pop().unwrap();
|
let rhs = args.pop().unwrap();
|
||||||
@ -1542,8 +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
|
||||||
let op = (op.0, false, op.2, op.3);
|
Expr::FnCall(
|
||||||
Expr::FnCall(Box::new((op, None, hash, args, None)))
|
Box::new(FnCallInfo {
|
||||||
|
hash,
|
||||||
|
args,
|
||||||
|
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)),
|
||||||
@ -1617,7 +1653,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() {
|
||||||
@ -1636,11 +1672,13 @@ fn parse_custom_syntax(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Expr::Custom(Box::new(CustomExpr {
|
Ok(Expr::Custom(
|
||||||
|
Box::new(CustomExpr {
|
||||||
keywords: exprs,
|
keywords: exprs,
|
||||||
func: syntax.func.clone(),
|
func: syntax.func.clone(),
|
||||||
|
}),
|
||||||
pos,
|
pos,
|
||||||
})))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression.
|
/// Parse an expression.
|
||||||
@ -2464,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((
|
let expr = Expr::FnCall(
|
||||||
(KEYWORD_FN_PTR_CURRY.into(), false, false, pos),
|
Box::new(FnCallInfo {
|
||||||
None,
|
name: KEYWORD_FN_PTR_CURRY.into(),
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
None,
|
..Default::default()
|
||||||
)));
|
}),
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
|
||||||
// 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 +2519,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 +2795,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)),
|
||||||
@ -2771,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
|
||||||
}
|
}
|
||||||
@ -2787,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(
|
||||||
|
Box::new(
|
||||||
items
|
items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, expr)| (k, expr.unwrap()))
|
.map(|(k, expr)| (k, expr.unwrap()))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
),
|
||||||
pos,
|
pos,
|
||||||
))))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user