Use bitflags.

This commit is contained in:
Stephen Chung 2022-02-25 11:42:59 +08:00
parent f47b911681
commit 8205547d8a
14 changed files with 143 additions and 266 deletions

View File

@ -20,6 +20,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
smallvec = { version = "1.7", default-features = false, features = ["union", "const_new" ] } smallvec = { version = "1.7", default-features = false, features = ["union", "const_new" ] }
ahash = { version = "0.7", default-features = false } ahash = { version = "0.7", default-features = false }
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
bitflags = { version = "1", default-features = false }
smartstring = { version = "1", default-features = false } smartstring = { version = "1", default-features = false }
rhai_codegen = { version = "1.2", path = "codegen", default-features = false } rhai_codegen = { version = "1.2", path = "codegen", default-features = false }

View File

@ -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). * 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. * 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). * 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). * 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. * 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). * Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts).

View File

@ -1,6 +1,6 @@
//! Module defining the AST (abstract syntax tree). //! 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}; use crate::{Dynamic, FnNamespace, Identifier, Position};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -745,12 +745,12 @@ impl AST {
) -> impl Iterator<Item = (&str, bool, Dynamic)> { ) -> impl Iterator<Item = (&str, bool, Dynamic)> {
self.statements().iter().filter_map(move |stmt| match stmt { self.statements().iter().filter_map(move |stmt| match stmt {
Stmt::Var(x, options, ..) Stmt::Var(x, options, ..)
if options.contains(AST_OPTION_CONSTANT) && include_constants if options.contains(ASTFlags::CONSTANT) && include_constants
|| !options.contains(AST_OPTION_CONSTANT) && include_variables => || !options.contains(ASTFlags::CONSTANT) && include_variables =>
{ {
let (name, expr, ..) = x.as_ref(); let (name, expr, ..) = x.as_ref();
if let Some(value) = expr.get_literal_value() { 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 { } else {
None None
} }

View File

@ -1,6 +1,6 @@
//! Module defining script expressions. //! 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::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
use crate::func::hashing::ALT_ZERO_HASH; use crate::func::hashing::ALT_ZERO_HASH;
use crate::tokenizer::Token; use crate::tokenizer::Token;
@ -419,10 +419,15 @@ pub enum Expr {
Stmt(Box<StmtBlock>), Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)` /// func `(` expr `,` ... `)`
FnCall(Box<FnCallExpr>, Position), FnCall(Box<FnCallExpr>, Position),
/// lhs `.` rhs - boolean variable is a dummy /// lhs `.` rhs
Dot(Box<BinaryExpr>, bool, Position), Dot(Box<BinaryExpr>, ASTFlags, Position),
/// lhs `[` rhs `]` - boolean indicates whether the dotting/indexing chain stops /// lhs `[` rhs `]`
Index(Box<BinaryExpr>, bool, Position), ///
/// ### Flags
///
/// [`NONE`][ASTFlags::NONE] = recurse into the indexing chain
/// [`BREAK`][ASTFlags::BREAK] = terminate the indexing chain
Index(Box<BinaryExpr>, ASTFlags, Position),
/// lhs `&&` rhs /// lhs `&&` rhs
And(Box<BinaryExpr>, Position), And(Box<BinaryExpr>, Position),
/// lhs `||` rhs /// lhs `||` rhs

View File

@ -1,6 +1,6 @@
//! Module defining script options. //! Module defining script options.
use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Sub, SubAssign}; use bitflags::bitflags;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -13,163 +13,19 @@ pub enum FnAccess {
Public, Public,
} }
/// _(internals)_ A type that holds a configuration option with bit-flags. bitflags! {
/// Exported under the `internals` feature only. /// _(internals)_ A type that holds a configuration option with bit-flags.
///
/// 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.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
pub const AST_OPTION_NONE: OptionFlags = OptionFlags(0b0000_0000); pub struct ASTFlags: u8 {
/// _(internals)_ The [`AST`][crate::AST] node is constant. /// No options for the [`AST`][crate::AST] node.
/// Exported under the `internals` feature only. const NONE = 0b0000_0000;
pub const AST_OPTION_CONSTANT: OptionFlags = OptionFlags(0b0000_0001); /// The [`AST`][crate::AST] node is read-only.
/// _(internals)_ The [`AST`][crate::AST] node is exported to the outside (i.e. public). const CONSTANT = 0b0000_0001;
/// Exported under the `internals` feature only. /// The [`AST`][crate::AST] node is exposed to the outside (i.e. public).
pub const AST_OPTION_EXPORTED: OptionFlags = OptionFlags(0b0000_0010); const EXPORTED = 0b0000_0010;
/// _(internals)_ The [`AST`][crate::AST] node is in negated mode /// The [`AST`][crate::AST] node is negated (i.e. whatever information is the opposite).
/// (meaning whatever information is the opposite). const NEGATED = 0b0000_0100;
/// Exported under the `internals` feature only. /// The [`AST`][crate::AST] node breaks out of normal control flow.
pub const AST_OPTION_NEGATED: OptionFlags = OptionFlags(0b0000_0100); const BREAK = 0b0000_1000;
/// _(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(())
}
} }
} }

View File

@ -9,7 +9,7 @@ pub mod stmt;
pub use ast::{ASTNode, AST}; pub use ast::{ASTNode, AST};
pub use expr::{BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes}; 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; pub use ident::Ident;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]

View File

@ -1,6 +1,6 @@
//! Module defining script statements. //! 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::engine::KEYWORD_EVAL;
use crate::tokenizer::{Span, Token}; use crate::tokenizer::{Span, Token};
use crate::{calc_fn_hash, Position, StaticVec, INT}; use crate::{calc_fn_hash, Position, StaticVec, INT};
@ -340,24 +340,20 @@ pub enum Stmt {
While(Box<(Expr, StmtBlock)>, Position), While(Box<(Expr, StmtBlock)>, Position),
/// `do` `{` stmt `}` `while`|`until` expr /// `do` `{` stmt `}` `while`|`until` expr
/// ///
/// ### Option Flags /// ### Flags
/// ///
/// * [`AST_OPTION_NONE`] = `while` /// * [`NONE`][ASTFlags::NONE] = `while`
/// * [`AST_OPTION_NEGATED`] = `until` /// * [`NEGATED`][ASTFlags::NEGATED] = `until`
Do(Box<(Expr, StmtBlock)>, OptionFlags, Position), Do(Box<(Expr, StmtBlock)>, ASTFlags, Position),
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}` /// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
For(Box<(Ident, Option<Ident>, Expr, StmtBlock)>, Position), For(Box<(Ident, Option<Ident>, Expr, StmtBlock)>, Position),
/// \[`export`\] `let`|`const` id `=` expr /// \[`export`\] `let`|`const` id `=` expr
/// ///
/// ### Option Flags /// ### Flags
/// ///
/// * [`AST_OPTION_EXPORTED`] = `export` /// * [`EXPORTED`][ASTFlags::EXPORTED] = `export`
/// * [`AST_OPTION_CONSTANT`] = `const` /// * [`CONSTANT`][ASTFlags::CONSTANT] = `const`
Var( Var(Box<(Ident, Expr, Option<NonZeroUsize>)>, ASTFlags, Position),
Box<(Ident, Expr, Option<NonZeroUsize>)>,
OptionFlags,
Position,
),
/// expr op`=` expr /// expr op`=` expr
Assignment(Box<(Option<OpAssignment<'static>>, BinaryExpr)>, Position), Assignment(Box<(Option<OpAssignment<'static>>, BinaryExpr)>, Position),
/// func `(` expr `,` ... `)` /// func `(` expr `,` ... `)`
@ -373,18 +369,18 @@ pub enum Stmt {
Expr(Box<Expr>), Expr(Box<Expr>),
/// `continue`/`break` /// `continue`/`break`
/// ///
/// ### Option Flags /// ### Flags
/// ///
/// * [`AST_OPTION_NONE`] = `continue` /// * [`NONE`][ASTFlags::NONE] = `continue`
/// * [`AST_OPTION_BREAK`] = `break` /// * [`BREAK`][ASTFlags::BREAK] = `break`
BreakLoop(OptionFlags, Position), BreakLoop(ASTFlags, Position),
/// `return`/`throw` /// `return`/`throw`
/// ///
/// ### Option Flags /// ### Flags
/// ///
/// * [`AST_OPTION_NONE`] = `return` /// * [`NONE`][ASTFlags::NONE] = `return`
/// * [`AST_OPTION_BREAK`] = `throw` /// * [`BREAK`][ASTFlags::BREAK] = `throw`
Return(Option<Box<Expr>>, OptionFlags, Position), Return(Option<Box<Expr>>, ASTFlags, Position),
/// `import` expr `as` alias /// `import` expr `as` alias
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
@ -590,7 +586,7 @@ impl Stmt {
// Loops that exit can be pure because it can never be infinite. // Loops that exit can be pure because it can never be infinite.
Self::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => true, Self::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => true,
Self::Do(x, options, ..) if matches!(x.0, Expr::BoolConstant(..)) => match x.0 { 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) x.1.iter().all(Stmt::is_pure)
} }
_ => false, _ => false,

View File

@ -2,7 +2,7 @@
#![cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #![cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
use super::{EvalState, GlobalRuntimeState, Target}; use super::{EvalState, GlobalRuntimeState, Target};
use crate::ast::{Expr, OpAssignment}; use crate::ast::{ASTFlags, Expr, OpAssignment};
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR}; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR};
use std::hash::Hash; use std::hash::Hash;
@ -127,15 +127,15 @@ impl Engine {
root: (&str, Position), root: (&str, Position),
parent: &Expr, parent: &Expr,
rhs: &Expr, rhs: &Expr,
terminate_chaining: bool, parent_options: ASTFlags,
idx_values: &mut StaticVec<super::ChainArgument>, idx_values: &mut StaticVec<super::ChainArgument>,
chain_type: ChainType, chain_type: ChainType,
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> RhaiResultOf<(Dynamic, bool)> { ) -> RhaiResultOf<(Dynamic, bool)> {
let _parent = parent; let _parent = parent;
let _parent_options = parent_options;
let is_ref_mut = target.is_ref(); let is_ref_mut = target.is_ref();
let _terminate_chaining = terminate_chaining;
// Pop the last index value // Pop the last index value
let idx_val = idx_values.pop().unwrap(); let idx_val = idx_values.pop().unwrap();
@ -151,8 +151,8 @@ impl Engine {
match rhs { match rhs {
// xxx[idx].expr... | xxx[idx][expr]... // xxx[idx].expr... | xxx[idx][expr]...
Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos) Expr::Dot(x, options, x_pos) | Expr::Index(x, options, x_pos)
if !_terminate_chaining => if !_parent_options.contains(ASTFlags::BREAK) =>
{ {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?;
@ -169,7 +169,7 @@ impl Engine {
let obj_ptr = &mut obj; let obj_ptr = &mut obj;
match self.eval_dot_index_chain_helper( 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, idx_values, rhs_chain, level, new_val,
) { ) {
Ok((result, true)) if is_obj_temp_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 // {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>() => if target.is::<crate::Map>() =>
{ {
let _node = &x.lhs; let _node = &x.lhs;
@ -457,13 +457,13 @@ impl Engine {
let rhs_chain = rhs.into(); let rhs_chain = rhs.into();
self.eval_dot_index_chain_helper( 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, idx_values, rhs_chain, level, new_val,
) )
.map_err(|err| err.fill_position(*x_pos)) .map_err(|err| err.fill_position(*x_pos))
} }
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr // xxx.sub_lhs[expr] | xxx.sub_lhs.expr
Expr::Index(x, 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; let _node = &x.lhs;
match x.lhs { match x.lhs {
@ -509,7 +509,7 @@ impl Engine {
let (result, may_be_changed) = self let (result, may_be_changed) = self
.eval_dot_index_chain_helper( .eval_dot_index_chain_helper(
global, state, lib, this_ptr, val, root, rhs, &x.rhs, 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))?; .map_err(|err| err.fill_position(*x_pos))?;
@ -570,7 +570,7 @@ impl Engine {
let val = &mut val.into(); let val = &mut val.into();
self.eval_dot_index_chain_helper( 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, idx_values, rhs_chain, level, new_val,
) )
.map_err(|err| err.fill_position(pos)) .map_err(|err| err.fill_position(pos))
@ -602,18 +602,18 @@ impl Engine {
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> RhaiResult { ) -> 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"))] #[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"))] #[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), expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr),
}; };
let idx_values = &mut StaticVec::new_const(); let idx_values = &mut StaticVec::new_const();
self.eval_dot_index_chain_arguments( 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(); let is_assignment = new_val.is_some();
@ -634,7 +634,7 @@ impl Engine {
let root = (x.2.as_str(), *var_pos); let root = (x.2.as_str(), *var_pos);
self.eval_dot_index_chain_helper( 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, chain_type, level, new_val,
) )
.map(|(v, ..)| v) .map(|(v, ..)| v)
@ -648,7 +648,7 @@ impl Engine {
let obj_ptr = &mut value.into(); let obj_ptr = &mut value.into();
let root = ("", expr.start_position()); let root = ("", expr.start_position());
self.eval_dot_index_chain_helper( 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, chain_type, level, new_val,
) )
.map(|(v, ..)| if is_assignment { Dynamic::UNIT } else { v }) .map(|(v, ..)| if is_assignment { Dynamic::UNIT } else { v })
@ -668,7 +668,7 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
expr: &Expr, expr: &Expr,
terminate_chaining: bool, parent_options: ASTFlags,
parent_chain_type: ChainType, parent_chain_type: ChainType,
idx_values: &mut StaticVec<super::ChainArgument>, idx_values: &mut StaticVec<super::ChainArgument>,
size: usize, size: usize,
@ -715,7 +715,9 @@ impl Engine {
} }
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"), 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(); let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref();
// Evaluate in left-to-right order // Evaluate in left-to-right order
@ -773,8 +775,8 @@ impl Engine {
let chain_type = expr.into(); let chain_type = expr.into();
self.eval_dot_index_chain_arguments( self.eval_dot_index_chain_arguments(
scope, global, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size, scope, global, state, lib, this_ptr, rhs, *options, chain_type, idx_values,
level, size, level,
)?; )?;
idx_values.push(lhs_arg_val); idx_values.push(lhs_arg_val);

View File

@ -3,7 +3,7 @@
use super::{EvalContext, EvalState, GlobalRuntimeState, Target}; use super::{EvalContext, EvalState, GlobalRuntimeState, Target};
use crate::api::events::VarDefInfo; use crate::api::events::VarDefInfo;
use crate::ast::{ 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::func::get_hasher;
use crate::types::dynamic::{AccessMode, Union}; use crate::types::dynamic::{AccessMode, Union};
@ -537,7 +537,7 @@ impl Engine {
// Do loop // Do loop
Stmt::Do(x, options, ..) => loop { Stmt::Do(x, options, ..) => loop {
let (expr, body) = x.as_ref(); 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() { if !body.is_empty() {
match self match self
@ -700,7 +700,7 @@ impl Engine {
// Continue/Break statement // Continue/Break statement
Stmt::BreakLoop(options, pos) => { 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 // Try/Catch statement
@ -790,12 +790,12 @@ impl Engine {
} }
// Throw value // 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) .eval_expr(scope, global, state, lib, this_ptr, expr, level)
.and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())), .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())),
// Empty throw // 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()) Err(ERR::ErrorRuntime(Dynamic::UNIT, *pos).into())
} }
@ -815,12 +815,12 @@ impl Engine {
Stmt::Var(x, options, pos) => { Stmt::Var(x, options, pos) => {
let (Ident { name: var_name, .. }, expr, index) = x.as_ref(); 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 AccessMode::ReadOnly
} else { } else {
AccessMode::ReadWrite AccessMode::ReadWrite
}; };
let export = options.contains(AST_OPTION_EXPORTED); let export = options.contains(ASTFlags::EXPORTED);
// Check variable definition filter // Check variable definition filter
let result = if let Some(ref filter) = self.def_var_filter { let result = if let Some(ref filter) = self.def_var_filter {

View File

@ -260,9 +260,8 @@ pub use parser::ParseState;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use ast::{ pub use ast::{
ASTNode, BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, ASTFlags, ASTNode, BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr,
OpAssignment, OptionFlags, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock, FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
AST_OPTION_FLAGS,
}; };
#[cfg(feature = "internals")] #[cfg(feature = "internals")]

View File

@ -114,7 +114,7 @@ impl Namespace {
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) { pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
self.index = index self.index = index
} }
/// Get the [position][Position] of this [`NameSpace`]. /// Get the [position][Position] of this [`Namespace`].
/// ///
/// # Panics /// # Panics
/// ///

View File

@ -1,9 +1,7 @@
//! Module implementing the [`AST`] optimizer. //! Module implementing the [`AST`] optimizer.
#![cfg(not(feature = "no_optimize"))] #![cfg(not(feature = "no_optimize"))]
use crate::ast::{ use crate::ast::{ASTFlags, Expr, OpAssignment, Stmt, StmtBlock, StmtBlockContainer, SwitchCases};
Expr, OpAssignment, Stmt, StmtBlock, StmtBlockContainer, SwitchCases, AST_OPTION_FLAGS::*,
};
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::eval::{EvalState, GlobalRuntimeState}; use crate::eval::{EvalState, GlobalRuntimeState};
use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::builtin::get_builtin_binary_op_fn;
@ -255,7 +253,7 @@ fn optimize_stmt_block(
for stmt in statements.iter_mut() { for stmt in statements.iter_mut() {
match stmt { match stmt {
Stmt::Var(x, options, ..) => { Stmt::Var(x, options, ..) => {
if options.contains(AST_OPTION_CONSTANT) { if options.contains(ASTFlags::CONSTANT) {
// Add constant literals into the state // Add constant literals into the state
optimize_expr(&mut x.1, state, false); optimize_expr(&mut x.1, state, false);
@ -324,7 +322,7 @@ fn optimize_stmt_block(
match statements[..] { match statements[..] {
// { return; } -> {} // { return; } -> {}
[Stmt::Return(None, options, ..)] [Stmt::Return(None, options, ..)]
if reduce_return && !options.contains(AST_OPTION_BREAK) => if reduce_return && !options.contains(ASTFlags::BREAK) =>
{ {
state.set_dirty(); state.set_dirty();
statements.clear(); statements.clear();
@ -336,7 +334,7 @@ fn optimize_stmt_block(
// { ...; return; } -> { ... } // { ...; return; } -> { ... }
[.., ref last_stmt, Stmt::Return(None, options, ..)] [.., ref last_stmt, Stmt::Return(None, options, ..)]
if reduce_return if reduce_return
&& !options.contains(AST_OPTION_BREAK) && !options.contains(ASTFlags::BREAK)
&& !last_stmt.returns_value() => && !last_stmt.returns_value() =>
{ {
state.set_dirty(); state.set_dirty();
@ -344,7 +342,7 @@ fn optimize_stmt_block(
} }
// { ...; return val; } -> { ...; val } // { ...; return val; } -> { ...; val }
[.., Stmt::Return(ref mut expr, options, pos)] [.., 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(); state.set_dirty();
*statements.last_mut().unwrap() = expr *statements.last_mut().unwrap() = expr
@ -381,7 +379,7 @@ fn optimize_stmt_block(
} }
// { ...; return; } -> { ... } // { ...; return; } -> { ... }
[.., Stmt::Return(None, options, ..)] [.., Stmt::Return(None, options, ..)]
if reduce_return && !options.contains(AST_OPTION_BREAK) => if reduce_return && !options.contains(ASTFlags::BREAK) =>
{ {
state.set_dirty(); state.set_dirty();
statements.pop().unwrap(); statements.pop().unwrap();
@ -389,7 +387,7 @@ fn optimize_stmt_block(
// { ...; return pure_val; } -> { ... } // { ...; return pure_val; } -> { ... }
[.., Stmt::Return(Some(ref expr), options, ..)] [.., Stmt::Return(Some(ref expr), options, ..)]
if reduce_return if reduce_return
&& !options.contains(AST_OPTION_BREAK) && !options.contains(ASTFlags::BREAK)
&& expr.is_pure() => && expr.is_pure() =>
{ {
state.set_dirty(); state.set_dirty();
@ -745,7 +743,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if body.len() == 1 { if body.len() == 1 {
match body[0] { match body[0] {
// while expr { break; } -> { expr; } // 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 // Only a single break statement - turn into running the guard expression once
state.set_dirty(); state.set_dirty();
if !condition.is_unit() { 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 } // do { block } until true -> { block }
Stmt::Do(x, options, ..) Stmt::Do(x, options, ..)
if matches!(x.0, Expr::BoolConstant(true, ..)) if matches!(x.0, Expr::BoolConstant(true, ..))
&& options.contains(AST_OPTION_NEGATED) => && options.contains(ASTFlags::NEGATED) =>
{ {
state.set_dirty(); state.set_dirty();
*stmt = ( *stmt = (
@ -777,7 +775,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// do { block } while false -> { block } // do { block } while false -> { block }
Stmt::Do(x, options, ..) Stmt::Do(x, options, ..)
if matches!(x.0, Expr::BoolConstant(false, ..)) if matches!(x.0, Expr::BoolConstant(false, ..))
&& !options.contains(AST_OPTION_NEGATED) => && !options.contains(ASTFlags::NEGATED) =>
{ {
state.set_dirty(); state.set_dirty();
*stmt = ( *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); *x.3 = optimize_stmt_block(mem::take(&mut *x.3), state, false, true, false);
} }
// let id = expr; // 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) optimize_expr(&mut x.1, state, false)
} }
// import expr as var; // import expr as var;

View File

@ -4,9 +4,8 @@ use crate::api::custom_syntax::{markers::*, CustomSyntax};
use crate::api::events::VarDefInfo; use crate::api::events::VarDefInfo;
use crate::api::options::LanguageOptions; use crate::api::options::LanguageOptions;
use crate::ast::{ 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, OpAssignment, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer, SwitchCases, TryCatchBlock,
AST_OPTION_FLAGS::*,
}; };
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
use crate::eval::{EvalState, GlobalRuntimeState}; use crate::eval::{EvalState, GlobalRuntimeState};
@ -764,14 +763,14 @@ fn parse_index_chain(
// Indexing binds to right // Indexing binds to right
Ok(Expr::Index( Ok(Expr::Index(
BinaryExpr { lhs, rhs: idx_expr }.into(), BinaryExpr { lhs, rhs: idx_expr }.into(),
false, ASTFlags::NONE,
prev_pos, prev_pos,
)) ))
} }
// Otherwise terminate the indexing chain // Otherwise terminate the indexing chain
_ => Ok(Expr::Index( _ => Ok(Expr::Index(
BinaryExpr { lhs, rhs: idx_expr }.into(), BinaryExpr { lhs, rhs: idx_expr }.into(),
true, ASTFlags::BREAK,
settings.pos, settings.pos,
)), )),
} }
@ -1599,7 +1598,7 @@ fn parse_postfix(
} }
let rhs = parse_primary(input, state, lib, settings.level_up())?; 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 // Unknown postfix operator
(expr, token) => unreachable!( (expr, token) => unreachable!(
@ -1764,15 +1763,20 @@ fn make_assignment_stmt(
#[must_use] #[must_use]
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> { fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
match expr { match expr {
Expr::Index(x, term, ..) | Expr::Dot(x, term, ..) if parent_is_dot => match x.lhs { Expr::Index(x, options, ..) | Expr::Dot(x, options, ..) if parent_is_dot => match x.lhs
Expr::Property(..) if !term => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(..))), {
Expr::Property(..) if !options.contains(ASTFlags::BREAK) => {
check_lvalue(&x.rhs, matches!(expr, Expr::Dot(..)))
}
Expr::Property(..) => None, Expr::Property(..) => None,
// Anything other than a property after dotting (e.g. a method call) is not an l-value // Anything other than a property after dotting (e.g. a method call) is not an l-value
ref e => Some(e.position()), 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"), 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, _ => None,
}, },
Expr::Property(..) if parent_is_dot => None, Expr::Property(..) if parent_is_dot => None,
@ -1817,8 +1821,8 @@ fn make_assignment_stmt(
} }
} }
// xxx[???]... = rhs, xxx.prop... = rhs // xxx[???]... = rhs, xxx.prop... = rhs
Expr::Index(ref x, term, ..) | Expr::Dot(ref x, term, ..) => { Expr::Index(ref x, options, ..) | Expr::Dot(ref x, options, ..) => {
let valid_lvalue = if term { let valid_lvalue = if options.contains(ASTFlags::BREAK) {
None None
} else { } else {
check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(..))) check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(..)))
@ -1890,15 +1894,15 @@ fn parse_op_assignment_stmt(
fn make_dot_expr( fn make_dot_expr(
state: &mut ParseState, state: &mut ParseState,
lhs: Expr, lhs: Expr,
terminate_chaining: bool, parent_options: ASTFlags,
rhs: Expr, rhs: Expr,
op_pos: Position, op_pos: Position,
) -> ParseResult<Expr> { ) -> ParseResult<Expr> {
match (lhs, rhs) { match (lhs, rhs) {
// lhs[idx_expr].rhs // lhs[idx_expr].rhs
(Expr::Index(mut x, term, pos), rhs) => { (Expr::Index(mut x, options, pos), rhs) => {
x.rhs = make_dot_expr(state, x.rhs, term || terminate_chaining, rhs, op_pos)?; x.rhs = make_dot_expr(state, x.rhs, options | parent_options, rhs, op_pos)?;
Ok(Expr::Index(x, false, pos)) Ok(Expr::Index(x, ASTFlags::NONE, pos))
} }
// lhs.module::id - syntax error // lhs.module::id - syntax error
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -1908,12 +1912,16 @@ fn make_dot_expr(
// lhs.id // lhs.id
(lhs, var_expr @ Expr::Variable(..)) => { (lhs, var_expr @ Expr::Variable(..)) => {
let rhs = var_expr.into_property(state); 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
(lhs, prop @ Expr::Property(..)) => Ok(Expr::Dot( (lhs, prop @ Expr::Property(..)) => Ok(Expr::Dot(
BinaryExpr { lhs, rhs: prop }.into(), BinaryExpr { lhs, rhs: prop }.into(),
false, ASTFlags::NONE,
op_pos, op_pos,
)), )),
// lhs.nnn::func(...) - syntax error // lhs.nnn::func(...) - syntax error
@ -1950,7 +1958,11 @@ fn make_dot_expr(
); );
let rhs = Expr::FnCall(func, func_pos); 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.dot_lhs.dot_rhs or lhs.dot_lhs[idx_rhs]
(lhs, rhs @ Expr::Dot(..)) | (lhs, rhs @ Expr::Index(..)) => { (lhs, rhs @ Expr::Dot(..)) | (lhs, rhs @ Expr::Index(..)) => {
@ -1984,7 +1996,11 @@ fn make_dot_expr(
} else { } else {
Expr::Index(new_lhs, term, pos) 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] // lhs.func().dot_rhs or lhs.func()[idx_rhs]
Expr::FnCall(mut func, func_pos) => { Expr::FnCall(mut func, func_pos) => {
@ -2006,7 +2022,11 @@ fn make_dot_expr(
} else { } else {
Expr::Index(new_lhs, term, pos) 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), expr => unreachable!("invalid dot expression: {:?}", expr),
} }
@ -2482,8 +2502,8 @@ fn parse_do(
let body = parse_block(input, state, lib, settings.level_up())?; let body = parse_block(input, state, lib, settings.level_up())?;
let negated = match input.next().expect(NEVER_ENDS) { let negated = match input.next().expect(NEVER_ENDS) {
(Token::While, ..) => AST_OPTION_NONE, (Token::While, ..) => ASTFlags::NONE,
(Token::Until, ..) => AST_OPTION_NEGATED, (Token::Until, ..) => ASTFlags::NEGATED,
(.., pos) => { (.., pos) => {
return Err( return Err(
PERR::MissingToken(Token::While.into(), "for the do statement".into()) PERR::MissingToken(Token::While.into(), "for the do statement".into())
@ -2657,9 +2677,9 @@ fn parse_let(
}; };
let export = if is_export { let export = if is_export {
AST_OPTION_EXPORTED ASTFlags::EXPORTED
} else { } else {
AST_OPTION_NONE ASTFlags::NONE
}; };
let existing = state.stack.get_index(&name).and_then(|(n, ..)| { let existing = state.stack.get_index(&name).and_then(|(n, ..)| {
@ -2685,7 +2705,7 @@ fn parse_let(
// let name = expr // let name = expr
AccessMode::ReadWrite => Stmt::Var(var_def, export, settings.pos), AccessMode::ReadWrite => Stmt::Var(var_def, export, settings.pos),
// const name = { expr:constant } // 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 => { Token::Continue if settings.default_options.allow_looping && settings.is_breakable => {
let pos = eat_token(input, Token::Continue); 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 => { Token::Break if settings.default_options.allow_looping && settings.is_breakable => {
let pos = eat_token(input, Token::Break); 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 => { Token::Continue | Token::Break if settings.default_options.allow_looping => {
Err(PERR::LoopBreak.into_err(token_pos)) Err(PERR::LoopBreak.into_err(token_pos))
@ -3059,8 +3079,8 @@ fn parse_stmt(
.next() .next()
.map(|(token, pos)| { .map(|(token, pos)| {
let flags = match token { let flags = match token {
Token::Return => AST_OPTION_NONE, Token::Return => ASTFlags::NONE,
Token::Throw => AST_OPTION_BREAK, Token::Throw => ASTFlags::BREAK,
token => unreachable!( token => unreachable!(
"Token::Return or Token::Throw expected but gets {:?}", "Token::Return or Token::Throw expected but gets {:?}",
token token

View File

@ -84,7 +84,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
format!("{:?}", ast), 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 }")?; let ast = engine.compile("if 1 == 2 { 42 }")?;