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" ] }
|
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 }
|
||||||
|
|
||||||
|
@ -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).
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
172
src/ast/flags.rs
172
src/ast/flags.rs
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"))]
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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")]
|
||||||
|
@ -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
|
||||||
///
|
///
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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 }")?;
|
||||||
|
Loading…
Reference in New Issue
Block a user