rhai/src/ast/stmt.rs

787 lines
24 KiB
Rust
Raw Normal View History

2021-12-17 09:07:13 +01:00
//! Module defining script statements.
use super::{ASTNode, BinaryExpr, Expr, FnCallExpr, Ident, OptionFlags, AST_OPTION_FLAGS::*};
2021-12-30 05:14:54 +01:00
use crate::engine::KEYWORD_EVAL;
2022-02-08 16:01:47 +01:00
use crate::tokenizer::{Span, Token};
2021-12-17 09:07:13 +01:00
use crate::{calc_fn_hash, Position, StaticVec, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
collections::BTreeMap,
fmt,
hash::Hash,
mem,
ops::{Deref, DerefMut},
};
/// _(internals)_ An op-assignment operator.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct OpAssignment<'a> {
/// Hash of the op-assignment call.
pub hash_op_assign: u64,
/// Hash of the underlying operator call (for fallback).
pub hash_op: u64,
/// Op-assignment operator.
2022-01-28 01:28:31 +01:00
pub op_assign: &'a str,
/// Underlying operator.
2021-12-17 09:07:13 +01:00
pub op: &'a str,
}
impl OpAssignment<'_> {
/// Create a new [`OpAssignment`].
///
/// # Panics
///
2021-12-17 09:55:24 +01:00
/// Panics if the name is not an op-assignment operator.
2021-12-17 09:07:13 +01:00
#[must_use]
2021-12-17 09:55:24 +01:00
#[inline(always)]
pub fn new(name: &str) -> Self {
Self::new_from_token(Token::lookup_from_syntax(name).expect("operator"))
}
/// Create a new [`OpAssignment`] from a [`Token`].
///
/// # Panics
///
/// Panics if the token is not an op-assignment operator.
#[must_use]
pub fn new_from_token(op: Token) -> Self {
2021-12-17 09:07:13 +01:00
let op_raw = op
.get_base_op_from_assignment()
2021-12-17 09:55:24 +01:00
.expect("op-assignment operator")
2021-12-17 09:07:13 +01:00
.literal_syntax();
Self {
2021-12-17 09:55:24 +01:00
hash_op_assign: calc_fn_hash(op.literal_syntax(), 2),
2021-12-17 09:07:13 +01:00
hash_op: calc_fn_hash(op_raw, 2),
2022-01-28 01:28:31 +01:00
op_assign: op.literal_syntax(),
op: op_raw,
2021-12-17 09:07:13 +01:00
}
}
/// Create a new [`OpAssignment`] from a base operator.
///
/// # Panics
///
/// Panics if the name is not an operator that can be converted into an op-operator.
#[must_use]
#[inline(always)]
pub fn new_from_base(name: &str) -> Self {
Self::new_from_base_token(Token::lookup_from_syntax(name).expect("operator"))
}
/// Convert a [`Token`] into a new [`OpAssignment`].
///
/// # Panics
///
/// Panics if the token is cannot be converted into an op-assignment operator.
#[inline(always)]
#[must_use]
pub fn new_from_base_token(op: Token) -> Self {
Self::new_from_token(op.convert_to_op_assignment().expect("operator"))
}
2021-12-17 09:07:13 +01:00
}
/// A statements block with an optional condition.
#[derive(Debug, Clone, Hash)]
pub struct ConditionalStmtBlock {
/// Optional condition.
pub condition: Option<Expr>,
/// Statements block.
pub statements: StmtBlock,
}
impl<B: Into<StmtBlock>> From<(Option<Expr>, B)> for ConditionalStmtBlock {
#[inline(always)]
fn from(value: (Option<Expr>, B)) -> Self {
Self {
condition: value.0,
statements: value.1.into(),
}
}
}
impl ConditionalStmtBlock {
/// Does the condition exist?
#[inline(always)]
#[must_use]
pub const fn has_condition(&self) -> bool {
self.condition.is_some()
}
}
/// _(internals)_ A type containing all cases for a `switch` statement.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub struct SwitchCases {
/// Dictionary mapping value hashes to [`ConditionalStmtBlock`]'s.
pub cases: BTreeMap<u64, Box<ConditionalStmtBlock>>,
/// Statements block for the default case (there can be no condition for the default case).
pub def_case: StmtBlock,
/// List of range cases.
pub ranges: StaticVec<(INT, INT, bool, ConditionalStmtBlock)>,
}
/// _(internals)_ A `try-catch` block.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub struct TryCatchBlock {
/// `try` block.
pub try_block: StmtBlock,
/// `catch` variable, if any.
pub catch_var: Option<Ident>,
/// `catch` block.
pub catch_block: StmtBlock,
}
2021-12-17 09:07:13 +01:00
/// _(internals)_ A scoped block of statements.
/// Exported under the `internals` feature only.
#[derive(Clone, Hash, Default)]
2022-02-08 16:01:47 +01:00
pub struct StmtBlock(StaticVec<Stmt>, Span);
2021-12-17 09:07:13 +01:00
impl StmtBlock {
/// A [`StmtBlock`] that does not exist.
pub const NONE: Self = Self::empty(Position::NONE);
/// Create a new [`StmtBlock`].
#[must_use]
2022-02-04 05:04:33 +01:00
pub fn new(
statements: impl IntoIterator<Item = Stmt>,
start_pos: Position,
end_pos: Position,
) -> Self {
2021-12-17 09:07:13 +01:00
let mut statements: StaticVec<_> = statements.into_iter().collect();
statements.shrink_to_fit();
2022-02-08 16:01:47 +01:00
Self(statements, Span::new(start_pos, end_pos))
2021-12-17 09:07:13 +01:00
}
/// Create an empty [`StmtBlock`].
#[inline(always)]
#[must_use]
pub const fn empty(pos: Position) -> Self {
2022-02-08 16:01:47 +01:00
Self(StaticVec::new_const(), Span::new(pos, pos))
2021-12-17 09:07:13 +01:00
}
/// Is this statements block empty?
#[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Number of statements in this statements block.
#[inline(always)]
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
/// Get the statements of this statements block.
#[inline(always)]
#[must_use]
pub fn statements(&self) -> &[Stmt] {
&self.0
}
/// Extract the statements.
#[inline(always)]
#[must_use]
pub(crate) fn take_statements(&mut self) -> StaticVec<Stmt> {
mem::take(&mut self.0)
}
/// Get an iterator over the statements of this statements block.
#[inline(always)]
#[must_use]
pub fn iter(&self) -> impl Iterator<Item = &Stmt> {
self.0.iter()
}
2022-02-04 05:04:33 +01:00
/// Get the start position (location of the beginning `{`) of this statements block.
2021-12-17 09:07:13 +01:00
#[inline(always)]
#[must_use]
pub const fn position(&self) -> Position {
2022-02-08 16:01:47 +01:00
(self.1).start()
2022-02-04 05:04:33 +01:00
}
/// Get the end position (location of the ending `}`) of this statements block.
#[inline(always)]
#[must_use]
pub const fn end_position(&self) -> Position {
2022-02-08 16:01:47 +01:00
(self.1).end()
2022-02-04 05:04:33 +01:00
}
/// Get the positions (locations of the beginning `{` and ending `}`) of this statements block.
#[inline(always)]
#[must_use]
2022-02-08 16:01:47 +01:00
pub const fn span(&self) -> Span {
2021-12-17 09:07:13 +01:00
self.1
}
2022-02-04 05:04:33 +01:00
/// Get the positions (locations of the beginning `{` and ending `}`) of this statements block
/// or a default.
2021-12-17 09:07:13 +01:00
#[inline(always)]
2022-02-04 05:04:33 +01:00
#[must_use]
2022-02-08 16:01:47 +01:00
pub const fn span_or_else(&self, def_start_pos: Position, def_end_pos: Position) -> Span {
Span::new(
(self.1).start().or_else(def_start_pos),
(self.1).end().or_else(def_end_pos),
2022-02-04 05:04:33 +01:00
)
}
/// Set the positions of this statements block.
#[inline(always)]
pub fn set_position(&mut self, start_pos: Position, end_pos: Position) {
2022-02-08 16:01:47 +01:00
self.1 = Span::new(start_pos, end_pos);
2021-12-17 09:07:13 +01:00
}
}
impl Deref for StmtBlock {
type Target = StaticVec<Stmt>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for StmtBlock {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl AsRef<[Stmt]> for StmtBlock {
#[inline(always)]
fn as_ref(&self) -> &[Stmt] {
&self.0
}
}
impl AsMut<[Stmt]> for StmtBlock {
#[inline(always)]
fn as_mut(&mut self) -> &mut [Stmt] {
&mut self.0
}
}
2021-12-17 09:07:13 +01:00
impl fmt::Debug for StmtBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Block")?;
fmt::Debug::fmt(&self.0, f)?;
2022-02-08 16:01:47 +01:00
if !self.1.is_none() {
write!(f, " @ {:?}", self.1)?;
2022-02-04 05:04:33 +01:00
}
Ok(())
2021-12-17 09:07:13 +01:00
}
}
impl From<Stmt> for StmtBlock {
#[inline]
fn from(stmt: Stmt) -> Self {
match stmt {
2022-02-08 16:01:47 +01:00
Stmt::Block(mut block, span) => Self(block.iter_mut().map(mem::take).collect(), span),
Stmt::Noop(pos) => Self(StaticVec::new_const(), Span::new(pos, pos)),
_ => {
let pos = stmt.position();
2022-02-08 16:01:47 +01:00
Self(vec![stmt].into(), Span::new(pos, Position::NONE))
}
}
2021-12-17 09:07:13 +01:00
}
}
impl IntoIterator for StmtBlock {
type Item = Stmt;
type IntoIter = smallvec::IntoIter<[Stmt; 3]>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Extend<Stmt> for StmtBlock {
#[inline(always)]
fn extend<T: IntoIterator<Item = Stmt>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
/// _(internals)_ A statement.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub enum Stmt {
/// No-op.
Noop(Position),
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
/// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}`
///
/// ### Data Structure
///
/// 0) Hash table for (condition, block)
/// 1) Default block
/// 2) List of ranges: (start, end, inclusive, condition, statement)
Switch(Expr, Box<SwitchCases>, Position),
2021-12-17 09:07:13 +01:00
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
///
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement.
While(Expr, Box<StmtBlock>, Position),
/// `do` `{` stmt `}` `while`|`until` expr
///
/// ### Option Flags
///
2022-01-28 01:28:17 +01:00
/// * [`AST_OPTION_NONE`] = `while`
/// * [`AST_OPTION_NEGATED`] = `until`
2021-12-17 09:07:13 +01:00
Do(Box<StmtBlock>, Expr, OptionFlags, Position),
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
For(Expr, Box<(Ident, Option<Ident>, StmtBlock)>, Position),
/// \[`export`\] `let`|`const` id `=` expr
///
/// ### Option Flags
///
2022-01-28 01:28:17 +01:00
/// * [`AST_OPTION_EXPORTED`] = `export`
/// * [`AST_OPTION_CONSTANT`] = `const`
2021-12-17 09:07:13 +01:00
Var(Expr, Box<Ident>, OptionFlags, Position),
/// expr op`=` expr
Assignment(Box<(Option<OpAssignment<'static>>, BinaryExpr)>, Position),
2021-12-17 09:07:13 +01:00
/// func `(` expr `,` ... `)`
///
/// Note - this is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single
/// function call forming one statement.
FnCall(Box<FnCallExpr>, Position),
/// `{` stmt`;` ... `}`
2022-02-08 16:01:47 +01:00
Block(Box<[Stmt]>, Span),
2021-12-17 09:07:13 +01:00
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
TryCatch(Box<TryCatchBlock>, Position),
2021-12-17 09:07:13 +01:00
/// [expression][Expr]
Expr(Expr),
/// `continue`/`break`
///
/// ### Option Flags
///
2022-01-28 01:28:17 +01:00
/// * [`AST_OPTION_NONE`] = `continue`
/// * [`AST_OPTION_BREAK`] = `break`
2021-12-17 09:07:13 +01:00
BreakLoop(OptionFlags, Position),
/// `return`/`throw`
///
/// ### Option Flags
///
2022-01-28 01:28:17 +01:00
/// * [`AST_OPTION_NONE`] = `return`
/// * [`AST_OPTION_BREAK`] = `throw`
2021-12-17 09:07:13 +01:00
Return(OptionFlags, Option<Expr>, Position),
/// `import` expr `as` var
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
Import(Expr, Option<Box<Ident>>, Position),
/// `export` var `as` var
2021-12-17 09:07:13 +01:00
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
Export(Box<(Ident, Ident)>, Position),
2021-12-17 09:07:13 +01:00
/// Convert a variable to shared.
///
/// Not available under `no_closure`.
///
/// # Notes
///
/// This variant does not map to any language structure. It is currently only used only to
/// convert a normal variable into a shared variable when the variable is _captured_ by a closure.
#[cfg(not(feature = "no_closure"))]
Share(crate::Identifier),
}
impl Default for Stmt {
#[inline(always)]
fn default() -> Self {
Self::Noop(Position::NONE)
}
}
impl From<StmtBlock> for Stmt {
#[inline(always)]
fn from(block: StmtBlock) -> Self {
Self::Block(block.0.into_boxed_slice(), block.1)
2021-12-17 09:07:13 +01:00
}
}
impl Stmt {
/// Is this statement [`Noop`][Stmt::Noop]?
#[inline(always)]
#[must_use]
pub const fn is_noop(&self) -> bool {
2022-02-08 02:46:14 +01:00
matches!(self, Self::Noop(..))
2021-12-17 09:07:13 +01:00
}
/// Get the [position][Position] of this statement.
#[must_use]
pub const fn position(&self) -> Position {
match self {
Self::Noop(pos)
2022-02-08 02:02:15 +01:00
| Self::BreakLoop(.., pos)
| Self::Assignment(.., pos)
| Self::FnCall(.., pos)
| Self::If(.., pos)
| Self::Switch(.., pos)
| Self::While(.., pos)
| Self::Do(.., pos)
| Self::For(.., pos)
| Self::Return(.., pos)
| Self::Var(.., pos)
| Self::TryCatch(.., pos) => *pos,
2021-12-17 09:07:13 +01:00
2022-02-08 16:01:47 +01:00
Self::Block(.., span) => span.start(),
2022-02-04 05:04:33 +01:00
Self::Expr(x) => x.start_position(),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(.., pos) => *pos,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Export(.., pos) => *pos,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_closure"))]
2022-02-08 02:46:14 +01:00
Self::Share(..) => Position::NONE,
2021-12-17 09:07:13 +01:00
}
}
/// Override the [position][Position] of this statement.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Self::Noop(pos)
2022-02-08 02:02:15 +01:00
| Self::BreakLoop(.., pos)
| Self::Assignment(.., pos)
| Self::FnCall(.., pos)
| Self::If(.., pos)
| Self::Switch(.., pos)
| Self::While(.., pos)
| Self::Do(.., pos)
| Self::For(.., pos)
| Self::Return(.., pos)
| Self::Var(.., pos)
| Self::TryCatch(.., pos) => *pos = new_pos,
2021-12-17 09:07:13 +01:00
2022-02-08 16:01:47 +01:00
Self::Block(.., span) => *span = Span::new(new_pos, span.end()),
2021-12-17 09:07:13 +01:00
Self::Expr(x) => {
x.set_position(new_pos);
}
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(.., pos) => *pos = new_pos,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Export(.., pos) => *pos = new_pos,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_closure"))]
2022-02-08 02:46:14 +01:00
Self::Share(..) => (),
2021-12-17 09:07:13 +01:00
}
self
}
/// Does this statement return a value?
#[must_use]
pub const fn returns_value(&self) -> bool {
match self {
2022-02-08 02:02:15 +01:00
Self::If(..)
| Self::Switch(..)
| Self::Block(..)
2022-02-08 02:46:14 +01:00
| Self::Expr(..)
2022-02-08 02:02:15 +01:00
| Self::FnCall(..) => true,
2021-12-17 09:07:13 +01:00
2022-02-08 14:28:15 +01:00
Self::Noop(..)
| Self::While(..)
| Self::Do(..)
| Self::For(..)
| Self::TryCatch(..) => false,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::Var(..) | Self::Assignment(..) | Self::BreakLoop(..) | Self::Return(..) => false,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(..) | Self::Export(..) => false,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_closure"))]
2022-02-08 02:46:14 +01:00
Self::Share(..) => false,
2021-12-17 09:07:13 +01:00
}
}
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
#[must_use]
pub const fn is_self_terminated(&self) -> bool {
match self {
2022-02-08 02:02:15 +01:00
Self::If(..)
| Self::Switch(..)
| Self::While(..)
| Self::For(..)
| Self::Block(..)
| Self::TryCatch(..) => true,
2021-12-17 09:07:13 +01:00
// A No-op requires a semicolon in order to know it is an empty statement!
2022-02-08 02:46:14 +01:00
Self::Noop(..) => false,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::Expr(Expr::Custom(x, ..)) if x.is_self_terminated() => true,
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::Var(..)
| Self::Assignment(..)
2022-02-08 02:46:14 +01:00
| Self::Expr(..)
2022-02-08 02:02:15 +01:00
| Self::FnCall(..)
| Self::Do(..)
| Self::BreakLoop(..)
| Self::Return(..) => false,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(..) | Self::Export(..) => false,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_closure"))]
2022-02-08 02:46:14 +01:00
Self::Share(..) => false,
2021-12-17 09:07:13 +01:00
}
}
/// Is this statement _pure_?
///
/// A pure statement has no side effects.
#[must_use]
pub fn is_pure(&self) -> bool {
match self {
2022-02-08 02:46:14 +01:00
Self::Noop(..) => true,
2021-12-17 09:07:13 +01:00
Self::Expr(expr) => expr.is_pure(),
2022-02-08 02:02:15 +01:00
Self::If(condition, x, ..) => {
2021-12-17 09:07:13 +01:00
condition.is_pure()
&& x.0.iter().all(Stmt::is_pure)
&& x.1.iter().all(Stmt::is_pure)
2021-12-17 09:07:13 +01:00
}
2022-02-08 02:02:15 +01:00
Self::Switch(expr, x, ..) => {
2021-12-17 09:07:13 +01:00
expr.is_pure()
&& x.cases.values().all(|block| {
block.condition.as_ref().map(Expr::is_pure).unwrap_or(true)
&& block.statements.iter().all(Stmt::is_pure)
2021-12-17 09:07:13 +01:00
})
2022-02-08 02:02:15 +01:00
&& x.ranges.iter().all(|(.., block)| {
block.condition.as_ref().map(Expr::is_pure).unwrap_or(true)
&& block.statements.iter().all(Stmt::is_pure)
2021-12-17 09:07:13 +01:00
})
&& x.def_case.iter().all(Stmt::is_pure)
2021-12-17 09:07:13 +01:00
}
// Loops that exit can be pure because it can never be infinite.
2022-02-08 02:02:15 +01:00
Self::While(Expr::BoolConstant(false, ..), ..) => true,
Self::Do(body, Expr::BoolConstant(x, ..), options, ..)
2022-01-28 01:28:17 +01:00
if *x == options.contains(AST_OPTION_NEGATED) =>
2021-12-17 09:07:13 +01:00
{
body.iter().all(Stmt::is_pure)
}
// Loops are never pure since they can be infinite - and that's a side effect.
2022-02-08 02:02:15 +01:00
Self::While(..) | Self::Do(..) => false,
2021-12-17 09:07:13 +01:00
// For loops can be pure because if the iterable is pure, it is finite,
// so infinite loops can never occur.
2022-02-08 02:02:15 +01:00
Self::For(iterable, x, ..) => iterable.is_pure() && x.2.iter().all(Stmt::is_pure),
2021-12-17 09:07:13 +01:00
2022-02-08 02:02:15 +01:00
Self::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
Self::Block(block, ..) => block.iter().all(|stmt| stmt.is_pure()),
Self::BreakLoop(..) | Self::Return(..) => false,
Self::TryCatch(x, ..) => {
x.try_block.iter().all(Stmt::is_pure) && x.catch_block.iter().all(Stmt::is_pure)
2021-12-17 09:07:13 +01:00
}
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(..) => false,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Export(..) => false,
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_closure"))]
2022-02-08 02:46:14 +01:00
Self::Share(..) => false,
2021-12-17 09:07:13 +01:00
}
}
2021-12-30 05:14:54 +01:00
/// Does this statement's behavior depend on its containing block?
///
/// A statement that depends on its containing block behaves differently when promoted
/// to an upper block.
///
/// Currently only variable definitions (i.e. `let` and `const`), `import`/`export` statements,
/// and `eval` calls (which may in turn call define variables) fall under this category.
#[inline]
#[must_use]
pub fn is_block_dependent(&self) -> bool {
match self {
2022-02-08 02:02:15 +01:00
Self::Var(..) => true,
2021-12-30 05:14:54 +01:00
2021-12-30 05:19:41 +01:00
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_block_dependent),
2022-02-08 02:02:15 +01:00
Self::FnCall(x, ..) | Self::Expr(Expr::FnCall(x, ..)) => {
2022-01-08 07:00:41 +01:00
!x.is_qualified() && x.name == KEYWORD_EVAL
2021-12-30 05:19:41 +01:00
}
2021-12-30 05:14:54 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(..) | Self::Export(..) => true,
2021-12-30 05:14:54 +01:00
_ => false,
}
}
2021-12-17 09:07:13 +01:00
/// Is this statement _pure_ within the containing block?
///
/// An internally pure statement only has side effects that disappear outside the block.
///
/// Currently only variable definitions (i.e. `let` and `const`) and `import`/`export`
2021-12-30 05:14:54 +01:00
/// statements are internally pure, other than pure expressions.
2021-12-17 09:07:13 +01:00
#[inline]
#[must_use]
pub fn is_internally_pure(&self) -> bool {
match self {
2022-02-08 02:02:15 +01:00
Self::Var(expr, _, ..) => expr.is_pure(),
2021-12-17 09:07:13 +01:00
2021-12-30 05:19:41 +01:00
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_internally_pure),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(expr, ..) => expr.is_pure(),
2021-12-17 09:07:13 +01:00
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Export(..) => true,
2021-12-17 09:07:13 +01:00
_ => self.is_pure(),
}
}
/// Does this statement break the current control flow through the containing block?
///
/// Currently this is only true for `return`, `throw`, `break` and `continue`.
///
/// All statements following this statement will essentially be dead code.
#[inline]
#[must_use]
pub const fn is_control_flow_break(&self) -> bool {
match self {
2022-02-08 02:02:15 +01:00
Self::Return(..) | Self::BreakLoop(..) => true,
2021-12-17 09:07:13 +01:00
_ => false,
}
}
/// Recursively walk this statement.
/// 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 {
2022-02-08 02:02:15 +01:00
Self::Var(e, _, ..) => {
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::If(e, x, ..) => {
2021-12-17 09:07:13 +01:00
if !e.walk(path, on_node) {
return false;
}
for s in x.0.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
for s in x.1.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::Switch(e, x, ..) => {
2021-12-17 09:07:13 +01:00
if !e.walk(path, on_node) {
return false;
}
for b in x.cases.values() {
if !b
.condition
.as_ref()
.map(|e| e.walk(path, on_node))
.unwrap_or(true)
{
2021-12-17 09:07:13 +01:00
return false;
}
for s in b.statements.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
for (.., b) in &x.ranges {
if !b
.condition
.as_ref()
.map(|e| e.walk(path, on_node))
.unwrap_or(true)
{
2021-12-17 09:07:13 +01:00
return false;
}
for s in b.statements.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
}
for s in x.def_case.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::While(e, s, ..) | Self::Do(s, e, ..) => {
2021-12-17 09:07:13 +01:00
if !e.walk(path, on_node) {
return false;
}
for s in &s.0 {
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::For(e, x, ..) => {
2021-12-17 09:07:13 +01:00
if !e.walk(path, on_node) {
return false;
}
for s in x.2.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::Assignment(x, ..) => {
if !x.1.lhs.walk(path, on_node) {
2021-12-17 09:07:13 +01:00
return false;
}
if !x.1.rhs.walk(path, on_node) {
2021-12-17 09:07:13 +01:00
return false;
}
}
2022-02-08 02:02:15 +01:00
Self::FnCall(x, ..) => {
2021-12-17 09:07:13 +01:00
for s in &x.args {
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::Block(x, ..) => {
2021-12-17 09:07:13 +01:00
for s in x.iter() {
if !s.walk(path, on_node) {
return false;
}
}
}
2022-02-08 02:02:15 +01:00
Self::TryCatch(x, ..) => {
for s in x.try_block.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
for s in x.catch_block.iter() {
2021-12-17 09:07:13 +01:00
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Expr(e) | Self::Return(_, Some(e), _) => {
if !e.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_module"))]
2022-02-08 02:02:15 +01:00
Self::Import(e, ..) => {
2021-12-17 09:07:13 +01:00
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
}
}