rhai/src/ast/expr.rs

918 lines
30 KiB
Rust
Raw Normal View History

2021-12-17 09:07:13 +01:00
//! Module defining script expressions.
2022-02-25 04:42:59 +01:00
use super::{ASTFlags, ASTNode, Ident, Stmt, StmtBlock};
2022-01-08 07:00:41 +01:00
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
2021-12-17 09:07:13 +01:00
use crate::func::hashing::ALT_ZERO_HASH;
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
2022-01-08 07:00:41 +01:00
use crate::{calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, StaticVec, INT};
2021-12-17 09:07:13 +01:00
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
collections::BTreeMap,
fmt,
hash::Hash,
2022-01-08 07:00:41 +01:00
iter::once,
2021-12-17 09:07:13 +01:00
num::{NonZeroU8, NonZeroUsize},
};
#[cfg(not(feature = "no_float"))]
use std::str::FromStr;
#[cfg(not(feature = "no_float"))]
use num_traits::float::FloatCore as Float;
2021-12-17 09:07:13 +01:00
/// _(internals)_ A binary expression.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub struct BinaryExpr {
/// LHS expression.
pub lhs: Expr,
/// RHS expression.
pub rhs: Expr,
}
impl From<(Expr, Expr)> for BinaryExpr {
#[inline(always)]
fn from(value: (Expr, Expr)) -> Self {
Self {
lhs: value.0,
rhs: value.1,
}
}
}
2021-12-17 09:07:13 +01:00
/// _(internals)_ A custom syntax expression.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub struct CustomExpr {
/// List of keywords.
pub inputs: StaticVec<Expr>,
/// List of tokens actually parsed.
pub tokens: StaticVec<Identifier>,
/// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement
/// (e.g. introducing a new variable)?
pub scope_may_be_changed: bool,
/// Is this custom syntax self-terminated?
pub self_terminated: bool,
}
impl CustomExpr {
/// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)?
///
/// A self-terminated custom syntax always ends in `$block$`, `}` or `;`
#[must_use]
#[inline(always)]
pub const fn is_self_terminated(&self) -> bool {
self.self_terminated
}
}
/// _(internals)_ A set of function call hashes. Exported under the `internals` feature only.
///
/// Two separate hashes are pre-calculated because of the following patterns:
///
2021-12-25 16:49:14 +01:00
/// ```js
2021-12-17 09:07:13 +01:00
/// func(a, b, c); // Native: func(a, b, c) - 3 parameters
/// // Script: func(a, b, c) - 3 parameters
///
/// a.func(b, c); // Native: func(&mut a, b, c) - 3 parameters
/// // Script: func(b, c) - 2 parameters
/// ```
///
/// For normal function calls, the native hash equals the script hash.
///
/// For method-style calls, the script hash contains one fewer parameter.
///
/// Function call hashes are used in the following manner:
///
/// * First, the script hash is tried, which contains only the called function's name plus the
/// number of parameters.
///
/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is
/// then used to search for a native function. In other words, a complete native function call
/// hash always contains the called function's name plus the types of the arguments. This is due
/// to possible function overloading for different parameter types.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)]
pub struct FnCallHashes {
/// Pre-calculated hash for a script-defined function (zero if native functions only).
#[cfg(not(feature = "no_function"))]
pub script: u64,
/// Pre-calculated hash for a native Rust function with no parameter types.
pub native: u64,
}
impl fmt::Debug for FnCallHashes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "no_function"))]
if self.script != 0 {
return if self.script == self.native {
fmt::Debug::fmt(&self.native, f)
} else {
write!(f, "({}, {})", self.script, self.native)
};
}
write!(f, "{} (native only)", self.native)
}
}
impl From<u64> for FnCallHashes {
#[inline(always)]
fn from(hash: u64) -> Self {
let hash = if hash == 0 { ALT_ZERO_HASH } else { hash };
Self {
#[cfg(not(feature = "no_function"))]
script: hash,
native: hash,
}
}
}
impl FnCallHashes {
/// Create a [`FnCallHashes`] with only the native Rust hash.
#[inline(always)]
#[must_use]
pub const fn from_native(hash: u64) -> Self {
Self {
#[cfg(not(feature = "no_function"))]
script: 0,
native: if hash == 0 { ALT_ZERO_HASH } else { hash },
}
}
/// Create a [`FnCallHashes`] with both native Rust and script function hashes.
#[inline(always)]
#[must_use]
pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
Self {
#[cfg(not(feature = "no_function"))]
script: if script == 0 { ALT_ZERO_HASH } else { script },
native: if native == 0 { ALT_ZERO_HASH } else { native },
}
}
/// Is this [`FnCallHashes`] native Rust only?
#[inline(always)]
#[must_use]
pub const fn is_native_only(&self) -> bool {
#[cfg(not(feature = "no_function"))]
return self.script == 0;
#[cfg(feature = "no_function")]
return true;
}
}
/// _(internals)_ A function call.
/// Exported under the `internals` feature only.
2022-01-31 06:38:27 +01:00
#[derive(Clone, Default, Hash)]
2021-12-17 09:07:13 +01:00
pub struct FnCallExpr {
/// Namespace of the function, if any.
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
pub namespace: Option<crate::module::Namespace>,
2021-12-17 09:07:13 +01:00
/// Function name.
pub name: Identifier,
/// Pre-calculated hashes.
pub hashes: FnCallHashes,
/// List of function call argument expressions.
pub args: StaticVec<Expr>,
/// Does this function call capture the parent scope?
pub capture_parent_scope: bool,
2022-02-04 05:04:33 +01:00
/// [Position] of the function name.
pub pos: Position,
2021-12-17 09:07:13 +01:00
}
2022-01-31 06:38:27 +01:00
impl fmt::Debug for FnCallExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ff = f.debug_struct("FnCallExpr");
#[cfg(not(feature = "no_module"))]
if let Some(ref ns) = self.namespace {
ff.field("namespace", ns);
2022-01-31 06:38:27 +01:00
}
if self.capture_parent_scope {
ff.field("capture_parent_scope", &self.capture_parent_scope);
}
ff.field("hash", &self.hashes)
.field("name", &self.name)
.field("args", &self.args);
2022-02-04 05:04:33 +01:00
ff.field("pos", &self.pos);
2022-01-31 06:38:27 +01:00
ff.finish()
}
}
2021-12-17 09:07:13 +01:00
impl FnCallExpr {
/// Does this function call contain a qualified namespace?
2022-01-29 04:09:43 +01:00
///
/// Always `false` under `no_module`.
2021-12-17 09:07:13 +01:00
#[inline(always)]
#[must_use]
pub const fn is_qualified(&self) -> bool {
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
return self.namespace.is_some();
#[cfg(feature = "no_module")]
return false;
2021-12-17 09:07:13 +01:00
}
/// Convert this into an [`Expr::FnCall`].
#[inline(always)]
#[must_use]
pub fn into_fn_call_expr(self, pos: Position) -> Expr {
Expr::FnCall(self.into(), pos)
}
}
/// A type that wraps a floating-point number and implements [`Hash`].
///
/// Not available under `no_float`.
#[cfg(not(feature = "no_float"))]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct FloatWrapper<F>(F);
#[cfg(not(feature = "no_float"))]
impl Hash for FloatWrapper<crate::FLOAT> {
#[inline(always)]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.to_ne_bytes().hash(state);
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> AsRef<F> for FloatWrapper<F> {
#[inline(always)]
fn as_ref(&self) -> &F {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> AsMut<F> for FloatWrapper<F> {
#[inline(always)]
fn as_mut(&mut self) -> &mut F {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> std::ops::Deref for FloatWrapper<F> {
type Target = F;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> std::ops::DerefMut for FloatWrapper<F> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + fmt::Debug> fmt::Debug for FloatWrapper<F> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abs = self.0.abs();
if abs.is_zero() {
f.write_str("0.0")
} else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into()
|| abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into()
{
write!(f, "{:e}", self.0)
} else {
fmt::Display::fmt(&self.0, f)?;
if abs.fract().is_zero() {
f.write_str(".0")?;
}
Ok(())
}
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> From<F> for FloatWrapper<F> {
#[inline(always)]
fn from(value: F) -> Self {
Self::new(value)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + FromStr> FromStr for FloatWrapper<F> {
type Err = <F as FromStr>::Err;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
2021-12-27 09:59:05 +01:00
F::from_str(s).map(Into::into)
2021-12-17 09:07:13 +01:00
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> FloatWrapper<F> {
/// Maximum floating-point number for natural display before switching to scientific notation.
pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10000000000000.0;
/// Minimum floating-point number for natural display before switching to scientific notation.
pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001;
/// Create a new [`FloatWrapper`].
#[inline(always)]
#[must_use]
pub fn new(value: F) -> Self {
Self(value)
}
}
#[cfg(not(feature = "no_float"))]
impl FloatWrapper<crate::FLOAT> {
/// Create a new [`FloatWrapper`].
#[inline(always)]
#[must_use]
pub const fn new_const(value: crate::FLOAT) -> Self {
Self(value)
}
}
/// _(internals)_ An expression sub-tree.
/// Exported under the `internals` feature only.
#[derive(Clone, Hash)]
pub enum Expr {
/// Dynamic constant.
///
/// Used to hold complex constants such as [`Array`][crate::Array] or [`Map`][crate::Map] for quick cloning.
/// Primitive data types should use the appropriate variants to avoid an allocation.
DynamicConstant(Box<Dynamic>, Position),
/// Boolean constant.
BoolConstant(bool, Position),
/// Integer constant.
IntegerConstant(INT, Position),
/// Floating-point constant.
#[cfg(not(feature = "no_float"))]
FloatConstant(FloatWrapper<crate::FLOAT>, Position),
/// Character constant.
CharConstant(char, Position),
/// [String][ImmutableString] constant.
StringConstant(ImmutableString, Position),
/// An interpolated [string][ImmutableString].
InterpolatedString(Box<StaticVec<Expr>>, Position),
/// [ expr, ... ]
Array(Box<StaticVec<Expr>>, Position),
/// #{ name:expr, ... }
Map(
Box<(StaticVec<(Ident, Expr)>, BTreeMap<Identifier, Dynamic>)>,
Position,
),
/// ()
Unit(Position),
/// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name)
///
/// The short index is [`u8`] which is used when the index is <= 255, which should be the vast
/// majority of cases (unless there are more than 255 variables defined!).
/// This is to avoid reading a pointer redirection during each variable access.
Variable(
Option<NonZeroU8>,
Position,
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
2022-01-29 06:37:58 +01:00
Box<(
Option<NonZeroUsize>,
Option<(crate::module::Namespace, u64)>,
Identifier,
)>,
2022-01-29 04:09:43 +01:00
#[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, (), Identifier)>,
2021-12-17 09:07:13 +01:00
),
/// Property access - ((getter, hash), (setter, hash), prop)
Property(
Box<((Identifier, u64), (Identifier, u64), ImmutableString)>,
Position,
2021-12-17 09:07:13 +01:00
),
/// xxx `.` method `(` expr `,` ... `)`
MethodCall(Box<FnCallExpr>, Position),
2021-12-17 09:07:13 +01:00
/// { [statement][Stmt] ... }
Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)`
FnCall(Box<FnCallExpr>, Position),
2022-02-25 04:42:59 +01:00
/// lhs `.` rhs
Dot(Box<BinaryExpr>, ASTFlags, Position),
/// lhs `[` rhs `]`
///
/// ### Flags
///
/// [`NONE`][ASTFlags::NONE] = recurse into the indexing chain
/// [`BREAK`][ASTFlags::BREAK] = terminate the indexing chain
Index(Box<BinaryExpr>, ASTFlags, Position),
2021-12-17 09:07:13 +01:00
/// lhs `&&` rhs
And(Box<BinaryExpr>, Position),
/// lhs `||` rhs
Or(Box<BinaryExpr>, Position),
/// Custom syntax
Custom(Box<CustomExpr>, Position),
}
impl Default for Expr {
#[inline(always)]
fn default() -> Self {
Self::Unit(Position::NONE)
}
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2022-02-10 10:55:32 +01:00
let mut display_pos = format!(" @ {:?}", self.start_position());
2021-12-17 09:07:13 +01:00
match self {
2022-02-08 02:02:15 +01:00
Self::DynamicConstant(value, ..) => write!(f, "{:?}", value),
Self::BoolConstant(value, ..) => write!(f, "{:?}", value),
Self::IntegerConstant(value, ..) => write!(f, "{:?}", value),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
Self::FloatConstant(value, ..) => write!(f, "{:?}", value),
Self::CharConstant(value, ..) => write!(f, "{:?}", value),
Self::StringConstant(value, ..) => write!(f, "{:?}", value),
2022-02-08 02:46:14 +01:00
Self::Unit(..) => f.write_str("()"),
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::InterpolatedString(x, ..) => {
2021-12-17 09:07:13 +01:00
f.write_str("InterpolatedString")?;
return f.debug_list().entries(x.iter()).finish();
}
2022-02-08 02:02:15 +01:00
Self::Array(x, ..) => {
2021-12-17 09:07:13 +01:00
f.write_str("Array")?;
f.debug_list().entries(x.iter()).finish()
}
2022-02-08 02:02:15 +01:00
Self::Map(x, ..) => {
2021-12-17 09:07:13 +01:00
f.write_str("Map")?;
f.debug_map()
.entries(x.0.iter().map(|(k, v)| (k, v)))
.finish()
}
Self::Variable(i, _, x) => {
f.write_str("Variable(")?;
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-10 10:55:32 +01:00
if let Some((ref namespace, ..)) = x.1 {
write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())?;
let pos = namespace.position();
if !pos.is_none() {
display_pos = format!(" @ {:?}", pos);
}
2021-12-17 09:07:13 +01:00
}
f.write_str(&x.2)?;
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
2022-02-10 10:55:32 +01:00
write!(f, " #{}", n)?;
2021-12-17 09:07:13 +01:00
}
f.write_str(")")
}
2022-02-08 02:02:15 +01:00
Self::Property(x, ..) => write!(f, "Property({})", x.2),
Self::MethodCall(x, ..) => f.debug_tuple("MethodCall").field(x).finish(),
2021-12-17 09:07:13 +01:00
Self::Stmt(x) => {
2022-02-10 10:55:32 +01:00
let pos = x.span();
if !pos.is_none() {
display_pos = format!(" @ {:?}", pos);
}
2021-12-17 09:07:13 +01:00
f.write_str("ExprStmtBlock")?;
f.debug_list().entries(x.iter()).finish()
}
2022-02-08 02:02:15 +01:00
Self::FnCall(x, ..) => fmt::Debug::fmt(x, f),
2021-12-17 09:07:13 +01:00
Self::Index(x, term, pos) => {
2022-02-10 10:55:32 +01:00
if !pos.is_none() {
display_pos = format!(" @ {:?}", pos);
}
2021-12-17 09:07:13 +01:00
f.debug_struct("Index")
.field("lhs", &x.lhs)
.field("rhs", &x.rhs)
.field("terminate", term)
.finish()
}
Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => {
let op_name = match self {
2022-02-08 02:02:15 +01:00
Self::Dot(..) => "Dot",
Self::And(..) => "And",
Self::Or(..) => "Or",
2021-12-30 05:19:41 +01:00
expr => unreachable!(
"Self::Dot or Self::And or Self::Or expected but gets {:?}",
expr
),
2021-12-17 09:07:13 +01:00
};
2022-02-10 10:55:32 +01:00
if !pos.is_none() {
display_pos = format!(" @ {:?}", pos);
}
2021-12-17 09:07:13 +01:00
f.debug_struct(op_name)
.field("lhs", &x.lhs)
.field("rhs", &x.rhs)
.finish()
}
2022-02-08 02:02:15 +01:00
Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(),
2021-12-17 09:07:13 +01:00
}?;
2022-02-10 10:55:32 +01:00
f.write_str(&display_pos)
2021-12-17 09:07:13 +01:00
}
}
impl Expr {
/// Get the [`Dynamic`] value of a literal constant expression.
///
/// Returns [`None`] if the expression is not a literal constant.
#[inline]
#[must_use]
pub fn get_literal_value(&self) -> Option<Dynamic> {
Some(match self {
2022-02-08 02:02:15 +01:00
Self::DynamicConstant(x, ..) => x.as_ref().clone(),
Self::IntegerConstant(x, ..) => (*x).into(),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
Self::FloatConstant(x, ..) => (*x).into(),
Self::CharConstant(x, ..) => (*x).into(),
Self::StringConstant(x, ..) => x.clone().into(),
Self::BoolConstant(x, ..) => (*x).into(),
2022-02-08 02:46:14 +01:00
Self::Unit(..) => Dynamic::UNIT,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_index"))]
2022-02-08 02:02:15 +01:00
Self::Array(x, ..) if self.is_constant() => {
2021-12-17 09:07:13 +01:00
let mut arr = crate::Array::with_capacity(x.len());
2022-01-06 04:07:52 +01:00
arr.extend(x.iter().map(|v| v.get_literal_value().unwrap()));
2021-12-17 09:07:13 +01:00
Dynamic::from_array(arr)
}
#[cfg(not(feature = "no_object"))]
2022-02-08 02:02:15 +01:00
Self::Map(x, ..) if self.is_constant() => {
2021-12-17 09:07:13 +01:00
Dynamic::from_map(x.0.iter().fold(x.1.clone(), |mut map, (k, v)| {
2022-01-06 04:07:52 +01:00
let value_ref = map.get_mut(k.name.as_str()).unwrap();
*value_ref = v.get_literal_value().unwrap();
2021-12-17 09:07:13 +01:00
map
}))
}
2022-01-08 07:00:41 +01:00
// Fn
2022-02-08 02:02:15 +01:00
Self::FnCall(ref x, ..)
2022-01-08 07:00:41 +01:00
if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR =>
{
2022-02-08 02:02:15 +01:00
if let Expr::StringConstant(ref s, ..) = x.args[0] {
2022-03-01 08:34:15 +01:00
FnPtr::new(s).ok()?.into()
2022-01-08 07:00:41 +01:00
} else {
return None;
}
}
2021-12-17 09:07:13 +01:00
// Binary operators
2022-02-08 02:02:15 +01:00
Self::FnCall(x, ..) if !x.is_qualified() && x.args.len() == 2 => {
match x.name.as_str() {
// x..y
OP_EXCLUSIVE_RANGE => {
if let Expr::IntegerConstant(ref start, ..) = x.args[0] {
if let Expr::IntegerConstant(ref end, ..) = x.args[1] {
(*start..*end).into()
} else {
return None;
}
2021-12-17 09:07:13 +01:00
} else {
return None;
}
}
2022-02-08 02:02:15 +01:00
// x..=y
OP_INCLUSIVE_RANGE => {
if let Expr::IntegerConstant(ref start, ..) = x.args[0] {
if let Expr::IntegerConstant(ref end, ..) = x.args[1] {
(*start..=*end).into()
} else {
return None;
}
2021-12-17 09:07:13 +01:00
} else {
return None;
}
}
2022-02-08 02:02:15 +01:00
_ => return None,
2021-12-17 09:07:13 +01:00
}
2022-02-08 02:02:15 +01:00
}
2021-12-17 09:07:13 +01:00
_ => return None,
})
}
/// Create an [`Expr`] from a [`Dynamic`] value.
#[inline]
#[must_use]
pub fn from_dynamic(value: Dynamic, pos: Position) -> Self {
match value.0 {
2022-02-08 02:02:15 +01:00
Union::Unit(..) => Self::Unit(pos),
Union::Bool(b, ..) => Self::BoolConstant(b, pos),
Union::Str(s, ..) => Self::StringConstant(s, pos),
Union::Char(c, ..) => Self::CharConstant(c, pos),
Union::Int(i, ..) => Self::IntegerConstant(i, pos),
2021-12-17 09:07:13 +01:00
#[cfg(feature = "decimal")]
2022-02-08 02:02:15 +01:00
Union::Decimal(value, ..) => Self::DynamicConstant(Box::new((*value).into()), pos),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
Union::Float(f, ..) => Self::FloatConstant(f, pos),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_index"))]
2022-02-08 02:02:15 +01:00
Union::Array(a, ..) => Self::DynamicConstant(Box::new((*a).into()), pos),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_object"))]
2022-02-08 02:02:15 +01:00
Union::Map(m, ..) => Self::DynamicConstant(Box::new((*m).into()), pos),
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
2022-01-08 07:00:41 +01:00
FnCallExpr {
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
2022-01-08 07:00:41 +01:00
namespace: None,
name: KEYWORD_FN_PTR.into(),
hashes: calc_fn_hash(f.fn_name(), 1).into(),
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
2022-01-08 07:00:41 +01:00
capture_parent_scope: false,
2022-02-04 05:04:33 +01:00
pos,
2022-01-08 07:00:41 +01:00
}
.into(),
pos,
),
2021-12-17 09:07:13 +01:00
_ => Self::DynamicConstant(value.into(), pos),
}
}
/// Is the expression a simple variable access?
2022-01-29 04:09:43 +01:00
///
/// `non_qualified` is ignored under `no_module`.
2021-12-17 09:07:13 +01:00
#[inline]
#[must_use]
pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool {
2022-01-29 04:09:43 +01:00
let _non_qualified = non_qualified;
2021-12-17 09:07:13 +01:00
match self {
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Variable(.., x) if _non_qualified && x.1.is_some() => false,
Self::Variable(..) => true,
2021-12-17 09:07:13 +01:00
_ => false,
}
}
/// Return the variable name if the expression a simple variable access.
2022-01-29 04:09:43 +01:00
///
/// `non_qualified` is ignored under `no_module`.
2021-12-17 09:07:13 +01:00
#[inline]
#[must_use]
pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> {
2022-01-29 04:09:43 +01:00
let _non_qualified = non_qualified;
2021-12-17 09:07:13 +01:00
match self {
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Variable(.., x) if _non_qualified && x.1.is_some() => None,
Self::Variable(.., x) => Some(x.2.as_str()),
2021-12-17 09:07:13 +01:00
_ => None,
}
}
/// Get the [position][Position] of the expression.
#[inline]
#[must_use]
pub const fn position(&self) -> Position {
match self {
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
Self::FloatConstant(.., pos) => *pos,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::DynamicConstant(.., pos)
| Self::BoolConstant(.., pos)
| Self::IntegerConstant(.., pos)
| Self::CharConstant(.., pos)
2021-12-17 09:07:13 +01:00
| Self::Unit(pos)
2022-02-08 02:02:15 +01:00
| Self::StringConstant(.., pos)
| Self::Array(.., pos)
| Self::Map(.., pos)
| Self::Variable(.., pos, _)
| Self::And(.., pos)
| Self::Or(.., pos)
| Self::Index(.., pos)
| Self::Dot(.., pos)
| Self::Custom(.., pos)
| Self::InterpolatedString(.., pos)
| Self::Property(.., pos) => *pos,
Self::FnCall(x, ..) | Self::MethodCall(x, ..) => x.pos,
2021-12-17 09:07:13 +01:00
2022-02-04 05:04:33 +01:00
Self::Stmt(x) => x.position(),
}
}
/// Get the starting [position][Position] of the expression.
/// For a binary expression, this will be the left-most LHS instead of the operator.
#[inline]
#[must_use]
2022-02-10 10:55:32 +01:00
pub fn start_position(&self) -> Position {
2022-02-04 05:04:33 +01:00
match self {
2022-02-10 10:55:32 +01:00
#[cfg(not(feature = "no_module"))]
Self::Variable(.., x) => {
if let Some((ref namespace, ..)) = x.1 {
namespace.position()
} else {
self.position()
}
}
2022-02-08 02:02:15 +01:00
Self::And(x, ..) | Self::Or(x, ..) | Self::Index(x, ..) | Self::Dot(x, ..) => {
2022-02-04 05:04:33 +01:00
x.lhs.start_position()
}
2022-02-08 02:02:15 +01:00
Self::FnCall(.., pos) => *pos,
2022-02-04 05:04:33 +01:00
_ => self.position(),
2021-12-17 09:07:13 +01:00
}
}
/// Override the [position][Position] of the expression.
#[inline]
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
Self::FloatConstant(.., pos) => *pos = new_pos,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::DynamicConstant(.., pos)
| Self::BoolConstant(.., pos)
| Self::IntegerConstant(.., pos)
| Self::CharConstant(.., pos)
2021-12-17 09:07:13 +01:00
| Self::Unit(pos)
2022-02-08 02:02:15 +01:00
| Self::StringConstant(.., pos)
| Self::Array(.., pos)
| Self::Map(.., pos)
| Self::And(.., pos)
| Self::Or(.., pos)
| Self::Dot(.., pos)
| Self::Index(.., pos)
| Self::Variable(.., pos, _)
| Self::FnCall(.., pos)
| Self::MethodCall(.., pos)
2022-02-08 02:02:15 +01:00
| Self::Custom(.., pos)
| Self::InterpolatedString(.., pos)
| Self::Property(.., pos) => *pos = new_pos,
2021-12-17 09:07:13 +01:00
2022-02-04 05:04:33 +01:00
Self::Stmt(x) => x.set_position(new_pos, Position::NONE),
2021-12-17 09:07:13 +01:00
}
self
}
/// Is the expression pure?
///
/// A pure expression has no side effects.
#[inline]
#[must_use]
pub fn is_pure(&self) -> bool {
match self {
2022-02-08 02:02:15 +01:00
Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_pure),
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::Map(x, ..) => x.0.iter().map(|(.., v)| v).all(Self::is_pure),
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::And(x, ..) | Self::Or(x, ..) => x.lhs.is_pure() && x.rhs.is_pure(),
2021-12-17 09:07:13 +01:00
Self::Stmt(x) => x.iter().all(Stmt::is_pure),
Self::Variable(..) => true,
2021-12-17 09:07:13 +01:00
_ => self.is_constant(),
}
}
/// Is the expression the unit `()` literal?
#[inline(always)]
#[must_use]
pub const fn is_unit(&self) -> bool {
2022-02-08 02:46:14 +01:00
matches!(self, Self::Unit(..))
2021-12-17 09:07:13 +01:00
}
/// Is the expression a constant?
#[inline]
#[must_use]
pub fn is_constant(&self) -> bool {
match self {
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
Self::FloatConstant(..) => true,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::DynamicConstant(..)
| Self::BoolConstant(..)
| Self::IntegerConstant(..)
| Self::CharConstant(..)
| Self::StringConstant(..)
| Self::Unit(..) => true,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant),
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::Map(x, ..) => x.0.iter().map(|(.., expr)| expr).all(Self::is_constant),
2021-12-17 09:07:13 +01:00
_ => false,
}
}
/// Is a particular [token][Token] allowed as a postfix operator to this expression?
#[inline]
#[must_use]
pub const fn is_valid_postfix(&self, token: &Token) -> bool {
match token {
#[cfg(not(feature = "no_object"))]
Token::Period => return true,
_ => (),
}
match self {
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
Self::FloatConstant(..) => false,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::DynamicConstant(..)
| Self::BoolConstant(..)
| Self::CharConstant(..)
| Self::And(..)
| Self::Or(..)
2022-02-08 02:46:14 +01:00
| Self::Unit(..) => false,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::IntegerConstant(..)
| Self::StringConstant(..)
| Self::InterpolatedString(..)
| Self::FnCall(..)
| Self::MethodCall(..)
2022-02-08 02:46:14 +01:00
| Self::Stmt(..)
2022-02-08 02:02:15 +01:00
| Self::Dot(..)
| Self::Index(..)
| Self::Array(..)
| Self::Map(..)
| Self::Custom(..) => match token {
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
_ => false,
},
2022-02-08 02:02:15 +01:00
Self::Variable(..) => match token {
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
Token::Bang => true,
Token::DoubleColon => true,
_ => false,
},
2022-02-08 02:02:15 +01:00
Self::Property(..) => match token {
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
_ => false,
},
}
}
/// Recursively walk this expression.
/// Return `false` from the callback to terminate the walk.
pub fn walk<'a>(
&'a self,
path: &mut Vec<ASTNode<'a>>,
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
) -> bool {
// Push the current node onto the path
path.push(self.into());
if !on_node(path) {
return false;
}
match self {
Self::Stmt(x) => {
for s in x.iter() {
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::InterpolatedString(x, ..) | Self::Array(x, ..) => {
2021-12-17 09:07:13 +01:00
for e in x.as_ref() {
if !e.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::Map(x, ..) => {
for (.., e) in &x.0 {
2021-12-17 09:07:13 +01:00
if !e.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::Index(x, ..) | Self::Dot(x, ..) | Expr::And(x, ..) | Expr::Or(x, ..) => {
2021-12-17 09:07:13 +01:00
if !x.lhs.walk(path, on_node) {
return false;
}
if !x.rhs.walk(path, on_node) {
return false;
}
}
2022-02-08 02:02:15 +01:00
Self::FnCall(x, ..) => {
2021-12-17 09:07:13 +01:00
for e in &x.args {
if !e.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::Custom(x, ..) => {
2021-12-17 09:07:13 +01:00
for e in &x.inputs {
if !e.walk(path, on_node) {
return false;
}
}
}
_ => (),
}
2022-01-06 04:07:52 +01:00
path.pop().unwrap();
2021-12-17 09:07:13 +01:00
true
}
}