Encapsulate into FlowControl type.
This commit is contained in:
parent
80ccd75514
commit
cb09393dba
@ -22,8 +22,8 @@ pub use namespace_none::Namespace;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use script_fn::{ScriptFnDef, ScriptFnMetadata};
|
||||
pub use stmt::{
|
||||
CaseBlocksList, ConditionalExpr, OpAssignment, RangeCase, Stmt, StmtBlock, StmtBlockContainer,
|
||||
SwitchCasesCollection, TryCatchBlock,
|
||||
CaseBlocksList, ConditionalExpr, FlowControl, OpAssignment, RangeCase, Stmt, StmtBlock,
|
||||
StmtBlockContainer, SwitchCasesCollection,
|
||||
};
|
||||
|
||||
/// _(internals)_ Placeholder for a script-defined function.
|
||||
|
@ -292,18 +292,6 @@ pub struct SwitchCasesCollection {
|
||||
pub def_case: Option<usize>,
|
||||
}
|
||||
|
||||
/// _(internals)_ A `try-catch` block.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct TryCatchBlock {
|
||||
/// `try` block.
|
||||
pub try_block: StmtBlock,
|
||||
/// `catch` variable, if any.
|
||||
pub catch_var: Ident,
|
||||
/// `catch` block.
|
||||
pub catch_block: StmtBlock,
|
||||
}
|
||||
|
||||
/// Number of items to keep inline for [`StmtBlockContainer`].
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
const STMT_BLOCK_INLINE_SIZE: usize = 8;
|
||||
@ -532,6 +520,22 @@ impl Extend<Stmt> for StmtBlock {
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A flow control block containing:
|
||||
/// * an expression,
|
||||
/// * a statements body
|
||||
/// * an alternate statements body
|
||||
///
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct FlowControl {
|
||||
/// Flow control expression.
|
||||
pub expr: Expr,
|
||||
/// Main body.
|
||||
pub body: StmtBlock,
|
||||
/// Branch body.
|
||||
pub branch: StmtBlock,
|
||||
}
|
||||
|
||||
/// _(internals)_ A statement.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
@ -540,7 +544,7 @@ pub enum Stmt {
|
||||
/// No-op.
|
||||
Noop(Position),
|
||||
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||
If(Box<(Expr, StmtBlock, StmtBlock)>, Position),
|
||||
If(Box<FlowControl>, Position),
|
||||
/// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}`
|
||||
///
|
||||
/// ### Data Structure
|
||||
@ -552,16 +556,16 @@ pub enum Stmt {
|
||||
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
|
||||
///
|
||||
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement.
|
||||
While(Box<(Expr, StmtBlock)>, Position),
|
||||
While(Box<FlowControl>, Position),
|
||||
/// `do` `{` stmt `}` `while`|`until` expr
|
||||
///
|
||||
/// ### Flags
|
||||
///
|
||||
/// * [`NONE`][ASTFlags::NONE] = `while`
|
||||
/// * [`NEGATED`][ASTFlags::NEGATED] = `until`
|
||||
Do(Box<(Expr, StmtBlock)>, ASTFlags, Position),
|
||||
Do(Box<FlowControl>, ASTFlags, Position),
|
||||
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
|
||||
For(Box<(Ident, Ident, Expr, StmtBlock)>, Position),
|
||||
For(Box<(Ident, Ident, FlowControl)>, Position),
|
||||
/// \[`export`\] `let`|`const` id `=` expr
|
||||
///
|
||||
/// ### Flags
|
||||
@ -579,7 +583,7 @@ pub enum Stmt {
|
||||
/// `{` stmt`;` ... `}`
|
||||
Block(Box<StmtBlock>),
|
||||
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
||||
TryCatch(Box<TryCatchBlock>, Position),
|
||||
TryCatch(Box<FlowControl>, Position),
|
||||
/// [expression][Expr]
|
||||
Expr(Box<Expr>),
|
||||
/// `continue`/`break` expr
|
||||
@ -815,7 +819,9 @@ impl Stmt {
|
||||
Self::Noop(..) => true,
|
||||
Self::Expr(expr) => expr.is_pure(),
|
||||
Self::If(x, ..) => {
|
||||
x.0.is_pure() && x.1.iter().all(Self::is_pure) && x.2.iter().all(Self::is_pure)
|
||||
x.expr.is_pure()
|
||||
&& x.body.iter().all(Self::is_pure)
|
||||
&& x.branch.iter().all(Self::is_pure)
|
||||
}
|
||||
Self::Switch(x, ..) => {
|
||||
let (expr, sw) = &**x;
|
||||
@ -833,10 +839,10 @@ impl Stmt {
|
||||
}
|
||||
|
||||
// Loops that exit can be pure because it can never be infinite.
|
||||
Self::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => true,
|
||||
Self::Do(x, options, ..) if matches!(x.0, Expr::BoolConstant(..)) => match x.0 {
|
||||
Self::While(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => true,
|
||||
Self::Do(x, options, ..) if matches!(x.expr, Expr::BoolConstant(..)) => match x.expr {
|
||||
Expr::BoolConstant(cond, ..) if cond == options.contains(ASTFlags::NEGATED) => {
|
||||
x.1.iter().all(Self::is_pure)
|
||||
x.body.iter().all(Self::is_pure)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
@ -846,13 +852,15 @@ impl Stmt {
|
||||
|
||||
// For loops can be pure because if the iterable is pure, it is finite,
|
||||
// so infinite loops can never occur.
|
||||
Self::For(x, ..) => x.2.is_pure() && x.3.iter().all(Self::is_pure),
|
||||
Self::For(x, ..) => x.2.expr.is_pure() && x.2.body.iter().all(Self::is_pure),
|
||||
|
||||
Self::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
|
||||
Self::Block(block, ..) => block.iter().all(Self::is_pure),
|
||||
Self::BreakLoop(..) | Self::Return(..) => false,
|
||||
Self::TryCatch(x, ..) => {
|
||||
x.try_block.iter().all(Self::is_pure) && x.catch_block.iter().all(Self::is_pure)
|
||||
x.expr.is_pure()
|
||||
&& x.body.iter().all(Self::is_pure)
|
||||
&& x.branch.iter().all(Self::is_pure)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -947,15 +955,15 @@ impl Stmt {
|
||||
}
|
||||
}
|
||||
Self::If(x, ..) => {
|
||||
if !x.0.walk(path, on_node) {
|
||||
if !x.expr.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
for s in &x.1 {
|
||||
for s in &x.body {
|
||||
if !s.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for s in &x.2 {
|
||||
for s in &x.branch {
|
||||
if !s.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
@ -996,20 +1004,20 @@ impl Stmt {
|
||||
}
|
||||
}
|
||||
Self::While(x, ..) | Self::Do(x, ..) => {
|
||||
if !x.0.walk(path, on_node) {
|
||||
if !x.expr.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
for s in x.1.statements() {
|
||||
for s in x.body.statements() {
|
||||
if !s.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::For(x, ..) => {
|
||||
if !x.2.walk(path, on_node) {
|
||||
if !x.2.expr.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
for s in &x.3 {
|
||||
for s in &x.2.body {
|
||||
if !s.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
@ -1038,12 +1046,12 @@ impl Stmt {
|
||||
}
|
||||
}
|
||||
Self::TryCatch(x, ..) => {
|
||||
for s in &x.try_block {
|
||||
for s in &x.body {
|
||||
if !s.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for s in &x.catch_block {
|
||||
for s in &x.branch {
|
||||
if !s.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||
use crate::api::events::VarDefInfo;
|
||||
use crate::ast::{
|
||||
ASTFlags, BinaryExpr, Expr, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock,
|
||||
ASTFlags, BinaryExpr, Expr, FlowControl, OpAssignment, Stmt, SwitchCasesCollection,
|
||||
};
|
||||
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
||||
use crate::types::dynamic::{AccessMode, Union};
|
||||
@ -310,7 +310,11 @@ impl Engine {
|
||||
|
||||
// If statement
|
||||
Stmt::If(x, ..) => {
|
||||
let (expr, if_block, else_block) = &**x;
|
||||
let FlowControl {
|
||||
expr,
|
||||
body: if_block,
|
||||
branch: else_block,
|
||||
} = &**x;
|
||||
|
||||
let guard_val = self
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||
@ -402,8 +406,10 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Loop
|
||||
Stmt::While(x, ..) if matches!(x.0, Expr::Unit(..) | Expr::BoolConstant(true, ..)) => {
|
||||
let (.., body) = &**x;
|
||||
Stmt::While(x, ..)
|
||||
if matches!(x.expr, Expr::Unit(..) | Expr::BoolConstant(true, ..)) =>
|
||||
{
|
||||
let FlowControl { body, .. } = &**x;
|
||||
|
||||
if body.is_empty() {
|
||||
loop {
|
||||
@ -427,7 +433,7 @@ impl Engine {
|
||||
|
||||
// While loop
|
||||
Stmt::While(x, ..) => {
|
||||
let (expr, body) = &**x;
|
||||
let FlowControl { expr, body, .. } = &**x;
|
||||
|
||||
loop {
|
||||
let condition = self
|
||||
@ -458,7 +464,7 @@ impl Engine {
|
||||
|
||||
// Do loop
|
||||
Stmt::Do(x, options, ..) => {
|
||||
let (expr, body) = &**x;
|
||||
let FlowControl { expr, body, .. } = &**x;
|
||||
let is_while = !options.contains(ASTFlags::NEGATED);
|
||||
|
||||
loop {
|
||||
@ -488,7 +494,7 @@ impl Engine {
|
||||
|
||||
// For loop
|
||||
Stmt::For(x, ..) => {
|
||||
let (var_name, counter, expr, statements) = &**x;
|
||||
let (var_name, counter, FlowControl { expr, body, .. }) = &**x;
|
||||
|
||||
let iter_obj = self
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||
@ -566,15 +572,15 @@ impl Engine {
|
||||
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
|
||||
|
||||
// Run block
|
||||
self.track_operation(global, statements.position())?;
|
||||
self.track_operation(global, body.position())?;
|
||||
|
||||
if statements.is_empty() {
|
||||
if body.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
||||
match self.eval_stmt_block(global, caches, scope, this_ptr, statements, true) {
|
||||
match self.eval_stmt_block(global, caches, scope, this_ptr, body, true) {
|
||||
Ok(_) => (),
|
||||
Err(err) => match *err {
|
||||
ERR::LoopBreak(false, ..) => (),
|
||||
@ -605,10 +611,10 @@ impl Engine {
|
||||
|
||||
// Try/Catch statement
|
||||
Stmt::TryCatch(x, ..) => {
|
||||
let TryCatchBlock {
|
||||
try_block,
|
||||
catch_var,
|
||||
catch_block,
|
||||
let FlowControl {
|
||||
body: try_block,
|
||||
expr: catch_var,
|
||||
branch: catch_block,
|
||||
} = &**x;
|
||||
|
||||
match self.eval_stmt_block(
|
||||
@ -659,10 +665,10 @@ impl Engine {
|
||||
};
|
||||
|
||||
// Restore scope at end of block
|
||||
auto_restore! { scope if !catch_var.is_empty() => rewind; let orig_scope_len = scope.len(); }
|
||||
auto_restore! { scope if !catch_var.is_unit() => rewind; let orig_scope_len = scope.len(); }
|
||||
|
||||
if !catch_var.is_empty() {
|
||||
scope.push(catch_var.name.clone(), err_value);
|
||||
if let Expr::Variable(x, ..) = catch_var {
|
||||
scope.push(x.3.clone(), err_value);
|
||||
}
|
||||
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
@ -336,8 +336,8 @@ pub use parser::ParseState;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use ast::{
|
||||
ASTFlags, ASTNode, BinaryExpr, ConditionalExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
||||
OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, SwitchCasesCollection, TryCatchBlock,
|
||||
ASTFlags, ASTNode, BinaryExpr, ConditionalExpr, Expr, FlowControl, FnCallExpr, FnCallHashes,
|
||||
Ident, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, SwitchCasesCollection,
|
||||
};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
|
103
src/optimizer.rs
103
src/optimizer.rs
@ -12,8 +12,8 @@ use crate::module::ModuleFlags;
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position,
|
||||
Scope, StaticVec, AST,
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FlowControl, FnPtr, Identifier,
|
||||
ImmutableString, Position, Scope, StaticVec, AST,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -430,8 +430,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
|
||||
// if expr {}
|
||||
Stmt::If(x, ..) if x.1.is_empty() && x.2.is_empty() => {
|
||||
let condition = &mut x.0;
|
||||
Stmt::If(x, ..) if x.body.is_empty() && x.branch.is_empty() => {
|
||||
let condition = &mut x.expr;
|
||||
state.set_dirty();
|
||||
|
||||
let pos = condition.start_position();
|
||||
@ -452,8 +452,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
};
|
||||
}
|
||||
// if false { if_block } -> Noop
|
||||
Stmt::If(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) && x.2.is_empty() => {
|
||||
if let Expr::BoolConstant(false, pos) = x.0 {
|
||||
Stmt::If(x, ..)
|
||||
if matches!(x.expr, Expr::BoolConstant(false, ..)) && x.branch.is_empty() =>
|
||||
{
|
||||
if let Expr::BoolConstant(false, pos) = x.expr {
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::Noop(pos);
|
||||
} else {
|
||||
@ -461,33 +463,31 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
}
|
||||
// if false { if_block } else { else_block } -> else_block
|
||||
Stmt::If(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => {
|
||||
Stmt::If(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => {
|
||||
state.set_dirty();
|
||||
*stmt =
|
||||
match optimize_stmt_block(mem::take(&mut *x.2), state, preserve_result, true, false)
|
||||
{
|
||||
statements if statements.is_empty() => Stmt::Noop(x.2.position()),
|
||||
statements => (statements, x.2.span()).into(),
|
||||
}
|
||||
let body = mem::take(&mut *x.branch);
|
||||
*stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
|
||||
statements if statements.is_empty() => Stmt::Noop(x.branch.position()),
|
||||
statements => (statements, x.branch.span()).into(),
|
||||
}
|
||||
}
|
||||
// if true { if_block } else { else_block } -> if_block
|
||||
Stmt::If(x, ..) if matches!(x.0, Expr::BoolConstant(true, ..)) => {
|
||||
Stmt::If(x, ..) if matches!(x.expr, Expr::BoolConstant(true, ..)) => {
|
||||
state.set_dirty();
|
||||
*stmt =
|
||||
match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false)
|
||||
{
|
||||
statements if statements.is_empty() => Stmt::Noop(x.1.position()),
|
||||
statements => (statements, x.1.span()).into(),
|
||||
}
|
||||
let body = mem::take(&mut *x.body);
|
||||
*stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
|
||||
statements if statements.is_empty() => Stmt::Noop(x.body.position()),
|
||||
statements => (statements, x.body.span()).into(),
|
||||
}
|
||||
}
|
||||
// if expr { if_block } else { else_block }
|
||||
Stmt::If(x, ..) => {
|
||||
let (condition, body, other) = &mut **x;
|
||||
optimize_expr(condition, state, false);
|
||||
**body =
|
||||
optimize_stmt_block(mem::take(&mut **body), state, preserve_result, true, false);
|
||||
**other =
|
||||
optimize_stmt_block(mem::take(&mut **other), state, preserve_result, true, false);
|
||||
let FlowControl { expr, body, branch } = &mut **x;
|
||||
optimize_expr(expr, state, false);
|
||||
let statements = mem::take(&mut **body);
|
||||
**body = optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
let statements = mem::take(&mut **branch);
|
||||
**branch = optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
}
|
||||
|
||||
// switch const { ... }
|
||||
@ -524,7 +524,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||
optimize_expr(&mut b.condition, state, false);
|
||||
|
||||
let else_stmt = match def_case {
|
||||
let branch = match def_case {
|
||||
Some(index) => {
|
||||
let mut def_stmt =
|
||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
@ -533,14 +533,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
_ => StmtBlock::NONE,
|
||||
};
|
||||
let body = Stmt::Expr(mem::take(&mut b.expr).into()).into();
|
||||
let expr = mem::take(&mut b.condition);
|
||||
|
||||
*stmt = Stmt::If(
|
||||
(
|
||||
mem::take(&mut b.condition),
|
||||
Stmt::Expr(mem::take(&mut b.expr).into()).into(),
|
||||
else_stmt,
|
||||
)
|
||||
.into(),
|
||||
FlowControl { expr, body, branch }.into(),
|
||||
match_expr.start_position(),
|
||||
);
|
||||
}
|
||||
@ -585,12 +582,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
optimize_stmt(&mut statements, state, true);
|
||||
*stmt = statements;
|
||||
} else {
|
||||
let mut condition = mem::take(&mut range_block.condition);
|
||||
let mut expr = mem::take(&mut range_block.condition);
|
||||
|
||||
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||
optimize_expr(&mut condition, state, false);
|
||||
optimize_expr(&mut expr, state, false);
|
||||
|
||||
let else_stmt = match def_case {
|
||||
let branch = match def_case {
|
||||
Some(index) => {
|
||||
let mut def_stmt =
|
||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
@ -600,12 +597,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
_ => StmtBlock::NONE,
|
||||
};
|
||||
|
||||
let if_stmt =
|
||||
let body =
|
||||
Stmt::Expr(mem::take(&mut expressions[r.index()].expr).into())
|
||||
.into();
|
||||
|
||||
*stmt = Stmt::If(
|
||||
(condition, if_stmt, else_stmt).into(),
|
||||
FlowControl { expr, body, branch }.into(),
|
||||
match_expr.start_position(),
|
||||
);
|
||||
}
|
||||
@ -738,7 +735,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
|
||||
// while false { block } -> Noop
|
||||
Stmt::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => match x.0 {
|
||||
Stmt::While(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => match x.expr {
|
||||
Expr::BoolConstant(false, pos) => {
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::Noop(pos);
|
||||
@ -747,22 +744,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
},
|
||||
// while expr { block }
|
||||
Stmt::While(x, ..) => {
|
||||
let (condition, body) = &mut **x;
|
||||
optimize_expr(condition, state, false);
|
||||
if let Expr::BoolConstant(true, pos) = condition {
|
||||
*condition = Expr::Unit(*pos);
|
||||
let FlowControl { expr, body, .. } = &mut **x;
|
||||
optimize_expr(expr, state, false);
|
||||
if let Expr::BoolConstant(true, pos) = expr {
|
||||
*expr = Expr::Unit(*pos);
|
||||
}
|
||||
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
|
||||
}
|
||||
// do { block } while|until expr
|
||||
Stmt::Do(x, ..) => {
|
||||
optimize_expr(&mut x.0, state, false);
|
||||
*x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false);
|
||||
optimize_expr(&mut x.expr, state, false);
|
||||
*x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
|
||||
}
|
||||
// for id in expr { block }
|
||||
Stmt::For(x, ..) => {
|
||||
optimize_expr(&mut x.2, state, false);
|
||||
*x.3 = optimize_stmt_block(mem::take(&mut *x.3), state, false, true, false);
|
||||
optimize_expr(&mut x.2.expr, state, false);
|
||||
*x.2.body = optimize_stmt_block(mem::take(&mut *x.2.body), state, false, true, false);
|
||||
}
|
||||
// let id = expr;
|
||||
Stmt::Var(x, options, ..) if !options.contains(ASTFlags::CONSTANT) => {
|
||||
@ -791,21 +788,19 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
}
|
||||
}
|
||||
// try { pure try_block } catch ( var ) { catch_block } -> try_block
|
||||
Stmt::TryCatch(x, ..) if x.try_block.iter().all(Stmt::is_pure) => {
|
||||
Stmt::TryCatch(x, ..) if x.body.iter().all(Stmt::is_pure) => {
|
||||
// If try block is pure, there will never be any exceptions
|
||||
state.set_dirty();
|
||||
*stmt = (
|
||||
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false),
|
||||
x.try_block.span(),
|
||||
optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false),
|
||||
x.body.span(),
|
||||
)
|
||||
.into();
|
||||
}
|
||||
// try { try_block } catch ( var ) { catch_block }
|
||||
Stmt::TryCatch(x, ..) => {
|
||||
*x.try_block =
|
||||
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false);
|
||||
*x.catch_block =
|
||||
optimize_stmt_block(mem::take(&mut *x.catch_block), state, false, true, false);
|
||||
*x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
|
||||
*x.branch = optimize_stmt_block(mem::take(&mut *x.branch), state, false, true, false);
|
||||
}
|
||||
|
||||
// expr(stmt)
|
||||
|
@ -3,9 +3,9 @@
|
||||
use crate::api::events::VarDefInfo;
|
||||
use crate::api::options::LangOptions;
|
||||
use crate::ast::{
|
||||
ASTFlags, BinaryExpr, CaseBlocksList, ConditionalExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
||||
Namespace, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer,
|
||||
SwitchCasesCollection, TryCatchBlock,
|
||||
ASTFlags, BinaryExpr, CaseBlocksList, ConditionalExpr, Expr, FlowControl, FnCallExpr,
|
||||
FnCallHashes, Ident, Namespace, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock,
|
||||
StmtBlockContainer, SwitchCasesCollection,
|
||||
};
|
||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
@ -2649,14 +2649,14 @@ impl Engine {
|
||||
|
||||
// if guard { if_body }
|
||||
ensure_not_statement_expr(input, "a boolean")?;
|
||||
let guard = self
|
||||
let expr = self
|
||||
.parse_expr(input, state, lib, settings)?
|
||||
.ensure_bool_expr()?;
|
||||
ensure_not_assignment(input)?;
|
||||
let if_body = self.parse_block(input, state, lib, settings)?;
|
||||
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||
|
||||
// if guard { if_body } else ...
|
||||
let else_body = if match_token(input, Token::Else).0 {
|
||||
let branch = if match_token(input, Token::Else).0 {
|
||||
if let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
|
||||
// if guard { if_body } else if ...
|
||||
self.parse_if(input, state, lib, settings)?
|
||||
@ -2666,10 +2666,11 @@ impl Engine {
|
||||
}
|
||||
} else {
|
||||
Stmt::Noop(Position::NONE)
|
||||
};
|
||||
}
|
||||
.into();
|
||||
|
||||
Ok(Stmt::If(
|
||||
(guard, if_body.into(), else_body.into()).into(),
|
||||
FlowControl { expr, body, branch }.into(),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -2685,7 +2686,7 @@ impl Engine {
|
||||
let mut settings = settings.level_up()?;
|
||||
|
||||
// while|loops ...
|
||||
let (guard, token_pos) = match input.next().expect(NEVER_ENDS) {
|
||||
let (expr, token_pos) = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::While, pos) => {
|
||||
ensure_not_statement_expr(input, "a boolean")?;
|
||||
let expr = self
|
||||
@ -2700,9 +2701,13 @@ impl Engine {
|
||||
settings.pos = token_pos;
|
||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||
|
||||
let body = self.parse_block(input, state, lib, settings)?;
|
||||
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||
let branch = StmtBlock::NONE;
|
||||
|
||||
Ok(Stmt::While((guard, body.into()).into(), settings.pos))
|
||||
Ok(Stmt::While(
|
||||
FlowControl { expr, body, branch }.into(),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a do loop.
|
||||
@ -2722,7 +2727,7 @@ impl Engine {
|
||||
|
||||
// do { body } [while|until] guard
|
||||
|
||||
let body = self.parse_block(input, state, lib, settings)?;
|
||||
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||
|
||||
let negated = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::While, ..) => ASTFlags::NONE,
|
||||
@ -2740,12 +2745,18 @@ impl Engine {
|
||||
}
|
||||
|
||||
ensure_not_statement_expr(input, "a boolean")?;
|
||||
let guard = self
|
||||
let expr = self
|
||||
.parse_expr(input, state, lib, settings)?
|
||||
.ensure_bool_expr()?;
|
||||
ensure_not_assignment(input)?;
|
||||
|
||||
Ok(Stmt::Do((guard, body.into()).into(), negated, settings.pos))
|
||||
let branch = StmtBlock::NONE;
|
||||
|
||||
Ok(Stmt::Do(
|
||||
FlowControl { expr, body, branch }.into(),
|
||||
negated,
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a for loop.
|
||||
@ -2837,12 +2848,14 @@ impl Engine {
|
||||
};
|
||||
|
||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||
let body = self.parse_block(input, state, lib, settings)?;
|
||||
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||
|
||||
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
||||
|
||||
let branch = StmtBlock::NONE;
|
||||
|
||||
Ok(Stmt::For(
|
||||
Box::new((loop_var, counter_var, expr, body.into())),
|
||||
Box::new((loop_var, counter_var, FlowControl { expr, body, branch })),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -3477,7 +3490,7 @@ impl Engine {
|
||||
settings.pos = eat_token(input, Token::Try);
|
||||
|
||||
// try { try_block }
|
||||
let try_block = self.parse_block(input, state, lib, settings)?;
|
||||
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||
|
||||
// try { try_block } catch
|
||||
let (matched, catch_pos) = match_token(input, Token::Catch);
|
||||
@ -3516,20 +3529,23 @@ impl Engine {
|
||||
};
|
||||
|
||||
// try { try_block } catch ( var ) { catch_block }
|
||||
let catch_block = self.parse_block(input, state, lib, settings)?;
|
||||
let branch = self.parse_block(input, state, lib, settings)?.into();
|
||||
|
||||
if !catch_var.is_empty() {
|
||||
let expr = if !catch_var.is_empty() {
|
||||
// Remove the error variable from the stack
|
||||
state.stack.as_deref_mut().unwrap().pop();
|
||||
}
|
||||
|
||||
Expr::Variable(
|
||||
(None, Namespace::default(), 0, catch_var.name).into(),
|
||||
None,
|
||||
catch_var.pos,
|
||||
)
|
||||
} else {
|
||||
Expr::Unit(catch_var.pos)
|
||||
};
|
||||
|
||||
Ok(Stmt::TryCatch(
|
||||
TryCatchBlock {
|
||||
try_block: try_block.into(),
|
||||
catch_var,
|
||||
catch_block: catch_block.into(),
|
||||
}
|
||||
.into(),
|
||||
FlowControl { body, expr, branch }.into(),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user