Flatten statement blocks.
This commit is contained in:
parent
2ff2789326
commit
352408fd36
@ -4,6 +4,8 @@ Rhai Release Notes
|
|||||||
Version 0.19.14
|
Version 0.19.14
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
This version runs faster due to optimizations done on AST node structures.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -35,6 +37,7 @@ Breaking changes
|
|||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
* Layout of AST nodes is optimized to reduce redirections, so speed is improved.
|
||||||
* Function calls are more optimized and should now run faster.
|
* Function calls are more optimized and should now run faster.
|
||||||
* `range` function now supports negative step and decreasing streams (i.e. to < from).
|
* `range` function now supports negative step and decreasing streams (i.e. to < from).
|
||||||
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
||||||
|
105
src/ast.rs
105
src/ast.rs
@ -757,8 +757,6 @@ pub struct Ident {
|
|||||||
pub name: ImmutableString,
|
pub name: ImmutableString,
|
||||||
/// Declaration position.
|
/// Declaration position.
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
/// Is this identifier public?
|
|
||||||
pub public: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Ident {
|
impl fmt::Debug for Ident {
|
||||||
@ -806,6 +804,29 @@ impl<'a> From<&'a Expr> for ASTNode<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// _(INTERNALS)_ A statements block.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// # Volatile Data Structure
|
||||||
|
///
|
||||||
|
/// This type is volatile and may change.
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct StmtBlock {
|
||||||
|
pub statements: StaticVec<Stmt>,
|
||||||
|
pub pos: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StmtBlock {
|
||||||
|
/// Is this statements block empty?
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.statements.is_empty()
|
||||||
|
}
|
||||||
|
/// Number of statements in this statements block.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.statements.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ A statement.
|
/// _(INTERNALS)_ A statement.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -817,7 +838,7 @@ pub enum Stmt {
|
|||||||
/// No-op.
|
/// No-op.
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||||
If(Expr, Box<(Stmt, Stmt)>, Position),
|
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
|
||||||
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
||||||
Switch(
|
Switch(
|
||||||
Expr,
|
Expr,
|
||||||
@ -828,21 +849,25 @@ pub enum Stmt {
|
|||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
/// `while` expr `{` stmt `}`
|
/// `while` expr `{` stmt `}`
|
||||||
While(Expr, Box<Stmt>, Position),
|
While(Expr, Box<StmtBlock>, Position),
|
||||||
/// `do` `{` stmt `}` `while`|`until` expr
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
Do(Box<Stmt>, Expr, bool, Position),
|
Do(Box<StmtBlock>, Expr, bool, Position),
|
||||||
/// `for` id `in` expr `{` stmt `}`
|
/// `for` id `in` expr `{` stmt `}`
|
||||||
For(Expr, Box<(String, Stmt)>, Position),
|
For(Expr, Box<(String, StmtBlock)>, Position),
|
||||||
/// \[`export`\] `let` id `=` expr
|
/// \[`export`\] `let` id `=` expr
|
||||||
Let(Expr, Ident, Position),
|
Let(Expr, Ident, bool, Position),
|
||||||
/// \[`export`\] `const` id `=` expr
|
/// \[`export`\] `const` id `=` expr
|
||||||
Const(Expr, Ident, Position),
|
Const(Expr, Ident, bool, Position),
|
||||||
/// expr op`=` expr
|
/// expr op`=` expr
|
||||||
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
|
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
|
||||||
/// `{` stmt`;` ... `}`
|
/// `{` stmt`;` ... `}`
|
||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
||||||
TryCatch(Box<(Stmt, Option<Ident>, Stmt)>, Position, Position),
|
TryCatch(
|
||||||
|
Box<(StmtBlock, Option<Ident>, StmtBlock)>,
|
||||||
|
Position,
|
||||||
|
Position,
|
||||||
|
),
|
||||||
/// [expression][Expr]
|
/// [expression][Expr]
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
/// `continue`
|
/// `continue`
|
||||||
@ -853,7 +878,7 @@ pub enum Stmt {
|
|||||||
Return(ReturnType, Option<Expr>, Position),
|
Return(ReturnType, Option<Expr>, Position),
|
||||||
/// `import` expr `as` var
|
/// `import` expr `as` var
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import(Expr, Ident, Position),
|
Import(Expr, Option<Ident>, Position),
|
||||||
/// `export` var `as` var `,` ...
|
/// `export` var `as` var `,` ...
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Export(Vec<(Ident, Option<Ident>)>, Position),
|
Export(Vec<(Ident, Option<Ident>)>, Position),
|
||||||
@ -869,6 +894,22 @@ impl Default for Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Stmt> for StmtBlock {
|
||||||
|
fn from(stmt: Stmt) -> Self {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Block(block, pos) => Self {
|
||||||
|
statements: block.into(),
|
||||||
|
pos,
|
||||||
|
},
|
||||||
|
Stmt::Noop(pos) => Self {
|
||||||
|
statements: Default::default(),
|
||||||
|
pos,
|
||||||
|
},
|
||||||
|
_ => panic!("cannot convert {:?} into a StmtBlock", stmt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
/// Is this statement [`Noop`][Stmt::Noop]?
|
/// Is this statement [`Noop`][Stmt::Noop]?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -892,8 +933,8 @@ impl Stmt {
|
|||||||
| Self::Do(_, _, _, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::Return(_, _, pos)
|
| Self::Return(_, _, pos)
|
||||||
| Self::Let(_, _, pos)
|
| Self::Let(_, _, _, pos)
|
||||||
| Self::Const(_, _, pos)
|
| Self::Const(_, _, _, pos)
|
||||||
| Self::TryCatch(_, pos, _) => *pos,
|
| Self::TryCatch(_, pos, _) => *pos,
|
||||||
|
|
||||||
Self::Expr(x) => x.position(),
|
Self::Expr(x) => x.position(),
|
||||||
@ -921,8 +962,8 @@ impl Stmt {
|
|||||||
| Self::Do(_, _, _, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::Return(_, _, pos)
|
| Self::Return(_, _, pos)
|
||||||
| Self::Let(_, _, pos)
|
| Self::Let(_, _, _, pos)
|
||||||
| Self::Const(_, _, pos)
|
| Self::Const(_, _, _, pos)
|
||||||
| Self::TryCatch(_, pos, _) => *pos = new_pos,
|
| Self::TryCatch(_, pos, _) => *pos = new_pos,
|
||||||
|
|
||||||
Self::Expr(x) => {
|
Self::Expr(x) => {
|
||||||
@ -953,8 +994,8 @@ impl Stmt {
|
|||||||
// A No-op requires a semicolon in order to know it is an empty statement!
|
// A No-op requires a semicolon in order to know it is an empty statement!
|
||||||
Self::Noop(_) => false,
|
Self::Noop(_) => false,
|
||||||
|
|
||||||
Self::Let(_, _, _)
|
Self::Let(_, _, _, _)
|
||||||
| Self::Const(_, _, _)
|
| Self::Const(_, _, _, _)
|
||||||
| Self::Assignment(_, _)
|
| Self::Assignment(_, _)
|
||||||
| Self::Expr(_)
|
| Self::Expr(_)
|
||||||
| Self::Do(_, _, _, _)
|
| Self::Do(_, _, _, _)
|
||||||
@ -976,20 +1017,28 @@ impl Stmt {
|
|||||||
match self {
|
match self {
|
||||||
Self::Noop(_) => true,
|
Self::Noop(_) => true,
|
||||||
Self::Expr(expr) => expr.is_pure(),
|
Self::Expr(expr) => expr.is_pure(),
|
||||||
Self::If(condition, x, _) => condition.is_pure() && x.0.is_pure() && x.1.is_pure(),
|
Self::If(condition, x, _) => {
|
||||||
|
condition.is_pure()
|
||||||
|
&& x.0.statements.iter().all(Stmt::is_pure)
|
||||||
|
&& x.1.statements.iter().all(Stmt::is_pure)
|
||||||
|
}
|
||||||
Self::Switch(expr, x, _) => {
|
Self::Switch(expr, x, _) => {
|
||||||
expr.is_pure()
|
expr.is_pure()
|
||||||
&& x.0.values().all(Stmt::is_pure)
|
&& x.0.values().all(Stmt::is_pure)
|
||||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||||
}
|
}
|
||||||
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
||||||
condition.is_pure() && block.is_pure()
|
condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
|
||||||
}
|
}
|
||||||
Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
|
Self::For(iterable, x, _) => {
|
||||||
Self::Let(_, _, _) | Self::Const(_, _, _) | Self::Assignment(_, _) => false,
|
iterable.is_pure() && x.1.statements.iter().all(Stmt::is_pure)
|
||||||
|
}
|
||||||
|
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => 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::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,
|
||||||
Self::TryCatch(x, _, _) => x.0.is_pure() && x.2.is_pure(),
|
Self::TryCatch(x, _, _) => {
|
||||||
|
x.0.statements.iter().all(Stmt::is_pure) && x.2.statements.iter().all(Stmt::is_pure)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Self::Import(_, _, _) => false,
|
Self::Import(_, _, _) => false,
|
||||||
@ -1007,11 +1056,11 @@ impl Stmt {
|
|||||||
on_node(path);
|
on_node(path);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Let(e, _, _) | Self::Const(e, _, _) => e.walk(path, on_node),
|
Self::Let(e, _, _, _) | Self::Const(e, _, _, _) => e.walk(path, on_node),
|
||||||
Self::If(e, x, _) => {
|
Self::If(e, x, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
x.0.walk(path, on_node);
|
x.0.statements.iter().for_each(|s| s.walk(path, on_node));
|
||||||
x.1.walk(path, on_node);
|
x.1.statements.iter().for_each(|s| s.walk(path, on_node));
|
||||||
}
|
}
|
||||||
Self::Switch(e, x, _) => {
|
Self::Switch(e, x, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
@ -1022,11 +1071,11 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
|
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
s.walk(path, on_node);
|
s.statements.iter().for_each(|s| s.walk(path, on_node));
|
||||||
}
|
}
|
||||||
Self::For(e, x, _) => {
|
Self::For(e, x, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
x.1.walk(path, on_node);
|
x.1.statements.iter().for_each(|s| s.walk(path, on_node));
|
||||||
}
|
}
|
||||||
Self::Assignment(x, _) => {
|
Self::Assignment(x, _) => {
|
||||||
x.0.walk(path, on_node);
|
x.0.walk(path, on_node);
|
||||||
@ -1034,8 +1083,8 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
Self::Block(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
|
Self::Block(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
|
||||||
Self::TryCatch(x, _, _) => {
|
Self::TryCatch(x, _, _) => {
|
||||||
x.0.walk(path, on_node);
|
x.0.statements.iter().for_each(|s| s.walk(path, on_node));
|
||||||
x.2.walk(path, on_node);
|
x.2.statements.iter().for_each(|s| s.walk(path, on_node));
|
||||||
}
|
}
|
||||||
Self::Expr(e) | Self::Return(_, Some(e), _) => e.walk(path, on_node),
|
Self::Expr(e) | Self::Return(_, Some(e), _) => e.walk(path, on_node),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Main module defining the script evaluation [`Engine`].
|
//! Main module defining the script evaluation [`Engine`].
|
||||||
|
|
||||||
use crate::ast::{Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, Stmt};
|
use crate::ast::{Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock};
|
||||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||||
use crate::fn_native::{
|
use crate::fn_native::{
|
||||||
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
||||||
@ -2031,15 +2031,28 @@ impl Engine {
|
|||||||
|
|
||||||
// If statement
|
// If statement
|
||||||
Stmt::If(expr, x, _) => {
|
Stmt::If(expr, x, _) => {
|
||||||
let (if_block, else_block) = x.as_ref();
|
let (
|
||||||
|
StmtBlock {
|
||||||
|
statements: if_stmt,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
StmtBlock {
|
||||||
|
statements: else_stmt,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) = x.as_ref();
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
|
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
|
||||||
.and_then(|guard_val| {
|
.and_then(|guard_val| {
|
||||||
if guard_val {
|
if guard_val {
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level)
|
self.eval_stmt_block(
|
||||||
} else if !else_block.is_noop() {
|
scope, mods, state, lib, this_ptr, if_stmt, true, level,
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, else_block, level)
|
)
|
||||||
|
} else if !else_stmt.is_empty() {
|
||||||
|
self.eval_stmt_block(
|
||||||
|
scope, mods, state, lib, this_ptr, else_stmt, true, level,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
@ -2076,17 +2089,23 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
Stmt::While(expr, body, _) => loop {
|
Stmt::While(expr, body, _) => {
|
||||||
|
let body = &body.statements;
|
||||||
|
loop {
|
||||||
let condition = if !expr.is_unit() {
|
let condition = if !expr.is_unit() {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?
|
.map_err(|err| {
|
||||||
|
self.make_type_mismatch_err::<bool>(err, expr.position())
|
||||||
|
})?
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
if condition {
|
if condition {
|
||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
match self
|
||||||
|
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
|
||||||
|
{
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::LoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => (),
|
||||||
@ -2097,11 +2116,16 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Do loop
|
// Do loop
|
||||||
Stmt::Do(body, expr, is_while, _) => loop {
|
Stmt::Do(body, expr, is_while, _) => {
|
||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
let body = &body.statements;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
|
||||||
|
{
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::LoopBreak(false, _) => continue,
|
EvalAltResult::LoopBreak(false, _) => continue,
|
||||||
@ -2123,11 +2147,12 @@ impl Engine {
|
|||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(expr, x, _) => {
|
Stmt::For(expr, x, _) => {
|
||||||
let (name, stmt) = x.as_ref();
|
let (name, StmtBlock { statements, pos }) = x.as_ref();
|
||||||
let iter_obj = self
|
let iter_obj = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2176,9 +2201,11 @@ impl Engine {
|
|||||||
*loop_var = value;
|
*loop_var = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inc_operations(state, stmt.position())?;
|
self.inc_operations(state, *pos)?;
|
||||||
|
|
||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) {
|
match self.eval_stmt_block(
|
||||||
|
scope, mods, state, lib, this_ptr, statements, true, level,
|
||||||
|
) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::LoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => (),
|
||||||
@ -2204,10 +2231,20 @@ impl Engine {
|
|||||||
|
|
||||||
// Try/Catch statement
|
// Try/Catch statement
|
||||||
Stmt::TryCatch(x, _, _) => {
|
Stmt::TryCatch(x, _, _) => {
|
||||||
let (try_body, err_var, catch_body) = x.as_ref();
|
let (
|
||||||
|
StmtBlock {
|
||||||
|
statements: try_body,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
err_var,
|
||||||
|
StmtBlock {
|
||||||
|
statements: catch_body,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) = x.as_ref();
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt(scope, mods, state, lib, this_ptr, try_body, level)
|
.eval_stmt_block(scope, mods, state, lib, this_ptr, try_body, true, level)
|
||||||
.map(|_| Dynamic::UNIT);
|
.map(|_| Dynamic::UNIT);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@ -2266,8 +2303,9 @@ impl Engine {
|
|||||||
scope.push(unsafe_cast_var_name_to_lifetime(&name), err_value);
|
scope.push(unsafe_cast_var_name_to_lifetime(&name), err_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result =
|
let result = self.eval_stmt_block(
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, catch_body, level);
|
scope, mods, state, lib, this_ptr, catch_body, true, level,
|
||||||
|
);
|
||||||
|
|
||||||
state.scope_level -= 1;
|
state.scope_level -= 1;
|
||||||
scope.rewind(orig_scope_len);
|
scope.rewind(orig_scope_len);
|
||||||
@ -2314,11 +2352,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let/const statement
|
// Let/const statement
|
||||||
Stmt::Let(expr, Ident { name, public, .. }, _)
|
Stmt::Let(expr, Ident { name, .. }, export, _)
|
||||||
| Stmt::Const(expr, Ident { name, public, .. }, _) => {
|
| Stmt::Const(expr, Ident { name, .. }, export, _) => {
|
||||||
let entry_type = match stmt {
|
let entry_type = match stmt {
|
||||||
Stmt::Let(_, _, _) => AccessMode::ReadWrite,
|
Stmt::Let(_, _, _, _) => AccessMode::ReadWrite,
|
||||||
Stmt::Const(_, _, _) => AccessMode::ReadOnly,
|
Stmt::Const(_, _, _, _) => AccessMode::ReadOnly,
|
||||||
_ => unreachable!("should be Stmt::Let or Stmt::Const, but gets {:?}", stmt),
|
_ => unreachable!("should be Stmt::Let or Stmt::Const, but gets {:?}", stmt),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2329,9 +2367,9 @@ impl Engine {
|
|||||||
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
||||||
(
|
(
|
||||||
name.to_string().into(),
|
name.to_string().into(),
|
||||||
if *public { Some(name.clone()) } else { None },
|
if *export { Some(name.clone()) } else { None },
|
||||||
)
|
)
|
||||||
} else if *public {
|
} else if *export {
|
||||||
unreachable!("exported variable not on global level");
|
unreachable!("exported variable not on global level");
|
||||||
} else {
|
} else {
|
||||||
(unsafe_cast_var_name_to_lifetime(name).into(), None)
|
(unsafe_cast_var_name_to_lifetime(name).into(), None)
|
||||||
@ -2348,7 +2386,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Import statement
|
// Import statement
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(expr, Ident { name, public, .. }, _pos) => {
|
Stmt::Import(expr, export, _pos) => {
|
||||||
// Guard against too many modules
|
// Guard against too many modules
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if state.modules >= self.max_modules() {
|
if state.modules >= self.max_modules() {
|
||||||
@ -2375,14 +2413,14 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|| self.module_resolver.resolve(self, &path, expr_pos))?;
|
.unwrap_or_else(|| self.module_resolver.resolve(self, &path, expr_pos))?;
|
||||||
|
|
||||||
if *public {
|
if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
|
||||||
if !module.is_indexed() {
|
if !module.is_indexed() {
|
||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||||
module.build_index();
|
module.build_index();
|
||||||
mods.push(name.clone(), module);
|
mods.push(name, module);
|
||||||
} else {
|
} else {
|
||||||
mods.push(name.clone(), module);
|
mods.push(name, module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
221
src/optimize.rs
221
src/optimize.rs
@ -171,8 +171,11 @@ fn optimize_stmt_block(
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
preserve_result: bool,
|
preserve_result: bool,
|
||||||
count_promote_as_dirty: bool,
|
) -> Vec<Stmt> {
|
||||||
) -> Stmt {
|
if statements.is_empty() {
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
|
||||||
let orig_len = statements.len(); // Original number of statements in the block, for change detection
|
let orig_len = statements.len(); // Original number of statements in the block, for change detection
|
||||||
let orig_constants_len = state.variables.len(); // Original number of constants in the state, for restore later
|
let orig_constants_len = state.variables.len(); // Original number of constants in the state, for restore later
|
||||||
let orig_propagate_constants = state.propagate_constants;
|
let orig_propagate_constants = state.propagate_constants;
|
||||||
@ -181,7 +184,7 @@ fn optimize_stmt_block(
|
|||||||
statements.iter_mut().for_each(|stmt| {
|
statements.iter_mut().for_each(|stmt| {
|
||||||
match stmt {
|
match stmt {
|
||||||
// Add constant literals into the state
|
// Add constant literals into the state
|
||||||
Stmt::Const(value_expr, Ident { name, .. }, _) => {
|
Stmt::Const(value_expr, Ident { name, .. }, _, _) => {
|
||||||
optimize_expr(value_expr, state);
|
optimize_expr(value_expr, state);
|
||||||
|
|
||||||
if value_expr.is_constant() {
|
if value_expr.is_constant() {
|
||||||
@ -189,7 +192,7 @@ fn optimize_stmt_block(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add variables into the state
|
// Add variables into the state
|
||||||
Stmt::Let(value_expr, Ident { name, pos, .. }, _) => {
|
Stmt::Let(value_expr, Ident { name, pos, .. }, _, _) => {
|
||||||
optimize_expr(value_expr, state);
|
optimize_expr(value_expr, state);
|
||||||
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos));
|
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos));
|
||||||
}
|
}
|
||||||
@ -218,7 +221,7 @@ fn optimize_stmt_block(
|
|||||||
|
|
||||||
while let Some(expr) = statements.pop() {
|
while let Some(expr) = statements.pop() {
|
||||||
match expr {
|
match expr {
|
||||||
Stmt::Let(expr, _, _) | Stmt::Const(expr, _, _) => removed = expr.is_pure(),
|
Stmt::Let(expr, _, _, _) | Stmt::Const(expr, _, _, _) => removed = expr.is_pure(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(expr, _, _) => removed = expr.is_pure(),
|
Stmt::Import(expr, _, _) => removed = expr.is_pure(),
|
||||||
_ => {
|
_ => {
|
||||||
@ -238,7 +241,7 @@ fn optimize_stmt_block(
|
|||||||
statements
|
statements
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, stmt)| optimize_stmt(stmt, state, i == num_statements));
|
.for_each(|(i, stmt)| optimize_stmt(stmt, state, i >= num_statements - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove everything following the the first return/throw
|
// Remove everything following the the first return/throw
|
||||||
@ -267,28 +270,7 @@ fn optimize_stmt_block(
|
|||||||
|
|
||||||
state.propagate_constants = orig_propagate_constants;
|
state.propagate_constants = orig_propagate_constants;
|
||||||
|
|
||||||
match &statements[..] {
|
statements
|
||||||
// No statements in block - change to No-op
|
|
||||||
[] => {
|
|
||||||
state.set_dirty();
|
|
||||||
Stmt::Noop(pos)
|
|
||||||
}
|
|
||||||
// Only one let statement - leave it alone
|
|
||||||
[x] if matches!(x, Stmt::Let(_, _, _)) => Stmt::Block(statements, pos),
|
|
||||||
// Only one const statement - leave it alone
|
|
||||||
[x] if matches!(x, Stmt::Const(_, _, _)) => Stmt::Block(statements, pos),
|
|
||||||
// Only one import statement - leave it alone
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
[x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(statements, pos),
|
|
||||||
// Only one statement - promote
|
|
||||||
[_] => {
|
|
||||||
if count_promote_as_dirty {
|
|
||||||
state.set_dirty();
|
|
||||||
}
|
|
||||||
statements.remove(0)
|
|
||||||
}
|
|
||||||
_ => Stmt::Block(statements, pos),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize a [statement][Stmt].
|
/// Optimize a [statement][Stmt].
|
||||||
@ -303,18 +285,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// if false { if_block } -> Noop
|
// if expr {}
|
||||||
Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_noop() => {
|
Stmt::If(condition, x, _) if x.0.is_empty() && x.1.is_empty() => {
|
||||||
state.set_dirty();
|
|
||||||
*stmt = Stmt::Noop(*pos);
|
|
||||||
}
|
|
||||||
// if true { if_block } -> if_block
|
|
||||||
Stmt::If(Expr::BoolConstant(true, _), x, _) if x.1.is_noop() => {
|
|
||||||
*stmt = mem::take(&mut x.0);
|
|
||||||
optimize_stmt(stmt, state, true);
|
|
||||||
}
|
|
||||||
// if expr { Noop }
|
|
||||||
Stmt::If(condition, x, _) if x.1.is_noop() && x.0.is_noop() => {
|
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
let pos = condition.position();
|
let pos = condition.position();
|
||||||
@ -323,32 +295,60 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
|
|
||||||
*stmt = if preserve_result {
|
*stmt = if preserve_result {
|
||||||
// -> { expr, Noop }
|
// -> { expr, Noop }
|
||||||
Stmt::Block(vec![Stmt::Expr(expr), mem::take(&mut x.0)], pos)
|
Stmt::Block(vec![Stmt::Expr(expr), Stmt::Noop(pos)], pos)
|
||||||
} else {
|
} else {
|
||||||
// -> expr
|
// -> expr
|
||||||
Stmt::Expr(expr)
|
Stmt::Expr(expr)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// if expr { if_block }
|
// if false { if_block } -> Noop
|
||||||
Stmt::If(condition, x, _) if x.1.is_noop() => {
|
Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_empty() => {
|
||||||
optimize_expr(condition, state);
|
state.set_dirty();
|
||||||
optimize_stmt(&mut x.0, state, true);
|
*stmt = Stmt::Noop(*pos);
|
||||||
}
|
}
|
||||||
// if false { if_block } else { else_block } -> else_block
|
// if false { if_block } else { else_block } -> else_block
|
||||||
Stmt::If(Expr::BoolConstant(false, _), x, _) if !x.1.is_noop() => {
|
Stmt::If(Expr::BoolConstant(false, _), x, _) => {
|
||||||
*stmt = mem::take(&mut x.1);
|
state.set_dirty();
|
||||||
optimize_stmt(stmt, state, true);
|
*stmt = match optimize_stmt_block(
|
||||||
|
mem::take(&mut x.1.statements).into_vec(),
|
||||||
|
x.1.pos,
|
||||||
|
state,
|
||||||
|
preserve_result,
|
||||||
|
) {
|
||||||
|
statements if statements.is_empty() => Stmt::Noop(x.1.pos),
|
||||||
|
statements => Stmt::Block(statements, x.1.pos),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if true { if_block } else { else_block } -> if_block
|
// if true { if_block } else { else_block } -> if_block
|
||||||
Stmt::If(Expr::BoolConstant(true, _), x, _) => {
|
Stmt::If(Expr::BoolConstant(true, _), x, _) => {
|
||||||
*stmt = mem::take(&mut x.0);
|
state.set_dirty();
|
||||||
optimize_stmt(stmt, state, true);
|
*stmt = match optimize_stmt_block(
|
||||||
|
mem::take(&mut x.0.statements).into_vec(),
|
||||||
|
x.0.pos,
|
||||||
|
state,
|
||||||
|
preserve_result,
|
||||||
|
) {
|
||||||
|
statements if statements.is_empty() => Stmt::Noop(x.0.pos),
|
||||||
|
statements => Stmt::Block(statements, x.0.pos),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 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);
|
optimize_expr(condition, state);
|
||||||
optimize_stmt(&mut x.0, state, true);
|
x.0.statements = optimize_stmt_block(
|
||||||
optimize_stmt(&mut x.1, state, true);
|
mem::take(&mut x.0.statements).into_vec(),
|
||||||
|
x.0.pos,
|
||||||
|
state,
|
||||||
|
preserve_result,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
x.1.statements = optimize_stmt_block(
|
||||||
|
mem::take(&mut x.1.statements).into_vec(),
|
||||||
|
x.1.pos,
|
||||||
|
state,
|
||||||
|
preserve_result,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch const { ... }
|
// switch const { ... }
|
||||||
@ -376,9 +376,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
Stmt::Switch(expr, x, _) => {
|
Stmt::Switch(expr, x, _) => {
|
||||||
optimize_expr(expr, state);
|
optimize_expr(expr, state);
|
||||||
x.0.values_mut()
|
x.0.values_mut()
|
||||||
.for_each(|stmt| optimize_stmt(stmt, state, true));
|
.for_each(|stmt| optimize_stmt(stmt, state, preserve_result));
|
||||||
if let Some(def_stmt) = x.1.as_mut() {
|
if let Some(def_stmt) = x.1.as_mut() {
|
||||||
optimize_stmt(def_stmt, state, true);
|
optimize_stmt(def_stmt, state, preserve_result);
|
||||||
|
|
||||||
match def_stmt {
|
match def_stmt {
|
||||||
Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None,
|
Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None,
|
||||||
@ -394,10 +394,18 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
}
|
}
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
Stmt::While(condition, block, _) => {
|
Stmt::While(condition, block, _) => {
|
||||||
optimize_stmt(block, state, false);
|
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
|
|
||||||
match **block {
|
block.statements = optimize_stmt_block(
|
||||||
|
mem::take(&mut block.statements).into_vec(),
|
||||||
|
block.pos,
|
||||||
|
state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
|
||||||
|
if block.len() == 1 {
|
||||||
|
match block.statements[0] {
|
||||||
// while expr { break; } -> { expr; }
|
// while expr { break; } -> { expr; }
|
||||||
Stmt::Break(pos) => {
|
Stmt::Break(pos) => {
|
||||||
// Only a single break statement - turn into running the guard expression once
|
// Only a single break statement - turn into running the guard expression once
|
||||||
@ -415,49 +423,93 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// do { block } while false | do { block } until true -> { block }
|
// do { block } while false | do { block } until true -> { block }
|
||||||
Stmt::Do(block, Expr::BoolConstant(true, _), false, _)
|
Stmt::Do(block, Expr::BoolConstant(true, _), false, _)
|
||||||
| Stmt::Do(block, Expr::BoolConstant(false, _), true, _) => {
|
| Stmt::Do(block, Expr::BoolConstant(false, _), true, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
optimize_stmt(block.as_mut(), state, false);
|
*stmt = Stmt::Block(
|
||||||
*stmt = mem::take(block.as_mut());
|
optimize_stmt_block(
|
||||||
|
mem::take(&mut block.statements).into_vec(),
|
||||||
|
block.pos,
|
||||||
|
state,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
block.pos,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// do { block } while|until expr
|
// do { block } while|until expr
|
||||||
Stmt::Do(block, condition, _, _) => {
|
Stmt::Do(block, condition, _, _) => {
|
||||||
optimize_stmt(block.as_mut(), state, false);
|
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
|
block.statements = optimize_stmt_block(
|
||||||
|
mem::take(&mut block.statements).into_vec(),
|
||||||
|
block.pos,
|
||||||
|
state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(iterable, x, _) => {
|
Stmt::For(iterable, x, _) => {
|
||||||
optimize_expr(iterable, state);
|
optimize_expr(iterable, state);
|
||||||
optimize_stmt(&mut x.1, state, false);
|
x.1.statements = optimize_stmt_block(
|
||||||
|
mem::take(&mut x.1.statements).into_vec(),
|
||||||
|
x.1.pos,
|
||||||
|
state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Let(expr, _, _) => optimize_expr(expr, state),
|
Stmt::Let(expr, _, _, _) => optimize_expr(expr, state),
|
||||||
// import expr as var;
|
// import expr as var;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(expr, _, _) => optimize_expr(expr, state),
|
Stmt::Import(expr, _, _) => optimize_expr(expr, state),
|
||||||
// { block }
|
// { block }
|
||||||
Stmt::Block(statements, pos) => {
|
Stmt::Block(statements, pos) => {
|
||||||
*stmt = optimize_stmt_block(mem::take(statements), *pos, state, preserve_result, true);
|
*stmt = match optimize_stmt_block(mem::take(statements), *pos, state, preserve_result) {
|
||||||
|
statements if statements.is_empty() => {
|
||||||
|
state.set_dirty();
|
||||||
|
Stmt::Noop(*pos)
|
||||||
}
|
}
|
||||||
// try { block } catch ( var ) { block }
|
// Only one statement - promote
|
||||||
Stmt::TryCatch(x, _, _) if x.0.is_pure() => {
|
mut statements if statements.len() == 1 => {
|
||||||
|
state.set_dirty();
|
||||||
|
statements.pop().unwrap()
|
||||||
|
}
|
||||||
|
statements => Stmt::Block(statements, *pos),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// try { pure block } catch ( var ) { block }
|
||||||
|
Stmt::TryCatch(x, _, _) if x.0.statements.iter().all(Stmt::is_pure) => {
|
||||||
// If try block is pure, there will never be any exceptions
|
// If try block is pure, there will never be any exceptions
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = x.0.position();
|
*stmt = Stmt::Block(
|
||||||
optimize_stmt(&mut x.0, state, preserve_result);
|
optimize_stmt_block(
|
||||||
let mut statements = match mem::take(&mut x.0) {
|
mem::take(&mut x.0.statements).into_vec(),
|
||||||
Stmt::Block(statements, _) => statements,
|
x.0.pos,
|
||||||
stmt => vec![stmt],
|
state,
|
||||||
};
|
false,
|
||||||
statements.push(Stmt::Noop(pos));
|
),
|
||||||
*stmt = Stmt::Block(statements, pos);
|
x.0.pos,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// try { block } catch ( var ) { block }
|
// try { block } catch ( var ) { block }
|
||||||
Stmt::TryCatch(x, _, _) => {
|
Stmt::TryCatch(x, _, _) => {
|
||||||
optimize_stmt(&mut x.0, state, false);
|
x.0.statements = optimize_stmt_block(
|
||||||
optimize_stmt(&mut x.2, state, false);
|
mem::take(&mut x.0.statements).into_vec(),
|
||||||
|
x.0.pos,
|
||||||
|
state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
x.2.statements = optimize_stmt_block(
|
||||||
|
mem::take(&mut x.2.statements).into_vec(),
|
||||||
|
x.2.pos,
|
||||||
|
state,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
// {}
|
// {}
|
||||||
Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => {
|
Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => {
|
||||||
@ -492,17 +544,10 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
// {}
|
// {}
|
||||||
Expr::Stmt(x, pos) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(*pos) }
|
Expr::Stmt(x, pos) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(*pos) }
|
||||||
// { 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, pos) => match optimize_stmt_block(mem::take(x).into_vec(), *pos, state, true, false) {
|
Expr::Stmt(x, pos) => {
|
||||||
// {}
|
let statements = optimize_stmt_block(mem::take(x).into_vec(), *pos, state, true);
|
||||||
Stmt::Noop(_) => { state.set_dirty(); *expr = Expr::Unit(*pos); }
|
*expr = Expr::Stmt(Box::new(statements.into()), *pos);
|
||||||
// { stmt, .. }
|
|
||||||
Stmt::Block(statements, _) => *x = Box::new(statements.into()),
|
|
||||||
// { expr }
|
|
||||||
Stmt::Expr(inner) => { state.set_dirty(); *expr = inner; }
|
|
||||||
// { stmt }
|
|
||||||
stmt => x.push(stmt),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||||
@ -766,7 +811,7 @@ fn optimize_top_level(
|
|||||||
|
|
||||||
statements.iter_mut().enumerate().for_each(|(i, stmt)| {
|
statements.iter_mut().enumerate().for_each(|(i, stmt)| {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Const(value_expr, Ident { name, .. }, _) => {
|
Stmt::Const(value_expr, Ident { name, .. }, _, _) => {
|
||||||
// Load constants
|
// Load constants
|
||||||
optimize_expr(value_expr, &mut state);
|
optimize_expr(value_expr, &mut state);
|
||||||
|
|
||||||
@ -774,7 +819,7 @@ fn optimize_top_level(
|
|||||||
state.push_var(name, AccessMode::ReadOnly, value_expr.clone());
|
state.push_var(name, AccessMode::ReadOnly, value_expr.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Let(value_expr, Ident { name, pos, .. }, _) => {
|
Stmt::Let(value_expr, Ident { name, pos, .. }, _, _) => {
|
||||||
optimize_expr(value_expr, &mut state);
|
optimize_expr(value_expr, &mut state);
|
||||||
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos));
|
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos));
|
||||||
}
|
}
|
||||||
@ -782,10 +827,10 @@ fn optimize_top_level(
|
|||||||
// Keep all variable declarations at this level
|
// Keep all variable declarations at this level
|
||||||
// and always keep the last return value
|
// and always keep the last return value
|
||||||
let keep = match stmt {
|
let keep = match stmt {
|
||||||
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => true,
|
Stmt::Let(_, _, _, _) | Stmt::Const(_, _, _, _) => true,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(_, _, _) => true,
|
Stmt::Import(_, _, _) => true,
|
||||||
_ => i == num_statements - 1,
|
_ => i >= num_statements - 1,
|
||||||
};
|
};
|
||||||
optimize_stmt(stmt, &mut state, keep);
|
optimize_stmt(stmt, &mut state, keep);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, ScriptFnDef,
|
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, ScriptFnDef,
|
||||||
Stmt,
|
Stmt, StmtBlock,
|
||||||
};
|
};
|
||||||
use crate::dynamic::{AccessMode, Union};
|
use crate::dynamic::{AccessMode, Union};
|
||||||
use crate::engine::{KEYWORD_THIS, OP_CONTAINS};
|
use crate::engine::{KEYWORD_THIS, OP_CONTAINS};
|
||||||
@ -755,8 +755,7 @@ fn parse_map_literal(
|
|||||||
|
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
let name = state.get_interned_string(name);
|
let name = state.get_interned_string(name);
|
||||||
let public = false;
|
map.push((Ident { name, pos }, expr));
|
||||||
map.push((Ident { name, pos, public }, expr));
|
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
@ -1034,7 +1033,6 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
public: false,
|
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1049,7 +1047,6 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
public: false,
|
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1059,7 +1056,6 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
public: false,
|
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((index, None, var_name_def)))
|
Expr::Variable(Box::new((index, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1079,7 +1075,6 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
public: false,
|
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1088,7 +1083,6 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
public: false,
|
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1182,7 +1176,6 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(id2),
|
name: state.get_interned_string(id2),
|
||||||
pos: pos2,
|
pos: pos2,
|
||||||
public: false,
|
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((index, namespace, var_name_def)))
|
Expr::Variable(Box::new((index, namespace, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1853,8 +1846,7 @@ fn parse_custom_syntax(
|
|||||||
let name = state.get_interned_string(s);
|
let name = state.get_interned_string(s);
|
||||||
segments.push(name.clone());
|
segments.push(name.clone());
|
||||||
tokens.push(state.get_interned_string(MARKER_IDENT));
|
tokens.push(state.get_interned_string(MARKER_IDENT));
|
||||||
let public = false;
|
let var_name_def = Ident { name, pos };
|
||||||
let var_name_def = Ident { name, pos, public };
|
|
||||||
keywords.push(Expr::Variable(Box::new((None, None, var_name_def))));
|
keywords.push(Expr::Variable(Box::new((None, None, var_name_def))));
|
||||||
}
|
}
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
@ -2014,9 +2006,19 @@ fn parse_if(
|
|||||||
Stmt::Noop(Position::NONE)
|
Stmt::Noop(Position::NONE)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let else_body = match else_body {
|
||||||
|
Stmt::If(_, _, pos) => {
|
||||||
|
let mut statements: StaticVec<_> = Default::default();
|
||||||
|
statements.push(else_body);
|
||||||
|
StmtBlock { statements, pos }
|
||||||
|
}
|
||||||
|
Stmt::Block(_, _) | Stmt::Noop(_) => else_body.into(),
|
||||||
|
_ => unreachable!("should either be if or a block, not {:?}", else_body),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Stmt::If(
|
Ok(Stmt::If(
|
||||||
guard,
|
guard,
|
||||||
Box::new((if_body, else_body)),
|
Box::new((if_body.into(), else_body)),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -2045,9 +2047,9 @@ fn parse_while_loop(
|
|||||||
|
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
settings.is_breakable = true;
|
settings.is_breakable = true;
|
||||||
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
let body = parse_block(input, state, lib, settings.level_up())?;
|
||||||
|
|
||||||
Ok(Stmt::While(guard, body, settings.pos))
|
Ok(Stmt::While(guard, Box::new(body.into()), settings.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a do loop.
|
/// Parse a do loop.
|
||||||
@ -2065,7 +2067,7 @@ fn parse_do(
|
|||||||
|
|
||||||
// do { body } [while|until] guard
|
// do { body } [while|until] guard
|
||||||
settings.is_breakable = true;
|
settings.is_breakable = true;
|
||||||
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
let body = parse_block(input, state, lib, settings.level_up())?;
|
||||||
|
|
||||||
let is_while = match input.next().unwrap() {
|
let is_while = match input.next().unwrap() {
|
||||||
(Token::While, _) => true,
|
(Token::While, _) => true,
|
||||||
@ -2083,7 +2085,12 @@ fn parse_do(
|
|||||||
let guard = parse_expr(input, state, lib, settings.level_up())?;
|
let guard = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
|
|
||||||
Ok(Stmt::Do(body, guard, is_while, settings.pos))
|
Ok(Stmt::Do(
|
||||||
|
Box::new(body.into()),
|
||||||
|
guard,
|
||||||
|
is_while,
|
||||||
|
settings.pos,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a for loop.
|
/// Parse a for loop.
|
||||||
@ -2138,7 +2145,7 @@ fn parse_for(
|
|||||||
|
|
||||||
state.stack.truncate(prev_stack_len);
|
state.stack.truncate(prev_stack_len);
|
||||||
|
|
||||||
Ok(Stmt::For(expr, Box::new((name, body)), settings.pos))
|
Ok(Stmt::For(expr, Box::new((name, body.into())), settings.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a variable definition statement.
|
/// Parse a variable definition statement.
|
||||||
@ -2170,7 +2177,6 @@ fn parse_let(
|
|||||||
let var_def = Ident {
|
let var_def = Ident {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
pos,
|
pos,
|
||||||
public: export,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// let name = ...
|
// let name = ...
|
||||||
@ -2185,9 +2191,9 @@ fn parse_let(
|
|||||||
|
|
||||||
match var_type {
|
match var_type {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
AccessMode::ReadWrite => Ok(Stmt::Let(expr, var_def, settings.pos)),
|
AccessMode::ReadWrite => Ok(Stmt::Let(expr, var_def, export, settings.pos)),
|
||||||
// const name = { expr:constant }
|
// const name = { expr:constant }
|
||||||
AccessMode::ReadOnly => Ok(Stmt::Const(expr, var_def, settings.pos)),
|
AccessMode::ReadOnly => Ok(Stmt::Const(expr, var_def, export, settings.pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2210,15 +2216,7 @@ fn parse_import(
|
|||||||
|
|
||||||
// import expr as ...
|
// import expr as ...
|
||||||
if !match_token(input, Token::As).0 {
|
if !match_token(input, Token::As).0 {
|
||||||
return Ok(Stmt::Import(
|
return Ok(Stmt::Import(expr, None, settings.pos));
|
||||||
expr,
|
|
||||||
Ident {
|
|
||||||
name: "".into(),
|
|
||||||
pos: Position::NONE,
|
|
||||||
public: false,
|
|
||||||
},
|
|
||||||
settings.pos,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// import expr as name ...
|
// import expr as name ...
|
||||||
@ -2236,11 +2234,10 @@ fn parse_import(
|
|||||||
|
|
||||||
Ok(Stmt::Import(
|
Ok(Stmt::Import(
|
||||||
expr,
|
expr,
|
||||||
Ident {
|
Some(Ident {
|
||||||
name,
|
name,
|
||||||
pos: name_pos,
|
pos: name_pos,
|
||||||
public: true,
|
}),
|
||||||
},
|
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -2291,7 +2288,6 @@ fn parse_export(
|
|||||||
(Token::Identifier(s), pos) => Some(Ident {
|
(Token::Identifier(s), pos) => Some(Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos,
|
pos,
|
||||||
public: false,
|
|
||||||
}),
|
}),
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
return Err(PERR::Reserved(s).into_err(pos));
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
@ -2307,7 +2303,6 @@ fn parse_export(
|
|||||||
Ident {
|
Ident {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
pos: id_pos,
|
pos: id_pos,
|
||||||
public: false,
|
|
||||||
},
|
},
|
||||||
rename,
|
rename,
|
||||||
));
|
));
|
||||||
@ -2646,7 +2641,6 @@ fn parse_try_catch(
|
|||||||
(Token::Identifier(s), pos) => Ident {
|
(Token::Identifier(s), pos) => Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos,
|
pos,
|
||||||
public: false,
|
|
||||||
},
|
},
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -2670,7 +2664,7 @@ fn parse_try_catch(
|
|||||||
let catch_body = parse_block(input, state, lib, settings.level_up())?;
|
let catch_body = parse_block(input, state, lib, settings.level_up())?;
|
||||||
|
|
||||||
Ok(Stmt::TryCatch(
|
Ok(Stmt::TryCatch(
|
||||||
Box::new((body, var_def, catch_body)),
|
Box::new((body.into(), var_def, catch_body.into())),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
catch_pos,
|
catch_pos,
|
||||||
))
|
))
|
||||||
@ -2879,7 +2873,6 @@ fn parse_anon_fn(
|
|||||||
.map(|(name, &pos)| Ident {
|
.map(|(name, &pos)| Ident {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
pos,
|
pos,
|
||||||
public: false,
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -54,9 +54,8 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.set_optimization_level(OptimizationLevel::Simple);
|
engine.set_optimization_level(OptimizationLevel::Simple);
|
||||||
|
|
||||||
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
||||||
println!("{:?}", ast);
|
|
||||||
|
|
||||||
assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(BoolConstant(false, 1:20), Ident("DECISION" @ 1:9), 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)], functions: Module("#));
|
assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(BoolConstant(false, 1:20), Ident("DECISION" @ 1:9), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)], functions: Module("#));
|
||||||
|
|
||||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user