Use bitflags.
This commit is contained in:
parent
f47b911681
commit
8205547d8a
@ -20,6 +20,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
||||
smallvec = { version = "1.7", default-features = false, features = ["union", "const_new" ] }
|
||||
ahash = { version = "0.7", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
bitflags = { version = "1", default-features = false }
|
||||
smartstring = { version = "1", default-features = false }
|
||||
rhai_codegen = { version = "1.2", path = "codegen", default-features = false }
|
||||
|
||||
|
@ -39,7 +39,7 @@ Standard features
|
||||
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) (including packed [byte arrays](https://rhai.rs/book/language/blobs.html)) and [object maps](https://rhai.rs/book/language/object-maps.html).
|
||||
* Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust.
|
||||
* Relatively little `unsafe` code (yes there are some for performance reasons).
|
||||
* Few dependencies - currently only [`smallvec`](https://crates.io/crates/smallvec), [`num-traits`](https://crates.io/crates/num-traits), [`ahash`](https://crates.io/crates/ahash) and [`smartstring`](https://crates.io/crates/smartstring).
|
||||
* Few dependencies - currently only [`smallvec`](https://crates.io/crates/smallvec), [`num-traits`](https://crates.io/crates/num-traits), [`ahash`](https://crates.io/crates/ahash), [`bitflags`](https://crates.io/crates/bitflags) and [`smartstring`](https://crates.io/crates/smartstring).
|
||||
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
|
||||
* Compile once to [AST](https://rhai.rs/book/engine/compile.html) form for repeated evaluations.
|
||||
* Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts).
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Module defining the AST (abstract syntax tree).
|
||||
|
||||
use super::{Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer, AST_OPTION_FLAGS::*};
|
||||
use super::{ASTFlags, Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer};
|
||||
use crate::{Dynamic, FnNamespace, Identifier, Position};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -745,12 +745,12 @@ impl AST {
|
||||
) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
||||
self.statements().iter().filter_map(move |stmt| match stmt {
|
||||
Stmt::Var(x, options, ..)
|
||||
if options.contains(AST_OPTION_CONSTANT) && include_constants
|
||||
|| !options.contains(AST_OPTION_CONSTANT) && include_variables =>
|
||||
if options.contains(ASTFlags::CONSTANT) && include_constants
|
||||
|| !options.contains(ASTFlags::CONSTANT) && include_variables =>
|
||||
{
|
||||
let (name, expr, ..) = x.as_ref();
|
||||
if let Some(value) = expr.get_literal_value() {
|
||||
Some((name.as_str(), options.contains(AST_OPTION_CONSTANT), value))
|
||||
Some((name.as_str(), options.contains(ASTFlags::CONSTANT), value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Module defining script expressions.
|
||||
|
||||
use super::{ASTNode, Ident, Stmt, StmtBlock};
|
||||
use super::{ASTFlags, ASTNode, Ident, Stmt, StmtBlock};
|
||||
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||
use crate::func::hashing::ALT_ZERO_HASH;
|
||||
use crate::tokenizer::Token;
|
||||
@ -419,10 +419,15 @@ pub enum Expr {
|
||||
Stmt(Box<StmtBlock>),
|
||||
/// func `(` expr `,` ... `)`
|
||||
FnCall(Box<FnCallExpr>, Position),
|
||||
/// lhs `.` rhs - boolean variable is a dummy
|
||||
Dot(Box<BinaryExpr>, bool, Position),
|
||||
/// lhs `[` rhs `]` - boolean indicates whether the dotting/indexing chain stops
|
||||
Index(Box<BinaryExpr>, bool, Position),
|
||||
/// 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),
|
||||
/// lhs `&&` rhs
|
||||
And(Box<BinaryExpr>, Position),
|
||||
/// lhs `||` rhs
|
||||
|
172
src/ast/flags.rs
172
src/ast/flags.rs
@ -1,6 +1,6 @@
|
||||
//! Module defining script options.
|
||||
|
||||
use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Sub, SubAssign};
|
||||
use bitflags::bitflags;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@ -13,163 +13,19 @@ pub enum FnAccess {
|
||||
Public,
|
||||
}
|
||||
|
||||
/// _(internals)_ A type that holds a configuration option with bit-flags.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Functionality-wise, this type is a naive and simplistic implementation of
|
||||
/// [`bit_flags`](https://crates.io/crates/bitflags). It is re-implemented to avoid pulling in yet
|
||||
/// one more dependency.
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Hash, Default)]
|
||||
pub struct OptionFlags(u8);
|
||||
|
||||
impl OptionFlags {
|
||||
/// Does this [`OptionFlags`] contain a particular option flag?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn contains(self, flag: Self) -> bool {
|
||||
self.0 & flag.0 != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for OptionFlags {
|
||||
type Output = Self;
|
||||
|
||||
/// Return the negation of the [`OptionFlags`].
|
||||
#[inline(always)]
|
||||
fn not(self) -> Self::Output {
|
||||
Self(!self.0) & AST_OPTION_FLAGS::AST_OPTION_ALL
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for OptionFlags {
|
||||
type Output = Self;
|
||||
|
||||
/// Return the union of two [`OptionFlags`].
|
||||
#[inline(always)]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for OptionFlags {
|
||||
/// Add the option flags in one [`OptionFlags`] to another.
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.0 |= rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for OptionFlags {
|
||||
type Output = Self;
|
||||
|
||||
/// Return the union of two [`OptionFlags`].
|
||||
#[inline(always)]
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOrAssign for OptionFlags {
|
||||
/// Add the option flags in one [`OptionFlags`] to another.
|
||||
#[inline(always)]
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
self.0 |= rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for OptionFlags {
|
||||
type Output = Self;
|
||||
|
||||
/// Return the difference of two [`OptionFlags`].
|
||||
#[inline(always)]
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 & !rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for OptionFlags {
|
||||
/// Remove the option flags in one [`OptionFlags`] from another.
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
self.0 &= !rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for OptionFlags {
|
||||
type Output = Self;
|
||||
|
||||
/// Return the intersection of two [`OptionFlags`].
|
||||
#[inline(always)]
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 & !rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAndAssign for OptionFlags {
|
||||
/// Keep only the intersection of one [`OptionFlags`] with another.
|
||||
#[inline(always)]
|
||||
fn bitand_assign(&mut self, rhs: Self) {
|
||||
self.0 &= !rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ Option bit-flags for [`AST`][super::AST] nodes.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[allow(non_snake_case)]
|
||||
pub mod AST_OPTION_FLAGS {
|
||||
use super::OptionFlags;
|
||||
|
||||
/// _(internals)_ No options for the [`AST`][crate::AST] node.
|
||||
bitflags! {
|
||||
/// _(internals)_ A type that holds a configuration option with bit-flags.
|
||||
/// Exported under the `internals` feature only.
|
||||
pub const AST_OPTION_NONE: OptionFlags = OptionFlags(0b0000_0000);
|
||||
/// _(internals)_ The [`AST`][crate::AST] node is constant.
|
||||
/// Exported under the `internals` feature only.
|
||||
pub const AST_OPTION_CONSTANT: OptionFlags = OptionFlags(0b0000_0001);
|
||||
/// _(internals)_ The [`AST`][crate::AST] node is exported to the outside (i.e. public).
|
||||
/// Exported under the `internals` feature only.
|
||||
pub const AST_OPTION_EXPORTED: OptionFlags = OptionFlags(0b0000_0010);
|
||||
/// _(internals)_ The [`AST`][crate::AST] node is in negated mode
|
||||
/// (meaning whatever information is the opposite).
|
||||
/// Exported under the `internals` feature only.
|
||||
pub const AST_OPTION_NEGATED: OptionFlags = OptionFlags(0b0000_0100);
|
||||
/// _(internals)_ The [`AST`][crate::AST] node breaks out of normal control flow.
|
||||
/// Exported under the `internals` feature only.
|
||||
pub const AST_OPTION_BREAK: OptionFlags = OptionFlags(0b0000_1000);
|
||||
/// _(internals)_ Mask of all options.
|
||||
/// Exported under the `internals` feature only.
|
||||
pub(crate) const AST_OPTION_ALL: OptionFlags = OptionFlags(
|
||||
AST_OPTION_CONSTANT.0 | AST_OPTION_EXPORTED.0 | AST_OPTION_NEGATED.0 | AST_OPTION_BREAK.0,
|
||||
);
|
||||
|
||||
impl std::fmt::Debug for OptionFlags {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn write_option(
|
||||
options: &OptionFlags,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
num_flags: &mut usize,
|
||||
flag: OptionFlags,
|
||||
name: &str,
|
||||
) -> std::fmt::Result {
|
||||
if options.contains(flag) {
|
||||
if *num_flags > 0 {
|
||||
f.write_str("+")?;
|
||||
}
|
||||
f.write_str(name)?;
|
||||
*num_flags += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let num_flags = &mut 0;
|
||||
|
||||
f.write_str("(")?;
|
||||
write_option(self, f, num_flags, AST_OPTION_CONSTANT, "Constant")?;
|
||||
write_option(self, f, num_flags, AST_OPTION_EXPORTED, "Exported")?;
|
||||
write_option(self, f, num_flags, AST_OPTION_NEGATED, "Negated")?;
|
||||
write_option(self, f, num_flags, AST_OPTION_BREAK, "Break")?;
|
||||
f.write_str(")")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub struct ASTFlags: u8 {
|
||||
/// No options for the [`AST`][crate::AST] node.
|
||||
const NONE = 0b0000_0000;
|
||||
/// The [`AST`][crate::AST] node is read-only.
|
||||
const CONSTANT = 0b0000_0001;
|
||||
/// The [`AST`][crate::AST] node is exposed to the outside (i.e. public).
|
||||
const EXPORTED = 0b0000_0010;
|
||||
/// The [`AST`][crate::AST] node is negated (i.e. whatever information is the opposite).
|
||||
const NEGATED = 0b0000_0100;
|
||||
/// The [`AST`][crate::AST] node breaks out of normal control flow.
|
||||
const BREAK = 0b0000_1000;
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ pub mod stmt;
|
||||
|
||||
pub use ast::{ASTNode, AST};
|
||||
pub use expr::{BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes};
|
||||
pub use flags::{FnAccess, OptionFlags, AST_OPTION_FLAGS};
|
||||
pub use flags::{ASTFlags, FnAccess};
|
||||
pub use ident::Ident;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Module defining script statements.
|
||||
|
||||
use super::{ASTNode, BinaryExpr, Expr, FnCallExpr, Ident, OptionFlags, AST_OPTION_FLAGS::*};
|
||||
use super::{ASTFlags, ASTNode, BinaryExpr, Expr, FnCallExpr, Ident};
|
||||
use crate::engine::KEYWORD_EVAL;
|
||||
use crate::tokenizer::{Span, Token};
|
||||
use crate::{calc_fn_hash, Position, StaticVec, INT};
|
||||
@ -340,24 +340,20 @@ pub enum Stmt {
|
||||
While(Box<(Expr, StmtBlock)>, Position),
|
||||
/// `do` `{` stmt `}` `while`|`until` expr
|
||||
///
|
||||
/// ### Option Flags
|
||||
/// ### Flags
|
||||
///
|
||||
/// * [`AST_OPTION_NONE`] = `while`
|
||||
/// * [`AST_OPTION_NEGATED`] = `until`
|
||||
Do(Box<(Expr, StmtBlock)>, OptionFlags, Position),
|
||||
/// * [`NONE`][ASTFlags::NONE] = `while`
|
||||
/// * [`NEGATED`][ASTFlags::NEGATED] = `until`
|
||||
Do(Box<(Expr, StmtBlock)>, ASTFlags, Position),
|
||||
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
|
||||
For(Box<(Ident, Option<Ident>, Expr, StmtBlock)>, Position),
|
||||
/// \[`export`\] `let`|`const` id `=` expr
|
||||
///
|
||||
/// ### Option Flags
|
||||
/// ### Flags
|
||||
///
|
||||
/// * [`AST_OPTION_EXPORTED`] = `export`
|
||||
/// * [`AST_OPTION_CONSTANT`] = `const`
|
||||
Var(
|
||||
Box<(Ident, Expr, Option<NonZeroUsize>)>,
|
||||
OptionFlags,
|
||||
Position,
|
||||
),
|
||||
/// * [`EXPORTED`][ASTFlags::EXPORTED] = `export`
|
||||
/// * [`CONSTANT`][ASTFlags::CONSTANT] = `const`
|
||||
Var(Box<(Ident, Expr, Option<NonZeroUsize>)>, ASTFlags, Position),
|
||||
/// expr op`=` expr
|
||||
Assignment(Box<(Option<OpAssignment<'static>>, BinaryExpr)>, Position),
|
||||
/// func `(` expr `,` ... `)`
|
||||
@ -373,18 +369,18 @@ pub enum Stmt {
|
||||
Expr(Box<Expr>),
|
||||
/// `continue`/`break`
|
||||
///
|
||||
/// ### Option Flags
|
||||
/// ### Flags
|
||||
///
|
||||
/// * [`AST_OPTION_NONE`] = `continue`
|
||||
/// * [`AST_OPTION_BREAK`] = `break`
|
||||
BreakLoop(OptionFlags, Position),
|
||||
/// * [`NONE`][ASTFlags::NONE] = `continue`
|
||||
/// * [`BREAK`][ASTFlags::BREAK] = `break`
|
||||
BreakLoop(ASTFlags, Position),
|
||||
/// `return`/`throw`
|
||||
///
|
||||
/// ### Option Flags
|
||||
/// ### Flags
|
||||
///
|
||||
/// * [`AST_OPTION_NONE`] = `return`
|
||||
/// * [`AST_OPTION_BREAK`] = `throw`
|
||||
Return(Option<Box<Expr>>, OptionFlags, Position),
|
||||
/// * [`NONE`][ASTFlags::NONE] = `return`
|
||||
/// * [`BREAK`][ASTFlags::BREAK] = `throw`
|
||||
Return(Option<Box<Expr>>, ASTFlags, Position),
|
||||
/// `import` expr `as` alias
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
@ -590,7 +586,7 @@ impl Stmt {
|
||||
// Loops that exit can be pure because it can never be infinite.
|
||||
Self::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => true,
|
||||
Self::Do(x, options, ..) if matches!(x.0, Expr::BoolConstant(..)) => match x.0 {
|
||||
Expr::BoolConstant(cond, ..) if cond == options.contains(AST_OPTION_NEGATED) => {
|
||||
Expr::BoolConstant(cond, ..) if cond == options.contains(ASTFlags::NEGATED) => {
|
||||
x.1.iter().all(Stmt::is_pure)
|
||||
}
|
||||
_ => false,
|
||||
|
@ -2,7 +2,7 @@
|
||||
#![cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
|
||||
use super::{EvalState, GlobalRuntimeState, Target};
|
||||
use crate::ast::{Expr, OpAssignment};
|
||||
use crate::ast::{ASTFlags, Expr, OpAssignment};
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR};
|
||||
use std::hash::Hash;
|
||||
@ -127,15 +127,15 @@ impl Engine {
|
||||
root: (&str, Position),
|
||||
parent: &Expr,
|
||||
rhs: &Expr,
|
||||
terminate_chaining: bool,
|
||||
parent_options: ASTFlags,
|
||||
idx_values: &mut StaticVec<super::ChainArgument>,
|
||||
chain_type: ChainType,
|
||||
level: usize,
|
||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
let _parent = parent;
|
||||
let _parent_options = parent_options;
|
||||
let is_ref_mut = target.is_ref();
|
||||
let _terminate_chaining = terminate_chaining;
|
||||
|
||||
// Pop the last index value
|
||||
let idx_val = idx_values.pop().unwrap();
|
||||
@ -151,8 +151,8 @@ impl Engine {
|
||||
|
||||
match rhs {
|
||||
// xxx[idx].expr... | xxx[idx][expr]...
|
||||
Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos)
|
||||
if !_terminate_chaining =>
|
||||
Expr::Dot(x, options, x_pos) | Expr::Index(x, options, x_pos)
|
||||
if !_parent_options.contains(ASTFlags::BREAK) =>
|
||||
{
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?;
|
||||
@ -169,7 +169,7 @@ impl Engine {
|
||||
let obj_ptr = &mut obj;
|
||||
|
||||
match self.eval_dot_index_chain_helper(
|
||||
global, state, lib, this_ptr, obj_ptr, root, rhs, &x.rhs, *term,
|
||||
global, state, lib, this_ptr, obj_ptr, root, rhs, &x.rhs, *options,
|
||||
idx_values, rhs_chain, level, new_val,
|
||||
) {
|
||||
Ok((result, true)) if is_obj_temp_val => {
|
||||
@ -410,7 +410,7 @@ impl Engine {
|
||||
)
|
||||
}
|
||||
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
||||
Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos)
|
||||
Expr::Index(x, options, x_pos) | Expr::Dot(x, options, x_pos)
|
||||
if target.is::<crate::Map>() =>
|
||||
{
|
||||
let _node = &x.lhs;
|
||||
@ -457,13 +457,13 @@ impl Engine {
|
||||
let rhs_chain = rhs.into();
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, state, lib, this_ptr, val_target, root, rhs, &x.rhs, *term,
|
||||
global, state, lib, this_ptr, val_target, root, rhs, &x.rhs, *options,
|
||||
idx_values, rhs_chain, level, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*x_pos))
|
||||
}
|
||||
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr
|
||||
Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) => {
|
||||
Expr::Index(x, options, x_pos) | Expr::Dot(x, options, x_pos) => {
|
||||
let _node = &x.lhs;
|
||||
|
||||
match x.lhs {
|
||||
@ -509,7 +509,7 @@ impl Engine {
|
||||
let (result, may_be_changed) = self
|
||||
.eval_dot_index_chain_helper(
|
||||
global, state, lib, this_ptr, val, root, rhs, &x.rhs,
|
||||
*term, idx_values, rhs_chain, level, new_val,
|
||||
*options, idx_values, rhs_chain, level, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*x_pos))?;
|
||||
|
||||
@ -570,7 +570,7 @@ impl Engine {
|
||||
let val = &mut val.into();
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, state, lib, this_ptr, val, root, rhs, &x.rhs, *term,
|
||||
global, state, lib, this_ptr, val, root, rhs, &x.rhs, *options,
|
||||
idx_values, rhs_chain, level, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
@ -602,18 +602,18 @@ impl Engine {
|
||||
level: usize,
|
||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||
) -> RhaiResult {
|
||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, term, op_pos) = match expr {
|
||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, options, op_pos) = match expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos),
|
||||
Expr::Index(x, options, pos) => (x.as_ref(), ChainType::Indexing, *options, *pos),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos),
|
||||
Expr::Dot(x, options, pos) => (x.as_ref(), ChainType::Dotting, *options, *pos),
|
||||
expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr),
|
||||
};
|
||||
|
||||
let idx_values = &mut StaticVec::new_const();
|
||||
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, global, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level,
|
||||
scope, global, state, lib, this_ptr, rhs, options, chain_type, idx_values, 0, level,
|
||||
)?;
|
||||
|
||||
let is_assignment = new_val.is_some();
|
||||
@ -634,7 +634,7 @@ impl Engine {
|
||||
let root = (x.2.as_str(), *var_pos);
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, state, lib, &mut None, obj_ptr, root, expr, rhs, term, idx_values,
|
||||
global, state, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values,
|
||||
chain_type, level, new_val,
|
||||
)
|
||||
.map(|(v, ..)| v)
|
||||
@ -648,7 +648,7 @@ impl Engine {
|
||||
let obj_ptr = &mut value.into();
|
||||
let root = ("", expr.start_position());
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, state, lib, this_ptr, obj_ptr, root, expr, rhs, term, idx_values,
|
||||
global, state, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values,
|
||||
chain_type, level, new_val,
|
||||
)
|
||||
.map(|(v, ..)| if is_assignment { Dynamic::UNIT } else { v })
|
||||
@ -668,7 +668,7 @@ impl Engine {
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
expr: &Expr,
|
||||
terminate_chaining: bool,
|
||||
parent_options: ASTFlags,
|
||||
parent_chain_type: ChainType,
|
||||
idx_values: &mut StaticVec<super::ChainArgument>,
|
||||
size: usize,
|
||||
@ -715,7 +715,9 @@ impl Engine {
|
||||
}
|
||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
Expr::Index(x, term, ..) | Expr::Dot(x, term, ..) if !terminate_chaining => {
|
||||
Expr::Index(x, options, ..) | Expr::Dot(x, options, ..)
|
||||
if !parent_options.contains(ASTFlags::BREAK) =>
|
||||
{
|
||||
let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref();
|
||||
|
||||
// Evaluate in left-to-right order
|
||||
@ -773,8 +775,8 @@ impl Engine {
|
||||
let chain_type = expr.into();
|
||||
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, global, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size,
|
||||
level,
|
||||
scope, global, state, lib, this_ptr, rhs, *options, chain_type, idx_values,
|
||||
size, level,
|
||||
)?;
|
||||
|
||||
idx_values.push(lhs_arg_val);
|
||||
|
@ -3,7 +3,7 @@
|
||||
use super::{EvalContext, EvalState, GlobalRuntimeState, Target};
|
||||
use crate::api::events::VarDefInfo;
|
||||
use crate::ast::{
|
||||
BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCases, TryCatchBlock, AST_OPTION_FLAGS::*,
|
||||
ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCases, TryCatchBlock,
|
||||
};
|
||||
use crate::func::get_hasher;
|
||||
use crate::types::dynamic::{AccessMode, Union};
|
||||
@ -537,7 +537,7 @@ impl Engine {
|
||||
// Do loop
|
||||
Stmt::Do(x, options, ..) => loop {
|
||||
let (expr, body) = x.as_ref();
|
||||
let is_while = !options.contains(AST_OPTION_NEGATED);
|
||||
let is_while = !options.contains(ASTFlags::NEGATED);
|
||||
|
||||
if !body.is_empty() {
|
||||
match self
|
||||
@ -700,7 +700,7 @@ impl Engine {
|
||||
|
||||
// Continue/Break statement
|
||||
Stmt::BreakLoop(options, pos) => {
|
||||
Err(ERR::LoopBreak(options.contains(AST_OPTION_BREAK), *pos).into())
|
||||
Err(ERR::LoopBreak(options.contains(ASTFlags::BREAK), *pos).into())
|
||||
}
|
||||
|
||||
// Try/Catch statement
|
||||
@ -790,12 +790,12 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Throw value
|
||||
Stmt::Return(Some(expr), options, pos) if options.contains(AST_OPTION_BREAK) => self
|
||||
Stmt::Return(Some(expr), options, pos) if options.contains(ASTFlags::BREAK) => self
|
||||
.eval_expr(scope, global, state, lib, this_ptr, expr, level)
|
||||
.and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())),
|
||||
|
||||
// Empty throw
|
||||
Stmt::Return(None, options, pos) if options.contains(AST_OPTION_BREAK) => {
|
||||
Stmt::Return(None, options, pos) if options.contains(ASTFlags::BREAK) => {
|
||||
Err(ERR::ErrorRuntime(Dynamic::UNIT, *pos).into())
|
||||
}
|
||||
|
||||
@ -815,12 +815,12 @@ impl Engine {
|
||||
Stmt::Var(x, options, pos) => {
|
||||
let (Ident { name: var_name, .. }, expr, index) = x.as_ref();
|
||||
|
||||
let access = if options.contains(AST_OPTION_CONSTANT) {
|
||||
let access = if options.contains(ASTFlags::CONSTANT) {
|
||||
AccessMode::ReadOnly
|
||||
} else {
|
||||
AccessMode::ReadWrite
|
||||
};
|
||||
let export = options.contains(AST_OPTION_EXPORTED);
|
||||
let export = options.contains(ASTFlags::EXPORTED);
|
||||
|
||||
// Check variable definition filter
|
||||
let result = if let Some(ref filter) = self.def_var_filter {
|
||||
|
@ -260,9 +260,8 @@ pub use parser::ParseState;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use ast::{
|
||||
ASTNode, BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
||||
OpAssignment, OptionFlags, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
|
||||
AST_OPTION_FLAGS,
|
||||
ASTFlags, ASTNode, BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr,
|
||||
FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
|
||||
};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
|
@ -114,7 +114,7 @@ impl Namespace {
|
||||
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
|
||||
self.index = index
|
||||
}
|
||||
/// Get the [position][Position] of this [`NameSpace`].
|
||||
/// Get the [position][Position] of this [`Namespace`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -1,9 +1,7 @@
|
||||
//! Module implementing the [`AST`] optimizer.
|
||||
#![cfg(not(feature = "no_optimize"))]
|
||||
|
||||
use crate::ast::{
|
||||
Expr, OpAssignment, Stmt, StmtBlock, StmtBlockContainer, SwitchCases, AST_OPTION_FLAGS::*,
|
||||
};
|
||||
use crate::ast::{ASTFlags, Expr, OpAssignment, Stmt, StmtBlock, StmtBlockContainer, SwitchCases};
|
||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||
use crate::eval::{EvalState, GlobalRuntimeState};
|
||||
use crate::func::builtin::get_builtin_binary_op_fn;
|
||||
@ -255,7 +253,7 @@ fn optimize_stmt_block(
|
||||
for stmt in statements.iter_mut() {
|
||||
match stmt {
|
||||
Stmt::Var(x, options, ..) => {
|
||||
if options.contains(AST_OPTION_CONSTANT) {
|
||||
if options.contains(ASTFlags::CONSTANT) {
|
||||
// Add constant literals into the state
|
||||
optimize_expr(&mut x.1, state, false);
|
||||
|
||||
@ -324,7 +322,7 @@ fn optimize_stmt_block(
|
||||
match statements[..] {
|
||||
// { return; } -> {}
|
||||
[Stmt::Return(None, options, ..)]
|
||||
if reduce_return && !options.contains(AST_OPTION_BREAK) =>
|
||||
if reduce_return && !options.contains(ASTFlags::BREAK) =>
|
||||
{
|
||||
state.set_dirty();
|
||||
statements.clear();
|
||||
@ -336,7 +334,7 @@ fn optimize_stmt_block(
|
||||
// { ...; return; } -> { ... }
|
||||
[.., ref last_stmt, Stmt::Return(None, options, ..)]
|
||||
if reduce_return
|
||||
&& !options.contains(AST_OPTION_BREAK)
|
||||
&& !options.contains(ASTFlags::BREAK)
|
||||
&& !last_stmt.returns_value() =>
|
||||
{
|
||||
state.set_dirty();
|
||||
@ -344,7 +342,7 @@ fn optimize_stmt_block(
|
||||
}
|
||||
// { ...; return val; } -> { ...; val }
|
||||
[.., Stmt::Return(ref mut expr, options, pos)]
|
||||
if reduce_return && !options.contains(AST_OPTION_BREAK) =>
|
||||
if reduce_return && !options.contains(ASTFlags::BREAK) =>
|
||||
{
|
||||
state.set_dirty();
|
||||
*statements.last_mut().unwrap() = expr
|
||||
@ -381,7 +379,7 @@ fn optimize_stmt_block(
|
||||
}
|
||||
// { ...; return; } -> { ... }
|
||||
[.., Stmt::Return(None, options, ..)]
|
||||
if reduce_return && !options.contains(AST_OPTION_BREAK) =>
|
||||
if reduce_return && !options.contains(ASTFlags::BREAK) =>
|
||||
{
|
||||
state.set_dirty();
|
||||
statements.pop().unwrap();
|
||||
@ -389,7 +387,7 @@ fn optimize_stmt_block(
|
||||
// { ...; return pure_val; } -> { ... }
|
||||
[.., Stmt::Return(Some(ref expr), options, ..)]
|
||||
if reduce_return
|
||||
&& !options.contains(AST_OPTION_BREAK)
|
||||
&& !options.contains(ASTFlags::BREAK)
|
||||
&& expr.is_pure() =>
|
||||
{
|
||||
state.set_dirty();
|
||||
@ -745,7 +743,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
if body.len() == 1 {
|
||||
match body[0] {
|
||||
// while expr { break; } -> { expr; }
|
||||
Stmt::BreakLoop(options, pos) if options.contains(AST_OPTION_BREAK) => {
|
||||
Stmt::BreakLoop(options, pos) if options.contains(ASTFlags::BREAK) => {
|
||||
// Only a single break statement - turn into running the guard expression once
|
||||
state.set_dirty();
|
||||
if !condition.is_unit() {
|
||||
@ -765,7 +763,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
// do { block } until true -> { block }
|
||||
Stmt::Do(x, options, ..)
|
||||
if matches!(x.0, Expr::BoolConstant(true, ..))
|
||||
&& options.contains(AST_OPTION_NEGATED) =>
|
||||
&& options.contains(ASTFlags::NEGATED) =>
|
||||
{
|
||||
state.set_dirty();
|
||||
*stmt = (
|
||||
@ -777,7 +775,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
// do { block } while false -> { block }
|
||||
Stmt::Do(x, options, ..)
|
||||
if matches!(x.0, Expr::BoolConstant(false, ..))
|
||||
&& !options.contains(AST_OPTION_NEGATED) =>
|
||||
&& !options.contains(ASTFlags::NEGATED) =>
|
||||
{
|
||||
state.set_dirty();
|
||||
*stmt = (
|
||||
@ -797,7 +795,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
*x.3 = optimize_stmt_block(mem::take(&mut *x.3), state, false, true, false);
|
||||
}
|
||||
// let id = expr;
|
||||
Stmt::Var(x, options, ..) if !options.contains(AST_OPTION_CONSTANT) => {
|
||||
Stmt::Var(x, options, ..) if !options.contains(ASTFlags::CONSTANT) => {
|
||||
optimize_expr(&mut x.1, state, false)
|
||||
}
|
||||
// import expr as var;
|
||||
|
@ -4,9 +4,8 @@ use crate::api::custom_syntax::{markers::*, CustomSyntax};
|
||||
use crate::api::events::VarDefInfo;
|
||||
use crate::api::options::LanguageOptions;
|
||||
use crate::ast::{
|
||||
BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
||||
ASTFlags, BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
||||
OpAssignment, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer, SwitchCases, TryCatchBlock,
|
||||
AST_OPTION_FLAGS::*,
|
||||
};
|
||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||
use crate::eval::{EvalState, GlobalRuntimeState};
|
||||
@ -764,14 +763,14 @@ fn parse_index_chain(
|
||||
// Indexing binds to right
|
||||
Ok(Expr::Index(
|
||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||
false,
|
||||
ASTFlags::NONE,
|
||||
prev_pos,
|
||||
))
|
||||
}
|
||||
// Otherwise terminate the indexing chain
|
||||
_ => Ok(Expr::Index(
|
||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||
true,
|
||||
ASTFlags::BREAK,
|
||||
settings.pos,
|
||||
)),
|
||||
}
|
||||
@ -1599,7 +1598,7 @@ fn parse_postfix(
|
||||
}
|
||||
|
||||
let rhs = parse_primary(input, state, lib, settings.level_up())?;
|
||||
make_dot_expr(state, expr, false, rhs, tail_pos)?
|
||||
make_dot_expr(state, expr, ASTFlags::NONE, rhs, tail_pos)?
|
||||
}
|
||||
// Unknown postfix operator
|
||||
(expr, token) => unreachable!(
|
||||
@ -1764,15 +1763,20 @@ fn make_assignment_stmt(
|
||||
#[must_use]
|
||||
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
|
||||
match expr {
|
||||
Expr::Index(x, term, ..) | Expr::Dot(x, term, ..) if parent_is_dot => match x.lhs {
|
||||
Expr::Property(..) if !term => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(..))),
|
||||
Expr::Index(x, options, ..) | Expr::Dot(x, options, ..) if parent_is_dot => match x.lhs
|
||||
{
|
||||
Expr::Property(..) if !options.contains(ASTFlags::BREAK) => {
|
||||
check_lvalue(&x.rhs, matches!(expr, Expr::Dot(..)))
|
||||
}
|
||||
Expr::Property(..) => None,
|
||||
// Anything other than a property after dotting (e.g. a method call) is not an l-value
|
||||
ref e => Some(e.position()),
|
||||
},
|
||||
Expr::Index(x, term, ..) | Expr::Dot(x, term, ..) => match x.lhs {
|
||||
Expr::Index(x, options, ..) | Expr::Dot(x, options, ..) => match x.lhs {
|
||||
Expr::Property(..) => unreachable!("unexpected Expr::Property in indexing"),
|
||||
_ if !term => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(..))),
|
||||
_ if !options.contains(ASTFlags::BREAK) => {
|
||||
check_lvalue(&x.rhs, matches!(expr, Expr::Dot(..)))
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Expr::Property(..) if parent_is_dot => None,
|
||||
@ -1817,8 +1821,8 @@ fn make_assignment_stmt(
|
||||
}
|
||||
}
|
||||
// xxx[???]... = rhs, xxx.prop... = rhs
|
||||
Expr::Index(ref x, term, ..) | Expr::Dot(ref x, term, ..) => {
|
||||
let valid_lvalue = if term {
|
||||
Expr::Index(ref x, options, ..) | Expr::Dot(ref x, options, ..) => {
|
||||
let valid_lvalue = if options.contains(ASTFlags::BREAK) {
|
||||
None
|
||||
} else {
|
||||
check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(..)))
|
||||
@ -1890,15 +1894,15 @@ fn parse_op_assignment_stmt(
|
||||
fn make_dot_expr(
|
||||
state: &mut ParseState,
|
||||
lhs: Expr,
|
||||
terminate_chaining: bool,
|
||||
parent_options: ASTFlags,
|
||||
rhs: Expr,
|
||||
op_pos: Position,
|
||||
) -> ParseResult<Expr> {
|
||||
match (lhs, rhs) {
|
||||
// lhs[idx_expr].rhs
|
||||
(Expr::Index(mut x, term, pos), rhs) => {
|
||||
x.rhs = make_dot_expr(state, x.rhs, term || terminate_chaining, rhs, op_pos)?;
|
||||
Ok(Expr::Index(x, false, pos))
|
||||
(Expr::Index(mut x, options, pos), rhs) => {
|
||||
x.rhs = make_dot_expr(state, x.rhs, options | parent_options, rhs, op_pos)?;
|
||||
Ok(Expr::Index(x, ASTFlags::NONE, pos))
|
||||
}
|
||||
// lhs.module::id - syntax error
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -1908,12 +1912,16 @@ fn make_dot_expr(
|
||||
// lhs.id
|
||||
(lhs, var_expr @ Expr::Variable(..)) => {
|
||||
let rhs = var_expr.into_property(state);
|
||||
Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos))
|
||||
Ok(Expr::Dot(
|
||||
BinaryExpr { lhs, rhs }.into(),
|
||||
ASTFlags::NONE,
|
||||
op_pos,
|
||||
))
|
||||
}
|
||||
// lhs.prop
|
||||
(lhs, prop @ Expr::Property(..)) => Ok(Expr::Dot(
|
||||
BinaryExpr { lhs, rhs: prop }.into(),
|
||||
false,
|
||||
ASTFlags::NONE,
|
||||
op_pos,
|
||||
)),
|
||||
// lhs.nnn::func(...) - syntax error
|
||||
@ -1950,7 +1958,11 @@ fn make_dot_expr(
|
||||
);
|
||||
|
||||
let rhs = Expr::FnCall(func, func_pos);
|
||||
Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos))
|
||||
Ok(Expr::Dot(
|
||||
BinaryExpr { lhs, rhs }.into(),
|
||||
ASTFlags::NONE,
|
||||
op_pos,
|
||||
))
|
||||
}
|
||||
// lhs.dot_lhs.dot_rhs or lhs.dot_lhs[idx_rhs]
|
||||
(lhs, rhs @ Expr::Dot(..)) | (lhs, rhs @ Expr::Index(..)) => {
|
||||
@ -1984,7 +1996,11 @@ fn make_dot_expr(
|
||||
} else {
|
||||
Expr::Index(new_lhs, term, pos)
|
||||
};
|
||||
Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos))
|
||||
Ok(Expr::Dot(
|
||||
BinaryExpr { lhs, rhs }.into(),
|
||||
ASTFlags::NONE,
|
||||
op_pos,
|
||||
))
|
||||
}
|
||||
// lhs.func().dot_rhs or lhs.func()[idx_rhs]
|
||||
Expr::FnCall(mut func, func_pos) => {
|
||||
@ -2006,7 +2022,11 @@ fn make_dot_expr(
|
||||
} else {
|
||||
Expr::Index(new_lhs, term, pos)
|
||||
};
|
||||
Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos))
|
||||
Ok(Expr::Dot(
|
||||
BinaryExpr { lhs, rhs }.into(),
|
||||
ASTFlags::NONE,
|
||||
op_pos,
|
||||
))
|
||||
}
|
||||
expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||
}
|
||||
@ -2482,8 +2502,8 @@ fn parse_do(
|
||||
let body = parse_block(input, state, lib, settings.level_up())?;
|
||||
|
||||
let negated = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::While, ..) => AST_OPTION_NONE,
|
||||
(Token::Until, ..) => AST_OPTION_NEGATED,
|
||||
(Token::While, ..) => ASTFlags::NONE,
|
||||
(Token::Until, ..) => ASTFlags::NEGATED,
|
||||
(.., pos) => {
|
||||
return Err(
|
||||
PERR::MissingToken(Token::While.into(), "for the do statement".into())
|
||||
@ -2657,9 +2677,9 @@ fn parse_let(
|
||||
};
|
||||
|
||||
let export = if is_export {
|
||||
AST_OPTION_EXPORTED
|
||||
ASTFlags::EXPORTED
|
||||
} else {
|
||||
AST_OPTION_NONE
|
||||
ASTFlags::NONE
|
||||
};
|
||||
|
||||
let existing = state.stack.get_index(&name).and_then(|(n, ..)| {
|
||||
@ -2685,7 +2705,7 @@ fn parse_let(
|
||||
// let name = expr
|
||||
AccessMode::ReadWrite => Stmt::Var(var_def, export, settings.pos),
|
||||
// const name = { expr:constant }
|
||||
AccessMode::ReadOnly => Stmt::Var(var_def, AST_OPTION_CONSTANT + export, settings.pos),
|
||||
AccessMode::ReadOnly => Stmt::Var(var_def, ASTFlags::CONSTANT | export, settings.pos),
|
||||
})
|
||||
}
|
||||
|
||||
@ -3044,11 +3064,11 @@ fn parse_stmt(
|
||||
|
||||
Token::Continue if settings.default_options.allow_looping && settings.is_breakable => {
|
||||
let pos = eat_token(input, Token::Continue);
|
||||
Ok(Stmt::BreakLoop(AST_OPTION_NONE, pos))
|
||||
Ok(Stmt::BreakLoop(ASTFlags::NONE, pos))
|
||||
}
|
||||
Token::Break if settings.default_options.allow_looping && settings.is_breakable => {
|
||||
let pos = eat_token(input, Token::Break);
|
||||
Ok(Stmt::BreakLoop(AST_OPTION_BREAK, pos))
|
||||
Ok(Stmt::BreakLoop(ASTFlags::BREAK, pos))
|
||||
}
|
||||
Token::Continue | Token::Break if settings.default_options.allow_looping => {
|
||||
Err(PERR::LoopBreak.into_err(token_pos))
|
||||
@ -3059,8 +3079,8 @@ fn parse_stmt(
|
||||
.next()
|
||||
.map(|(token, pos)| {
|
||||
let flags = match token {
|
||||
Token::Return => AST_OPTION_NONE,
|
||||
Token::Throw => AST_OPTION_BREAK,
|
||||
Token::Return => ASTFlags::NONE,
|
||||
Token::Throw => ASTFlags::BREAK,
|
||||
token => unreachable!(
|
||||
"Token::Return or Token::Throw expected but gets {:?}",
|
||||
token
|
||||
|
@ -84,7 +84,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert_eq!(
|
||||
format!("{:?}", ast),
|
||||
r#"AST { body: [Var(("DECISION" @ 1:7, false @ 1:18, None), (Constant), 1:1), Expr(123 @ 1:51)] }"#
|
||||
r#"AST { body: [Var(("DECISION" @ 1:7, false @ 1:18, None), CONSTANT, 1:1), Expr(123 @ 1:51)] }"#
|
||||
);
|
||||
|
||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||
|
Loading…
Reference in New Issue
Block a user