Use option flags for certain AST nodes.
This commit is contained in:
parent
7956f9d59c
commit
43de522568
@ -44,6 +44,8 @@ Enhancements
|
|||||||
### `AST` API
|
### `AST` API
|
||||||
|
|
||||||
* Added `ASTNode::position`.
|
* Added `ASTNode::position`.
|
||||||
|
* `ReturnType` is removed in favor of option flags for `Stmt::Return`.
|
||||||
|
* `Stmt::Break` and `Stmt::Continue` are merged into `Stmt::BreakLoop` via an option flag.
|
||||||
|
|
||||||
|
|
||||||
Version 1.0.4
|
Version 1.0.4
|
||||||
|
93
src/ast.rs
93
src/ast.rs
@ -837,20 +837,6 @@ impl fmt::Debug for Ident {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A type encapsulating the mode of a `return`/`throw` statement.
|
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
///
|
|
||||||
/// # Volatile Data Structure
|
|
||||||
///
|
|
||||||
/// This type is volatile and may change.
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
|
||||||
pub enum ReturnType {
|
|
||||||
/// `return` statement.
|
|
||||||
Return,
|
|
||||||
/// `throw` statement.
|
|
||||||
Exception,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -859,7 +845,9 @@ pub enum ReturnType {
|
|||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum ASTNode<'a> {
|
pub enum ASTNode<'a> {
|
||||||
|
/// A statement ([`Stmt`]).
|
||||||
Stmt(&'a Stmt),
|
Stmt(&'a Stmt),
|
||||||
|
/// An expression ([`Expr`]).
|
||||||
Expr(&'a Expr),
|
Expr(&'a Expr),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -885,7 +873,7 @@ impl ASTNode<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A statements block.
|
/// _(internals)_ A scoped block of statements.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # Volatile Data Structure
|
/// # Volatile Data Structure
|
||||||
@ -914,18 +902,12 @@ impl StmtBlock {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
/// Get the position of this statements block.
|
/// Get the position (location of the beginning `{`) of this statements block.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn position(&self) -> Position {
|
pub const fn position(&self) -> Position {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
/// Get the statements of this statements block.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn statements_mut(&mut self) -> &mut StaticVec<Stmt> {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for StmtBlock {
|
impl Deref for StmtBlock {
|
||||||
@ -1026,12 +1008,15 @@ pub mod AST_OPTION_FLAGS {
|
|||||||
/// _(internals)_ The [`AST`][crate::AST] node is constant.
|
/// _(internals)_ The [`AST`][crate::AST] node is constant.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
pub const AST_OPTION_CONSTANT: OptionFlags = OptionFlags(0b0000_0001);
|
pub const AST_OPTION_CONSTANT: OptionFlags = OptionFlags(0b0000_0001);
|
||||||
/// _(internals)_ The [`AST`][crate::AST] node is exported.
|
/// _(internals)_ The [`AST`][crate::AST] node is public.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
pub const AST_OPTION_EXPORTED: OptionFlags = OptionFlags(0b0000_0010);
|
pub const AST_OPTION_PUBLIC: OptionFlags = OptionFlags(0b0000_0010);
|
||||||
/// _(internals)_ The [`AST`][crate::AST] node is in negated mode.
|
/// _(internals)_ The [`AST`][crate::AST] node is in negated mode.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
pub const AST_OPTION_NEGATED: OptionFlags = OptionFlags(0b0000_0100);
|
pub const AST_OPTION_NEGATED: OptionFlags = OptionFlags(0b0000_0100);
|
||||||
|
/// _(internals)_ The [`AST`][crate::AST] node breaks out of normal control flow.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
pub const AST_OPTION_BREAK_OUT: OptionFlags = OptionFlags(0b0000_1000);
|
||||||
|
|
||||||
impl std::fmt::Debug for OptionFlags {
|
impl std::fmt::Debug for OptionFlags {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@ -1056,8 +1041,9 @@ pub mod AST_OPTION_FLAGS {
|
|||||||
|
|
||||||
f.write_str("(")?;
|
f.write_str("(")?;
|
||||||
write_option(self, f, num_flags, AST_OPTION_CONSTANT, "Constant")?;
|
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_PUBLIC, "Public")?;
|
||||||
write_option(self, f, num_flags, AST_OPTION_NEGATED, "Negated")?;
|
write_option(self, f, num_flags, AST_OPTION_NEGATED, "Negated")?;
|
||||||
|
write_option(self, f, num_flags, AST_OPTION_BREAK_OUT, "Break")?;
|
||||||
f.write_str(")")?;
|
f.write_str(")")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1100,7 +1086,7 @@ pub enum Stmt {
|
|||||||
///
|
///
|
||||||
/// ### Option Flags
|
/// ### Option Flags
|
||||||
///
|
///
|
||||||
/// * [`AST_FLAG_EXPORTED`][AST_FLAGS::AST_FLAG_EXPORTED] = `export`
|
/// * [`AST_FLAG_PUBLIC`][AST_FLAGS::AST_FLAG_PUBLIC] = `export`
|
||||||
/// * [`AST_FLAG_CONSTANT`][AST_FLAGS::AST_FLAG_CONSTANT] = `const`
|
/// * [`AST_FLAG_CONSTANT`][AST_FLAGS::AST_FLAG_CONSTANT] = `const`
|
||||||
Var(Expr, Box<Ident>, OptionFlags, Position),
|
Var(Expr, Box<Ident>, OptionFlags, Position),
|
||||||
/// expr op`=` expr
|
/// expr op`=` expr
|
||||||
@ -1116,12 +1102,20 @@ pub enum Stmt {
|
|||||||
TryCatch(Box<(StmtBlock, Option<Ident>, StmtBlock)>, Position),
|
TryCatch(Box<(StmtBlock, Option<Ident>, StmtBlock)>, Position),
|
||||||
/// [expression][Expr]
|
/// [expression][Expr]
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
/// `continue`
|
/// `continue`/`break`
|
||||||
Continue(Position),
|
///
|
||||||
/// `break`
|
/// ### Option Flags
|
||||||
Break(Position),
|
///
|
||||||
|
/// * [`AST_FLAG_NONE`][AST_FLAGS::AST_FLAG_NONE] = `continue`
|
||||||
|
/// * [`AST_FLAG_BREAK_OUT`][AST_FLAGS::AST_FLAG_BREAK_OUT] = `break`
|
||||||
|
BreakLoop(OptionFlags, Position),
|
||||||
/// `return`/`throw`
|
/// `return`/`throw`
|
||||||
Return(ReturnType, Option<Expr>, Position),
|
///
|
||||||
|
/// ### Option Flags
|
||||||
|
///
|
||||||
|
/// * [`AST_FLAG_NONE`][AST_FLAGS::AST_FLAG_NONE] = `return`
|
||||||
|
/// * [`AST_FLAG_BREAK_OUT`][AST_FLAGS::AST_FLAG_BREAK_OUT] = `throw`
|
||||||
|
Return(OptionFlags, Option<Expr>, Position),
|
||||||
/// `import` expr `as` var
|
/// `import` expr `as` var
|
||||||
///
|
///
|
||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
@ -1135,6 +1129,11 @@ pub enum Stmt {
|
|||||||
/// Convert a variable to shared.
|
/// Convert a variable to shared.
|
||||||
///
|
///
|
||||||
/// Not available under `no_closure`.
|
/// Not available under `no_closure`.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
///
|
||||||
|
/// This variant does not map to any language structure. It is currently only used only to
|
||||||
|
/// convert a normal variable into a shared variable when the variable is _captured_ by a closure.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Share(Identifier),
|
Share(Identifier),
|
||||||
}
|
}
|
||||||
@ -1172,8 +1171,7 @@ impl Stmt {
|
|||||||
pub const fn position(&self) -> Position {
|
pub const fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Self::Noop(pos)
|
Self::Noop(pos)
|
||||||
| Self::Continue(pos)
|
| Self::BreakLoop(_, pos)
|
||||||
| Self::Break(pos)
|
|
||||||
| Self::Block(_, pos)
|
| Self::Block(_, pos)
|
||||||
| Self::Assignment(_, pos)
|
| Self::Assignment(_, pos)
|
||||||
| Self::FnCall(_, pos)
|
| Self::FnCall(_, pos)
|
||||||
@ -1201,8 +1199,7 @@ impl Stmt {
|
|||||||
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Noop(pos)
|
Self::Noop(pos)
|
||||||
| Self::Continue(pos)
|
| Self::BreakLoop(_, pos)
|
||||||
| Self::Break(pos)
|
|
||||||
| Self::Block(_, pos)
|
| Self::Block(_, pos)
|
||||||
| Self::Assignment(_, pos)
|
| Self::Assignment(_, pos)
|
||||||
| Self::FnCall(_, pos)
|
| Self::FnCall(_, pos)
|
||||||
@ -1248,8 +1245,7 @@ impl Stmt {
|
|||||||
|
|
||||||
Self::Var(_, _, _, _)
|
Self::Var(_, _, _, _)
|
||||||
| Self::Assignment(_, _)
|
| Self::Assignment(_, _)
|
||||||
| Self::Continue(_)
|
| Self::BreakLoop(_, _)
|
||||||
| Self::Break(_)
|
|
||||||
| Self::Return(_, _, _) => false,
|
| Self::Return(_, _, _) => false,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1280,8 +1276,7 @@ impl Stmt {
|
|||||||
| Self::Expr(_)
|
| Self::Expr(_)
|
||||||
| Self::FnCall(_, _)
|
| Self::FnCall(_, _)
|
||||||
| Self::Do(_, _, _, _)
|
| Self::Do(_, _, _, _)
|
||||||
| Self::Continue(_)
|
| Self::BreakLoop(_, _)
|
||||||
| Self::Break(_)
|
|
||||||
| Self::Return(_, _, _) => false,
|
| Self::Return(_, _, _) => false,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1330,7 +1325,7 @@ impl Stmt {
|
|||||||
|
|
||||||
Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false,
|
Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false,
|
||||||
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
||||||
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,
|
Self::BreakLoop(_, _) | Self::Return(_, _, _) => false,
|
||||||
Self::TryCatch(x, _) => {
|
Self::TryCatch(x, _) => {
|
||||||
(x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure)
|
(x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure)
|
||||||
}
|
}
|
||||||
@ -1348,8 +1343,8 @@ impl Stmt {
|
|||||||
///
|
///
|
||||||
/// An internally pure statement only has side effects that disappear outside the block.
|
/// An internally pure statement only has side effects that disappear outside the block.
|
||||||
///
|
///
|
||||||
/// Only variable declarations (i.e. `let` and `const`) and `import`/`export` statements
|
/// Currently only variable declarations (i.e. `let` and `const`) and `import`/`export`
|
||||||
/// are internally pure.
|
/// statements are internally pure.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_internally_pure(&self) -> bool {
|
pub fn is_internally_pure(&self) -> bool {
|
||||||
@ -1373,7 +1368,7 @@ impl Stmt {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_control_flow_break(&self) -> bool {
|
pub const fn is_control_flow_break(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Return(_, _, _) | Self::Break(_) | Self::Continue(_) => true,
|
Self::Return(_, _, _) | Self::BreakLoop(_, _) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1516,7 +1511,8 @@ impl Stmt {
|
|||||||
pub struct CustomExpr {
|
pub struct CustomExpr {
|
||||||
/// List of keywords.
|
/// List of keywords.
|
||||||
pub keywords: StaticVec<Expr>,
|
pub keywords: StaticVec<Expr>,
|
||||||
/// Is the current [`Scope`][crate::Scope] modified?
|
/// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement
|
||||||
|
/// (e.g. introducing a new variable)?
|
||||||
pub scope_may_be_changed: bool,
|
pub scope_may_be_changed: bool,
|
||||||
/// List of tokens actually parsed.
|
/// List of tokens actually parsed.
|
||||||
pub tokens: StaticVec<Identifier>,
|
pub tokens: StaticVec<Identifier>,
|
||||||
@ -1689,6 +1685,9 @@ pub struct FnCallExpr {
|
|||||||
/// List of function call argument expressions.
|
/// List of function call argument expressions.
|
||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
/// List of function call arguments that are constants.
|
/// List of function call arguments that are constants.
|
||||||
|
///
|
||||||
|
/// Any arguments in `args` that is [`Expr::Stack`][Expr::Stack] indexes into this
|
||||||
|
/// array to find the constant for use as its argument value.
|
||||||
pub constants: smallvec::SmallVec<[Dynamic; 2]>,
|
pub constants: smallvec::SmallVec<[Dynamic; 2]>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
@ -1889,6 +1888,12 @@ pub enum Expr {
|
|||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
/// Stack slot
|
/// Stack slot
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
///
|
||||||
|
/// This variant does not map to any language structure. It is currently only used in function
|
||||||
|
/// calls with constant arguments where the `usize` number indexes into an array containing a
|
||||||
|
/// list of constant arguments for the function call. See [`FnCallExpr`] for more details.
|
||||||
Stack(usize, Position),
|
Stack(usize, Position),
|
||||||
/// { [statement][Stmt] ... }
|
/// { [statement][Stmt] ... }
|
||||||
Stmt(Box<StmtBlock>),
|
Stmt(Box<StmtBlock>),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Main module defining the script evaluation [`Engine`].
|
//! Main module defining the script evaluation [`Engine`].
|
||||||
|
|
||||||
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt, AST_OPTION_FLAGS::*};
|
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
|
||||||
use crate::custom_syntax::CustomSyntax;
|
use crate::custom_syntax::CustomSyntax;
|
||||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||||
use crate::fn_hash::get_hasher;
|
use crate::fn_hash::get_hasher;
|
||||||
@ -2704,11 +2704,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue statement
|
// Continue/Break statement
|
||||||
Stmt::Continue(pos) => EvalAltResult::LoopBreak(false, *pos).into(),
|
Stmt::BreakLoop(options, pos) => {
|
||||||
|
EvalAltResult::LoopBreak(options.contains(AST_OPTION_BREAK_OUT), *pos).into()
|
||||||
// Break statement
|
}
|
||||||
Stmt::Break(pos) => EvalAltResult::LoopBreak(true, *pos).into(),
|
|
||||||
|
|
||||||
// Namespace-qualified function call
|
// Namespace-qualified function call
|
||||||
Stmt::FnCall(x, pos) if x.is_qualified() => {
|
Stmt::FnCall(x, pos) if x.is_qualified() => {
|
||||||
@ -2828,8 +2827,23 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throw value
|
||||||
|
Stmt::Return(options, Some(expr), pos) if options.contains(AST_OPTION_BREAK_OUT) => {
|
||||||
|
EvalAltResult::ErrorRuntime(
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
|
.flatten(),
|
||||||
|
*pos,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty throw
|
||||||
|
Stmt::Return(options, None, pos) if options.contains(AST_OPTION_BREAK_OUT) => {
|
||||||
|
EvalAltResult::ErrorRuntime(Dynamic::UNIT, *pos).into()
|
||||||
|
}
|
||||||
|
|
||||||
// Return value
|
// Return value
|
||||||
Stmt::Return(ReturnType::Return, Some(expr), pos) => EvalAltResult::Return(
|
Stmt::Return(_, Some(expr), pos) => EvalAltResult::Return(
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten(),
|
.flatten(),
|
||||||
*pos,
|
*pos,
|
||||||
@ -2837,22 +2851,7 @@ impl Engine {
|
|||||||
.into(),
|
.into(),
|
||||||
|
|
||||||
// Empty return
|
// Empty return
|
||||||
Stmt::Return(ReturnType::Return, None, pos) => {
|
Stmt::Return(_, None, pos) => EvalAltResult::Return(Dynamic::UNIT, *pos).into(),
|
||||||
EvalAltResult::Return(Dynamic::UNIT, *pos).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Throw value
|
|
||||||
Stmt::Return(ReturnType::Exception, Some(expr), pos) => EvalAltResult::ErrorRuntime(
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
|
||||||
.flatten(),
|
|
||||||
*pos,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
|
|
||||||
// Empty throw
|
|
||||||
Stmt::Return(ReturnType::Exception, None, pos) => {
|
|
||||||
EvalAltResult::ErrorRuntime(Dynamic::UNIT, *pos).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let/const statement
|
// Let/const statement
|
||||||
Stmt::Var(expr, x, options, _) => {
|
Stmt::Var(expr, x, options, _) => {
|
||||||
@ -2862,7 +2861,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
AccessMode::ReadWrite
|
AccessMode::ReadWrite
|
||||||
};
|
};
|
||||||
let export = options.contains(AST_OPTION_EXPORTED);
|
let export = options.contains(AST_OPTION_PUBLIC);
|
||||||
|
|
||||||
let value = self
|
let value = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
|
@ -230,7 +230,7 @@ pub use token::{InputStream, Token, TokenizeState, TokenizerControl, TokenizerCo
|
|||||||
#[deprecated = "this type is volatile and may change"]
|
#[deprecated = "this type is volatile and may change"]
|
||||||
pub use ast::{
|
pub use ast::{
|
||||||
ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHashes, Ident,
|
ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHashes, Ident,
|
||||||
OpAssignment, OptionFlags, ReturnType, ScriptFnDef, Stmt, StmtBlock, AST_OPTION_FLAGS::*,
|
OpAssignment, OptionFlags, ScriptFnDef, Stmt, StmtBlock, AST_OPTION_FLAGS::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -16,6 +16,7 @@ use std::{
|
|||||||
any::TypeId,
|
any::TypeId,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
mem,
|
mem,
|
||||||
|
ops::DerefMut,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -267,7 +268,9 @@ fn optimize_stmt_block(
|
|||||||
loop {
|
loop {
|
||||||
match statements[..] {
|
match statements[..] {
|
||||||
// { return; } -> {}
|
// { return; } -> {}
|
||||||
[Stmt::Return(crate::ast::ReturnType::Return, None, _)] if reduce_return => {
|
[Stmt::Return(options, None, _)]
|
||||||
|
if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) =>
|
||||||
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements.clear();
|
statements.clear();
|
||||||
}
|
}
|
||||||
@ -276,8 +279,10 @@ fn optimize_stmt_block(
|
|||||||
statements.clear();
|
statements.clear();
|
||||||
}
|
}
|
||||||
// { ...; return; } -> { ... }
|
// { ...; return; } -> { ... }
|
||||||
[.., ref last_stmt, Stmt::Return(crate::ast::ReturnType::Return, None, _)]
|
[.., ref last_stmt, Stmt::Return(options, None, _)]
|
||||||
if reduce_return && !last_stmt.returns_value() =>
|
if reduce_return
|
||||||
|
&& !options.contains(AST_OPTION_BREAK_OUT)
|
||||||
|
&& !last_stmt.returns_value() =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements
|
statements
|
||||||
@ -285,8 +290,8 @@ fn optimize_stmt_block(
|
|||||||
.expect("`statements` contains at least two elements");
|
.expect("`statements` contains at least two elements");
|
||||||
}
|
}
|
||||||
// { ...; return val; } -> { ...; val }
|
// { ...; return val; } -> { ...; val }
|
||||||
[.., Stmt::Return(crate::ast::ReturnType::Return, ref mut expr, pos)]
|
[.., Stmt::Return(options, ref mut expr, pos)]
|
||||||
if reduce_return =>
|
if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*statements
|
*statements
|
||||||
@ -332,8 +337,8 @@ fn optimize_stmt_block(
|
|||||||
statements.clear();
|
statements.clear();
|
||||||
}
|
}
|
||||||
// { ...; return; } -> { ... }
|
// { ...; return; } -> { ... }
|
||||||
[.., Stmt::Return(crate::ast::ReturnType::Return, None, _)]
|
[.., Stmt::Return(options, None, _)]
|
||||||
if reduce_return =>
|
if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements
|
statements
|
||||||
@ -341,8 +346,10 @@ fn optimize_stmt_block(
|
|||||||
.expect("`statements` contains at least two elements");
|
.expect("`statements` contains at least two elements");
|
||||||
}
|
}
|
||||||
// { ...; return pure_val; } -> { ... }
|
// { ...; return pure_val; } -> { ... }
|
||||||
[.., Stmt::Return(crate::ast::ReturnType::Return, Some(ref expr), _)]
|
[.., Stmt::Return(options, Some(ref expr), _)]
|
||||||
if reduce_return && expr.is_pure() =>
|
if reduce_return
|
||||||
|
&& !options.contains(AST_OPTION_BREAK_OUT)
|
||||||
|
&& expr.is_pure() =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements
|
statements
|
||||||
@ -467,12 +474,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
// if expr { if_block } else { else_block }
|
// if expr { if_block } else { else_block }
|
||||||
Stmt::If(condition, x, _) => {
|
Stmt::If(condition, x, _) => {
|
||||||
optimize_expr(condition, state, false);
|
optimize_expr(condition, state, false);
|
||||||
let if_block = mem::take(x.0.statements_mut()).into_vec();
|
let if_block = mem::take(x.0.deref_mut()).into_vec();
|
||||||
*x.0.statements_mut() =
|
*x.0 = optimize_stmt_block(if_block, state, preserve_result, true, false).into();
|
||||||
optimize_stmt_block(if_block, state, preserve_result, true, false).into();
|
let else_block = mem::take(x.1.deref_mut()).into_vec();
|
||||||
let else_block = mem::take(x.1.statements_mut()).into_vec();
|
*x.1 = optimize_stmt_block(else_block, state, preserve_result, true, false).into();
|
||||||
*x.1.statements_mut() =
|
|
||||||
optimize_stmt_block(else_block, state, preserve_result, true, false).into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch const { ... }
|
// switch const { ... }
|
||||||
@ -545,8 +550,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
_ => {
|
_ => {
|
||||||
block.0 = Some(condition);
|
block.0 = Some(condition);
|
||||||
|
|
||||||
*block.1.statements_mut() = optimize_stmt_block(
|
*block.1 = optimize_stmt_block(
|
||||||
mem::take(block.1.statements_mut()).into_vec(),
|
mem::take(block.1.deref_mut()).into_vec(),
|
||||||
state,
|
state,
|
||||||
preserve_result,
|
preserve_result,
|
||||||
true,
|
true,
|
||||||
@ -566,9 +571,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
x.0.remove(&key);
|
x.0.remove(&key);
|
||||||
}
|
}
|
||||||
|
|
||||||
let def_block = mem::take(x.1.statements_mut()).into_vec();
|
let def_block = mem::take(x.1.deref_mut()).into_vec();
|
||||||
*x.1.statements_mut() =
|
*x.1 = optimize_stmt_block(def_block, state, preserve_result, true, false).into();
|
||||||
optimize_stmt_block(def_block, state, preserve_result, true, false).into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
@ -582,13 +586,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
if let Expr::BoolConstant(true, pos) = condition {
|
if let Expr::BoolConstant(true, pos) = condition {
|
||||||
*condition = Expr::Unit(*pos);
|
*condition = Expr::Unit(*pos);
|
||||||
}
|
}
|
||||||
let block = mem::take(body.statements_mut()).into_vec();
|
let block = mem::take(body.as_mut().deref_mut()).into_vec();
|
||||||
*body.statements_mut() = optimize_stmt_block(block, state, false, true, false).into();
|
*body.as_mut().deref_mut() =
|
||||||
|
optimize_stmt_block(block, state, false, true, false).into();
|
||||||
|
|
||||||
if body.len() == 1 {
|
if body.len() == 1 {
|
||||||
match body[0] {
|
match body[0] {
|
||||||
// while expr { break; } -> { expr; }
|
// while expr { break; } -> { expr; }
|
||||||
Stmt::Break(pos) => {
|
Stmt::BreakLoop(options, pos) if options.contains(AST_OPTION_BREAK_OUT) => {
|
||||||
// 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() {
|
||||||
@ -611,7 +616,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let block_pos = body.position();
|
let block_pos = body.position();
|
||||||
let block = mem::take(body.statements_mut()).into_vec();
|
let block = mem::take(body.as_mut().deref_mut()).into_vec();
|
||||||
*stmt = Stmt::Block(
|
*stmt = Stmt::Block(
|
||||||
optimize_stmt_block(block, state, false, true, false).into_boxed_slice(),
|
optimize_stmt_block(block, state, false, true, false).into_boxed_slice(),
|
||||||
block_pos,
|
block_pos,
|
||||||
@ -620,14 +625,15 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
// do { block } while|until expr
|
// do { block } while|until expr
|
||||||
Stmt::Do(body, condition, _, _) => {
|
Stmt::Do(body, condition, _, _) => {
|
||||||
optimize_expr(condition, state, false);
|
optimize_expr(condition, state, false);
|
||||||
let block = mem::take(body.statements_mut()).into_vec();
|
let block = mem::take(body.as_mut().deref_mut()).into_vec();
|
||||||
*body.statements_mut() = optimize_stmt_block(block, state, false, true, false).into();
|
*body.as_mut().deref_mut() =
|
||||||
|
optimize_stmt_block(block, state, false, true, false).into();
|
||||||
}
|
}
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(iterable, x, _) => {
|
Stmt::For(iterable, x, _) => {
|
||||||
optimize_expr(iterable, state, false);
|
optimize_expr(iterable, state, false);
|
||||||
let body = mem::take(x.2.statements_mut()).into_vec();
|
let body = mem::take(x.2.deref_mut()).into_vec();
|
||||||
*x.2.statements_mut() = optimize_stmt_block(body, state, false, true, false).into();
|
*x.2 = optimize_stmt_block(body, state, false, true, false).into();
|
||||||
}
|
}
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Var(expr, _, options, _) if !options.contains(AST_OPTION_CONSTANT) => {
|
Stmt::Var(expr, _, options, _) if !options.contains(AST_OPTION_CONSTANT) => {
|
||||||
@ -667,12 +673,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
Stmt::TryCatch(x, _) => {
|
Stmt::TryCatch(x, _) => {
|
||||||
let try_block = mem::take(x.0.statements_mut()).into_vec();
|
let try_block = mem::take(x.0.deref_mut()).into_vec();
|
||||||
*x.0.statements_mut() =
|
*x.0 = optimize_stmt_block(try_block, state, false, true, false).into();
|
||||||
optimize_stmt_block(try_block, state, false, true, false).into();
|
let catch_block = mem::take(x.2.deref_mut()).into_vec();
|
||||||
let catch_block = mem::take(x.2.statements_mut()).into_vec();
|
*x.2 = optimize_stmt_block(catch_block, state, false, true, false).into();
|
||||||
*x.2.statements_mut() =
|
|
||||||
optimize_stmt_block(catch_block, state, false, true, false).into();
|
|
||||||
}
|
}
|
||||||
// func(...)
|
// func(...)
|
||||||
Stmt::Expr(expr @ Expr::FnCall(_, _)) => {
|
Stmt::Expr(expr @ Expr::FnCall(_, _)) => {
|
||||||
@ -721,7 +725,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) }
|
Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) }
|
||||||
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
|
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
|
||||||
Expr::Stmt(x) => {
|
Expr::Stmt(x) => {
|
||||||
*x.statements_mut() = optimize_stmt_block(mem::take(x.statements_mut()).into_vec(), state, true, true, false).into();
|
*x.as_mut().deref_mut() =
|
||||||
|
optimize_stmt_block(mem::take(x.as_mut().deref_mut()).into_vec(), state, true, true, false).into();
|
||||||
|
|
||||||
// { Stmt(Expr) } - promote
|
// { Stmt(Expr) } - promote
|
||||||
match x.as_mut().as_mut() {
|
match x.as_mut().as_mut() {
|
||||||
@ -1171,10 +1176,9 @@ pub fn optimize_into_ast(
|
|||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let state = &mut OptimizerState::new(engine, lib2, level);
|
let state = &mut OptimizerState::new(engine, lib2, level);
|
||||||
|
|
||||||
let body = mem::take(fn_def.body.statements_mut()).into_vec();
|
let body = mem::take(fn_def.body.deref_mut()).into_vec();
|
||||||
|
|
||||||
*fn_def.body.statements_mut() =
|
*fn_def.body = optimize_stmt_block(body, state, true, true, true).into();
|
||||||
optimize_stmt_block(body, state, true, true, true).into();
|
|
||||||
|
|
||||||
fn_def
|
fn_def
|
||||||
})
|
})
|
||||||
|
14
src/parse.rs
14
src/parse.rs
@ -1,8 +1,8 @@
|
|||||||
//! Main module defining the lexer and parser.
|
//! Main module defining the lexer and parser.
|
||||||
|
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType,
|
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt,
|
||||||
ScriptFnDef, Stmt, StmtBlock, AST_OPTION_FLAGS::*,
|
StmtBlock, AST_OPTION_FLAGS::*,
|
||||||
};
|
};
|
||||||
use crate::custom_syntax::{markers::*, CustomSyntax};
|
use crate::custom_syntax::{markers::*, CustomSyntax};
|
||||||
use crate::dynamic::AccessMode;
|
use crate::dynamic::AccessMode;
|
||||||
@ -2428,7 +2428,7 @@ fn parse_let(
|
|||||||
state.stack.push((name, var_type));
|
state.stack.push((name, var_type));
|
||||||
|
|
||||||
let export = if is_export {
|
let export = if is_export {
|
||||||
AST_OPTION_EXPORTED
|
AST_OPTION_PUBLIC
|
||||||
} else {
|
} else {
|
||||||
AST_OPTION_NONE
|
AST_OPTION_NONE
|
||||||
};
|
};
|
||||||
@ -2829,11 +2829,11 @@ fn parse_stmt(
|
|||||||
|
|
||||||
Token::Continue if settings.is_breakable => {
|
Token::Continue if settings.is_breakable => {
|
||||||
let pos = eat_token(input, Token::Continue);
|
let pos = eat_token(input, Token::Continue);
|
||||||
Ok(Stmt::Continue(pos))
|
Ok(Stmt::BreakLoop(AST_OPTION_NONE, pos))
|
||||||
}
|
}
|
||||||
Token::Break if settings.is_breakable => {
|
Token::Break if settings.is_breakable => {
|
||||||
let pos = eat_token(input, Token::Break);
|
let pos = eat_token(input, Token::Break);
|
||||||
Ok(Stmt::Break(pos))
|
Ok(Stmt::BreakLoop(AST_OPTION_BREAK_OUT, pos))
|
||||||
}
|
}
|
||||||
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)),
|
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)),
|
||||||
|
|
||||||
@ -2843,8 +2843,8 @@ fn parse_stmt(
|
|||||||
.map(|(token, pos)| {
|
.map(|(token, pos)| {
|
||||||
(
|
(
|
||||||
match token {
|
match token {
|
||||||
Token::Return => ReturnType::Return,
|
Token::Return => AST_OPTION_NONE,
|
||||||
Token::Throw => ReturnType::Exception,
|
Token::Throw => AST_OPTION_BREAK_OUT,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
pos,
|
pos,
|
||||||
|
Loading…
Reference in New Issue
Block a user