Code structure refactor.
This commit is contained in:
804
src/ast.rs
804
src/ast.rs
@@ -1,15 +1,124 @@
|
||||
//! Module defining the AST (abstract syntax tree).
|
||||
|
||||
use crate::fn_native::Shared;
|
||||
use crate::module::Module;
|
||||
use crate::parser::{FnAccess, ScriptFnDef, Stmt};
|
||||
use crate::dynamic::{Dynamic, Union};
|
||||
use crate::fn_native::{FnPtr, Shared};
|
||||
use crate::module::{Module, ModuleRef};
|
||||
use crate::syntax::FnCustomSyntaxEval;
|
||||
use crate::token::{Position, Token};
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::StaticVec;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::engine::Array;
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::engine::{make_getter, make_setter, Map};
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
num::NonZeroUsize,
|
||||
ops::{Add, AddAssign},
|
||||
string::String,
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::stdlib::ops::Neg;
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
use crate::stdlib::collections::HashSet;
|
||||
|
||||
/// A type representing the access mode of a scripted function.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum FnAccess {
|
||||
/// Public function.
|
||||
Public,
|
||||
/// Private function.
|
||||
Private,
|
||||
}
|
||||
|
||||
impl fmt::Display for FnAccess {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Private => write!(f, "private"),
|
||||
Self::Public => write!(f, "public"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FnAccess {
|
||||
/// Is this access mode private?
|
||||
#[inline(always)]
|
||||
pub fn is_private(self) -> bool {
|
||||
match self {
|
||||
Self::Public => false,
|
||||
Self::Private => true,
|
||||
}
|
||||
}
|
||||
/// Is this access mode public?
|
||||
#[inline(always)]
|
||||
pub fn is_public(self) -> bool {
|
||||
match self {
|
||||
Self::Public => true,
|
||||
Self::Private => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// _[INTERNALS]_ A type containing information on a scripted function.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScriptFnDef {
|
||||
/// Function name.
|
||||
pub name: ImmutableString,
|
||||
/// Function access mode.
|
||||
pub access: FnAccess,
|
||||
/// Names of function parameters.
|
||||
pub params: StaticVec<String>,
|
||||
/// Access to external variables.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
pub externals: HashSet<String>,
|
||||
/// Function body.
|
||||
pub body: Stmt,
|
||||
/// Position of the function definition.
|
||||
pub pos: Position,
|
||||
/// Encapsulated running environment, if any.
|
||||
pub lib: Option<Shared<Module>>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ScriptFnDef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}{}({})",
|
||||
if self.access.is_private() {
|
||||
"private "
|
||||
} else {
|
||||
""
|
||||
},
|
||||
self.name,
|
||||
self.params
|
||||
.iter()
|
||||
.map(|s| s.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||
///
|
||||
/// # Thread Safety
|
||||
@@ -73,6 +182,7 @@ impl AST {
|
||||
/// No statements are cloned.
|
||||
///
|
||||
/// This operation is cheap because functions are shared.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
pub fn clone_functions_only(&self) -> Self {
|
||||
self.clone_functions_only_filtered(|_, _, _| true)
|
||||
@@ -82,6 +192,7 @@ impl AST {
|
||||
/// No statements are cloned.
|
||||
///
|
||||
/// This operation is cheap because functions are shared.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
pub fn clone_functions_only_filtered(
|
||||
&self,
|
||||
@@ -425,3 +536,690 @@ impl AsRef<Module> for AST {
|
||||
self.lib()
|
||||
}
|
||||
}
|
||||
|
||||
/// An identifier containing a string name and a position.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct Ident {
|
||||
pub name: String,
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
/// Create a new `Identifier`.
|
||||
pub fn new(name: String, pos: Position) -> Self {
|
||||
Self { name, pos }
|
||||
}
|
||||
}
|
||||
|
||||
/// An identifier containing an immutable name and a position.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct IdentX {
|
||||
pub name: ImmutableString,
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
impl IdentX {
|
||||
/// Create a new `Identifier`.
|
||||
pub fn new(name: impl Into<ImmutableString>, pos: Position) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// _[INTERNALS]_ A type encapsulating the mode of a `return`/`throw` statement.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||
pub enum ReturnType {
|
||||
/// `return` statement.
|
||||
Return,
|
||||
/// `throw` statement.
|
||||
Exception,
|
||||
}
|
||||
|
||||
/// _[INTERNALS]_ A Rhai statement.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Each variant is at most one pointer in size (for speed),
|
||||
/// with everything being allocated together in one single tuple.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub enum Stmt {
|
||||
/// No-op.
|
||||
Noop(Position),
|
||||
/// if expr { stmt } else { stmt }
|
||||
IfThenElse(Expr, Box<(Stmt, Option<Stmt>)>, Position),
|
||||
/// while expr { stmt }
|
||||
While(Expr, Box<Stmt>, Position),
|
||||
/// loop { stmt }
|
||||
Loop(Box<Stmt>, Position),
|
||||
/// for id in expr { stmt }
|
||||
For(Expr, Box<(String, Stmt)>, Position),
|
||||
/// let id = expr
|
||||
Let(Box<Ident>, Option<Expr>, Position),
|
||||
/// const id = expr
|
||||
Const(Box<Ident>, Option<Expr>, Position),
|
||||
/// expr op= expr
|
||||
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
|
||||
/// { stmt; ... }
|
||||
Block(Vec<Stmt>, Position),
|
||||
/// try { stmt; ... } catch ( var ) { stmt; ... }
|
||||
TryCatch(Box<(Stmt, Option<Ident>, Stmt, (Position, Position))>),
|
||||
/// expr
|
||||
Expr(Expr),
|
||||
/// continue
|
||||
Continue(Position),
|
||||
/// break
|
||||
Break(Position),
|
||||
/// return/throw
|
||||
ReturnWithVal((ReturnType, Position), Option<Expr>, Position),
|
||||
/// import expr as var
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Import(Expr, Option<Box<IdentX>>, Position),
|
||||
/// export var as var, ...
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Export(Vec<(Ident, Option<Ident>)>, Position),
|
||||
/// Convert a variable to shared.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Share(Ident),
|
||||
}
|
||||
|
||||
impl Default for Stmt {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::Noop(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
/// Is this statement `Noop`?
|
||||
pub fn is_noop(&self) -> bool {
|
||||
match self {
|
||||
Self::Noop(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `Position` of this statement.
|
||||
pub fn position(&self) -> Position {
|
||||
match self {
|
||||
Self::Noop(pos)
|
||||
| Self::Continue(pos)
|
||||
| Self::Break(pos)
|
||||
| Self::Block(_, pos)
|
||||
| Self::Assignment(_, pos)
|
||||
| Self::IfThenElse(_, _, pos)
|
||||
| Self::While(_, _, pos)
|
||||
| Self::Loop(_, pos)
|
||||
| Self::For(_, _, pos)
|
||||
| Self::ReturnWithVal((_, pos), _, _) => *pos,
|
||||
|
||||
Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos,
|
||||
Self::TryCatch(x) => (x.3).0,
|
||||
|
||||
Self::Expr(x) => x.position(),
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Import(_, _, pos) => *pos,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Export(_, pos) => *pos,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(Ident { pos, .. }) => *pos,
|
||||
}
|
||||
}
|
||||
|
||||
/// Override the `Position` of this statement.
|
||||
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
||||
match self {
|
||||
Self::Noop(pos)
|
||||
| Self::Continue(pos)
|
||||
| Self::Break(pos)
|
||||
| Self::Block(_, pos)
|
||||
| Self::Assignment(_, pos)
|
||||
| Self::IfThenElse(_, _, pos)
|
||||
| Self::While(_, _, pos)
|
||||
| Self::Loop(_, pos)
|
||||
| Self::For(_, _, pos)
|
||||
| Self::ReturnWithVal((_, pos), _, _) => *pos = new_pos,
|
||||
|
||||
Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos = new_pos,
|
||||
Self::TryCatch(x) => (x.3).0 = new_pos,
|
||||
|
||||
Self::Expr(x) => {
|
||||
x.set_position(new_pos);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Import(_, _, pos) => *pos = new_pos,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Export(_, pos) => *pos = new_pos,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(Ident { pos, .. }) => *pos = new_pos,
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
|
||||
pub fn is_self_terminated(&self) -> bool {
|
||||
match self {
|
||||
Self::IfThenElse(_, _, _)
|
||||
| Self::While(_, _, _)
|
||||
| Self::Loop(_, _)
|
||||
| Self::For(_, _, _)
|
||||
| Self::Block(_, _)
|
||||
| Self::TryCatch(_) => true,
|
||||
|
||||
// A No-op requires a semicolon in order to know it is an empty statement!
|
||||
Self::Noop(_) => false,
|
||||
|
||||
Self::Let(_, _, _)
|
||||
| Self::Const(_, _, _)
|
||||
| Self::Assignment(_, _)
|
||||
| Self::Expr(_)
|
||||
| Self::Continue(_)
|
||||
| Self::Break(_)
|
||||
| Self::ReturnWithVal(_, _, _) => false,
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this statement _pure_?
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::Noop(_) => true,
|
||||
Self::Expr(expr) => expr.is_pure(),
|
||||
Self::IfThenElse(condition, x, _) if x.1.is_some() => {
|
||||
condition.is_pure() && x.0.is_pure() && x.1.as_ref().unwrap().is_pure()
|
||||
}
|
||||
Self::IfThenElse(condition, x, _) => condition.is_pure() && x.0.is_pure(),
|
||||
Self::While(condition, block, _) => condition.is_pure() && block.is_pure(),
|
||||
Self::Loop(block, _) => block.is_pure(),
|
||||
Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
|
||||
Self::Let(_, _, _) | Self::Const(_, _, _) | Self::Assignment(_, _) => false,
|
||||
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
||||
Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false,
|
||||
Self::TryCatch(x) => x.0.is_pure() && x.2.is_pure(),
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Import(_, _, _) => false,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Export(_, _) => false,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// _[INTERNALS]_ A type wrapping a custom syntax definition.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[derive(Clone)]
|
||||
pub struct CustomExpr {
|
||||
pub(crate) keywords: StaticVec<Expr>,
|
||||
pub(crate) func: Shared<FnCustomSyntaxEval>,
|
||||
pub(crate) pos: Position,
|
||||
}
|
||||
|
||||
impl fmt::Debug for CustomExpr {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.keywords, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for CustomExpr {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.keywords.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomExpr {
|
||||
/// Get the keywords for this `CustomExpr`.
|
||||
#[inline(always)]
|
||||
pub fn keywords(&self) -> &[Expr] {
|
||||
&self.keywords
|
||||
}
|
||||
/// Get the implementation function for this `CustomExpr`.
|
||||
#[inline(always)]
|
||||
pub fn func(&self) -> &FnCustomSyntaxEval {
|
||||
self.func.as_ref()
|
||||
}
|
||||
/// Get the position of this `CustomExpr`.
|
||||
#[inline(always)]
|
||||
pub fn position(&self) -> Position {
|
||||
self.pos
|
||||
}
|
||||
}
|
||||
|
||||
/// _[INTERNALS]_ A type wrapping a floating-point number.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This type is mainly used to provide a standard `Hash` implementation
|
||||
/// to floating-point numbers, allowing `Expr` to derive `Hash` automatically.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[derive(Debug, PartialEq, PartialOrd, Clone)]
|
||||
pub struct FloatWrapper(pub FLOAT, pub Position);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl Hash for FloatWrapper {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write(&self.0.to_le_bytes());
|
||||
self.1.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl Neg for FloatWrapper {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
Self(-self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl From<(INT, Position)> for FloatWrapper {
|
||||
fn from((value, pos): (INT, Position)) -> Self {
|
||||
Self(value as FLOAT, pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// A binary expression structure.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct BinaryExpr {
|
||||
pub lhs: Expr,
|
||||
pub rhs: Expr,
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
/// _[INTERNALS]_ An expression sub-tree.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Each variant is at most one pointer in size (for speed),
|
||||
/// with everything being allocated together in one single tuple.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub enum Expr {
|
||||
/// Integer constant.
|
||||
IntegerConstant(Box<(INT, Position)>),
|
||||
/// Floating-point constant.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
FloatConstant(Box<FloatWrapper>),
|
||||
/// Character constant.
|
||||
CharConstant(Box<(char, Position)>),
|
||||
/// String constant.
|
||||
StringConstant(Box<IdentX>),
|
||||
/// FnPtr constant.
|
||||
FnPointer(Box<IdentX>),
|
||||
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
||||
Variable(Box<(Ident, Option<Box<ModuleRef>>, u64, Option<NonZeroUsize>)>),
|
||||
/// Property access.
|
||||
Property(Box<((ImmutableString, String, String), Position)>),
|
||||
/// { stmt }
|
||||
Stmt(Box<(Stmt, Position)>),
|
||||
/// Wrapped expression - should not be optimized away.
|
||||
Expr(Box<Expr>),
|
||||
/// func(expr, ... ) - ((function name, native_only, capture, position), optional modules, hash, arguments, optional default value)
|
||||
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
||||
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||
FnCall(
|
||||
Box<(
|
||||
(Cow<'static, str>, bool, bool, Position),
|
||||
Option<Box<ModuleRef>>,
|
||||
u64,
|
||||
StaticVec<Expr>,
|
||||
Option<bool>, // Default value is `bool` in order for `Expr` to be `Hash`.
|
||||
)>,
|
||||
),
|
||||
/// lhs.rhs
|
||||
Dot(Box<BinaryExpr>),
|
||||
/// expr[expr]
|
||||
Index(Box<BinaryExpr>),
|
||||
/// [ expr, ... ]
|
||||
Array(Box<(StaticVec<Expr>, Position)>),
|
||||
/// #{ name:expr, ... }
|
||||
Map(Box<(StaticVec<(IdentX, Expr)>, Position)>),
|
||||
/// lhs in rhs
|
||||
In(Box<BinaryExpr>),
|
||||
/// lhs && rhs
|
||||
And(Box<BinaryExpr>),
|
||||
/// lhs || rhs
|
||||
Or(Box<BinaryExpr>),
|
||||
/// true
|
||||
True(Position),
|
||||
/// false
|
||||
False(Position),
|
||||
/// ()
|
||||
Unit(Position),
|
||||
/// Custom syntax
|
||||
Custom(Box<CustomExpr>),
|
||||
}
|
||||
|
||||
impl Default for Expr {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::Unit(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
/// Get the type of an expression.
|
||||
///
|
||||
/// Returns `None` if the expression's result type is not constant.
|
||||
pub fn get_type_id(&self) -> Option<TypeId> {
|
||||
Some(match self {
|
||||
Self::Expr(x) => return x.get_type_id(),
|
||||
|
||||
Self::IntegerConstant(_) => TypeId::of::<INT>(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(_) => TypeId::of::<FLOAT>(),
|
||||
Self::CharConstant(_) => TypeId::of::<char>(),
|
||||
Self::StringConstant(_) => TypeId::of::<ImmutableString>(),
|
||||
Self::FnPointer(_) => TypeId::of::<FnPtr>(),
|
||||
Self::True(_) | Self::False(_) | Self::In(_) | Self::And(_) | Self::Or(_) => {
|
||||
TypeId::of::<bool>()
|
||||
}
|
||||
Self::Unit(_) => TypeId::of::<()>(),
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Array(_) => TypeId::of::<Array>(),
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Self::Map(_) => TypeId::of::<Map>(),
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the `Dynamic` value of a constant expression.
|
||||
///
|
||||
/// Returns `None` if the expression is not constant.
|
||||
pub fn get_constant_value(&self) -> Option<Dynamic> {
|
||||
Some(match self {
|
||||
Self::Expr(x) => return x.get_constant_value(),
|
||||
|
||||
Self::IntegerConstant(x) => x.0.into(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(x) => x.0.into(),
|
||||
Self::CharConstant(x) => x.0.into(),
|
||||
Self::StringConstant(x) => x.name.clone().into(),
|
||||
Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
|
||||
x.name.clone(),
|
||||
Default::default(),
|
||||
)))),
|
||||
Self::True(_) => true.into(),
|
||||
Self::False(_) => false.into(),
|
||||
Self::Unit(_) => ().into(),
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Array(x) if x.0.iter().all(Self::is_constant) => Dynamic(Union::Array(Box::new(
|
||||
x.0.iter()
|
||||
.map(|v| v.get_constant_value().unwrap())
|
||||
.collect(),
|
||||
))),
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Self::Map(x) if x.0.iter().all(|(_, v)| v.is_constant()) => {
|
||||
Dynamic(Union::Map(Box::new(
|
||||
x.0.iter()
|
||||
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap()))
|
||||
.collect(),
|
||||
)))
|
||||
}
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Is the expression a simple variable access?
|
||||
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
|
||||
match self {
|
||||
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).name.as_str()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `Position` of the expression.
|
||||
pub fn position(&self) -> Position {
|
||||
match self {
|
||||
Self::Expr(x) => x.position(),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(x) => x.1,
|
||||
|
||||
Self::IntegerConstant(x) => x.1,
|
||||
Self::CharConstant(x) => x.1,
|
||||
Self::StringConstant(x) => x.pos,
|
||||
Self::FnPointer(x) => x.pos,
|
||||
Self::Array(x) => x.1,
|
||||
Self::Map(x) => x.1,
|
||||
Self::Property(x) => x.1,
|
||||
Self::Stmt(x) => x.1,
|
||||
Self::Variable(x) => (x.0).pos,
|
||||
Self::FnCall(x) => (x.0).3,
|
||||
|
||||
Self::And(x) | Self::Or(x) | Self::In(x) => x.pos,
|
||||
|
||||
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos,
|
||||
|
||||
Self::Dot(x) | Self::Index(x) => x.lhs.position(),
|
||||
|
||||
Self::Custom(x) => x.pos,
|
||||
}
|
||||
}
|
||||
|
||||
/// Override the `Position` of the expression.
|
||||
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
||||
match self {
|
||||
Self::Expr(x) => {
|
||||
x.set_position(new_pos);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(x) => x.1 = new_pos,
|
||||
|
||||
Self::IntegerConstant(x) => x.1 = new_pos,
|
||||
Self::CharConstant(x) => x.1 = new_pos,
|
||||
Self::StringConstant(x) => x.pos = new_pos,
|
||||
Self::FnPointer(x) => x.pos = new_pos,
|
||||
Self::Array(x) => x.1 = new_pos,
|
||||
Self::Map(x) => x.1 = new_pos,
|
||||
Self::Variable(x) => (x.0).pos = new_pos,
|
||||
Self::Property(x) => x.1 = new_pos,
|
||||
Self::Stmt(x) => x.1 = new_pos,
|
||||
Self::FnCall(x) => (x.0).3 = new_pos,
|
||||
Self::And(x) | Self::Or(x) | Self::In(x) => x.pos = new_pos,
|
||||
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos,
|
||||
Self::Dot(x) | Self::Index(x) => x.pos = new_pos,
|
||||
Self::Custom(x) => x.pos = new_pos,
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Is the expression pure?
|
||||
///
|
||||
/// A pure expression has no side effects.
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::Expr(x) => x.is_pure(),
|
||||
|
||||
Self::Array(x) => x.0.iter().all(Self::is_pure),
|
||||
|
||||
Self::Index(x) | Self::And(x) | Self::Or(x) | Self::In(x) => {
|
||||
x.lhs.is_pure() && x.rhs.is_pure()
|
||||
}
|
||||
|
||||
Self::Stmt(x) => x.0.is_pure(),
|
||||
|
||||
Self::Variable(_) => true,
|
||||
|
||||
_ => self.is_constant(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the expression the unit `()` literal?
|
||||
#[inline(always)]
|
||||
pub fn is_unit(&self) -> bool {
|
||||
match self {
|
||||
Self::Unit(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the expression a simple constant literal?
|
||||
pub fn is_literal(&self) -> bool {
|
||||
match self {
|
||||
Self::Expr(x) => x.is_literal(),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(_) => true,
|
||||
|
||||
Self::IntegerConstant(_)
|
||||
| Self::CharConstant(_)
|
||||
| Self::StringConstant(_)
|
||||
| Self::FnPointer(_)
|
||||
| Self::True(_)
|
||||
| Self::False(_)
|
||||
| Self::Unit(_) => true,
|
||||
|
||||
// An array literal is literal if all items are literals
|
||||
Self::Array(x) => x.0.iter().all(Self::is_literal),
|
||||
|
||||
// An map literal is literal if all items are literals
|
||||
Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_literal),
|
||||
|
||||
// Check in expression
|
||||
Self::In(x) => match (&x.lhs, &x.rhs) {
|
||||
(Self::StringConstant(_), Self::StringConstant(_))
|
||||
| (Self::CharConstant(_), Self::StringConstant(_)) => true,
|
||||
_ => false,
|
||||
},
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the expression a constant?
|
||||
pub fn is_constant(&self) -> bool {
|
||||
match self {
|
||||
Self::Expr(x) => x.is_constant(),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(_) => true,
|
||||
|
||||
Self::IntegerConstant(_)
|
||||
| Self::CharConstant(_)
|
||||
| Self::StringConstant(_)
|
||||
| Self::FnPointer(_)
|
||||
| Self::True(_)
|
||||
| Self::False(_)
|
||||
| Self::Unit(_) => true,
|
||||
|
||||
// An array literal is constant if all items are constant
|
||||
Self::Array(x) => x.0.iter().all(Self::is_constant),
|
||||
|
||||
// An map literal is constant if all items are constant
|
||||
Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant),
|
||||
|
||||
// Check in expression
|
||||
Self::In(x) => match (&x.lhs, &x.rhs) {
|
||||
(Self::StringConstant(_), Self::StringConstant(_))
|
||||
| (Self::CharConstant(_), Self::StringConstant(_)) => true,
|
||||
_ => false,
|
||||
},
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is a particular token allowed as a postfix operator to this expression?
|
||||
pub fn is_valid_postfix(&self, token: &Token) -> bool {
|
||||
match self {
|
||||
Self::Expr(x) => x.is_valid_postfix(token),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(_) => false,
|
||||
|
||||
Self::IntegerConstant(_)
|
||||
| Self::CharConstant(_)
|
||||
| Self::FnPointer(_)
|
||||
| Self::In(_)
|
||||
| Self::And(_)
|
||||
| Self::Or(_)
|
||||
| Self::True(_)
|
||||
| Self::False(_)
|
||||
| Self::Unit(_) => false,
|
||||
|
||||
Self::StringConstant(_)
|
||||
| Self::Stmt(_)
|
||||
| Self::FnCall(_)
|
||||
| Self::Dot(_)
|
||||
| Self::Index(_)
|
||||
| Self::Array(_)
|
||||
| Self::Map(_) => match token {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Token::LeftBracket => true,
|
||||
_ => false,
|
||||
},
|
||||
|
||||
Self::Variable(_) => match token {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Token::LeftBracket => true,
|
||||
Token::LeftParen => true,
|
||||
Token::Bang => true,
|
||||
Token::DoubleColon => true,
|
||||
_ => false,
|
||||
},
|
||||
|
||||
Self::Property(_) => match token {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Token::LeftBracket => true,
|
||||
Token::LeftParen => true,
|
||||
_ => false,
|
||||
},
|
||||
|
||||
Self::Custom(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline]
|
||||
pub(crate) fn into_property(self) -> Self {
|
||||
match self {
|
||||
Self::Variable(x) if x.1.is_none() => {
|
||||
let Ident { name, pos } = x.0;
|
||||
let getter = make_getter(&name);
|
||||
let setter = make_setter(&name);
|
||||
Self::Property(Box::new(((name.into(), getter, setter), pos)))
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user