Encapsulate into FlowControl type.

This commit is contained in:
Stephen Chung 2022-12-23 14:26:06 +08:00
parent 80ccd75514
commit cb09393dba
6 changed files with 158 additions and 133 deletions

View File

@ -22,8 +22,8 @@ pub use namespace_none::Namespace;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use script_fn::{ScriptFnDef, ScriptFnMetadata}; pub use script_fn::{ScriptFnDef, ScriptFnMetadata};
pub use stmt::{ pub use stmt::{
CaseBlocksList, ConditionalExpr, OpAssignment, RangeCase, Stmt, StmtBlock, StmtBlockContainer, CaseBlocksList, ConditionalExpr, FlowControl, OpAssignment, RangeCase, Stmt, StmtBlock,
SwitchCasesCollection, TryCatchBlock, StmtBlockContainer, SwitchCasesCollection,
}; };
/// _(internals)_ Placeholder for a script-defined function. /// _(internals)_ Placeholder for a script-defined function.

View File

@ -292,18 +292,6 @@ pub struct SwitchCasesCollection {
pub def_case: Option<usize>, 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`]. /// Number of items to keep inline for [`StmtBlockContainer`].
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
const STMT_BLOCK_INLINE_SIZE: usize = 8; 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. /// _(internals)_ A statement.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
@ -540,7 +544,7 @@ pub enum Stmt {
/// No-op. /// No-op.
Noop(Position), Noop(Position),
/// `if` expr `{` stmt `}` `else` `{` stmt `}` /// `if` expr `{` stmt `}` `else` `{` stmt `}`
If(Box<(Expr, StmtBlock, StmtBlock)>, Position), If(Box<FlowControl>, Position),
/// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}` /// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}`
/// ///
/// ### Data Structure /// ### Data Structure
@ -552,16 +556,16 @@ pub enum Stmt {
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}` /// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
/// ///
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement. /// 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 /// `do` `{` stmt `}` `while`|`until` expr
/// ///
/// ### Flags /// ### Flags
/// ///
/// * [`NONE`][ASTFlags::NONE] = `while` /// * [`NONE`][ASTFlags::NONE] = `while`
/// * [`NEGATED`][ASTFlags::NEGATED] = `until` /// * [`NEGATED`][ASTFlags::NEGATED] = `until`
Do(Box<(Expr, StmtBlock)>, ASTFlags, Position), Do(Box<FlowControl>, ASTFlags, Position),
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}` /// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
For(Box<(Ident, Ident, Expr, StmtBlock)>, Position), For(Box<(Ident, Ident, FlowControl)>, Position),
/// \[`export`\] `let`|`const` id `=` expr /// \[`export`\] `let`|`const` id `=` expr
/// ///
/// ### Flags /// ### Flags
@ -579,7 +583,7 @@ pub enum Stmt {
/// `{` stmt`;` ... `}` /// `{` stmt`;` ... `}`
Block(Box<StmtBlock>), Block(Box<StmtBlock>),
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
TryCatch(Box<TryCatchBlock>, Position), TryCatch(Box<FlowControl>, Position),
/// [expression][Expr] /// [expression][Expr]
Expr(Box<Expr>), Expr(Box<Expr>),
/// `continue`/`break` expr /// `continue`/`break` expr
@ -815,7 +819,9 @@ impl Stmt {
Self::Noop(..) => true, Self::Noop(..) => true,
Self::Expr(expr) => expr.is_pure(), Self::Expr(expr) => expr.is_pure(),
Self::If(x, ..) => { 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, ..) => { Self::Switch(x, ..) => {
let (expr, sw) = &**x; let (expr, sw) = &**x;
@ -833,10 +839,10 @@ impl Stmt {
} }
// Loops that exit can be pure because it can never be infinite. // Loops that exit can be pure because it can never be infinite.
Self::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => true, Self::While(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => true,
Self::Do(x, options, ..) if matches!(x.0, Expr::BoolConstant(..)) => match x.0 { Self::Do(x, options, ..) if matches!(x.expr, Expr::BoolConstant(..)) => match x.expr {
Expr::BoolConstant(cond, ..) if cond == options.contains(ASTFlags::NEGATED) => { Expr::BoolConstant(cond, ..) if cond == options.contains(ASTFlags::NEGATED) => {
x.1.iter().all(Self::is_pure) x.body.iter().all(Self::is_pure)
} }
_ => false, _ => false,
}, },
@ -846,13 +852,15 @@ impl Stmt {
// For loops can be pure because if the iterable is pure, it is finite, // For loops can be pure because if the iterable is pure, it is finite,
// so infinite loops can never occur. // 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::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
Self::Block(block, ..) => block.iter().all(Self::is_pure), Self::Block(block, ..) => block.iter().all(Self::is_pure),
Self::BreakLoop(..) | Self::Return(..) => false, Self::BreakLoop(..) | Self::Return(..) => false,
Self::TryCatch(x, ..) => { 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"))] #[cfg(not(feature = "no_module"))]
@ -947,15 +955,15 @@ impl Stmt {
} }
} }
Self::If(x, ..) => { Self::If(x, ..) => {
if !x.0.walk(path, on_node) { if !x.expr.walk(path, on_node) {
return false; return false;
} }
for s in &x.1 { for s in &x.body {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
} }
for s in &x.2 { for s in &x.branch {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
@ -996,20 +1004,20 @@ impl Stmt {
} }
} }
Self::While(x, ..) | Self::Do(x, ..) => { Self::While(x, ..) | Self::Do(x, ..) => {
if !x.0.walk(path, on_node) { if !x.expr.walk(path, on_node) {
return false; return false;
} }
for s in x.1.statements() { for s in x.body.statements() {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
} }
} }
Self::For(x, ..) => { Self::For(x, ..) => {
if !x.2.walk(path, on_node) { if !x.2.expr.walk(path, on_node) {
return false; return false;
} }
for s in &x.3 { for s in &x.2.body {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
@ -1038,12 +1046,12 @@ impl Stmt {
} }
} }
Self::TryCatch(x, ..) => { Self::TryCatch(x, ..) => {
for s in &x.try_block { for s in &x.body {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
} }
for s in &x.catch_block { for s in &x.branch {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }

View File

@ -3,7 +3,7 @@
use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use super::{Caches, EvalContext, GlobalRuntimeState, Target};
use crate::api::events::VarDefInfo; use crate::api::events::VarDefInfo;
use crate::ast::{ 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::func::{get_builtin_op_assignment_fn, get_hasher};
use crate::types::dynamic::{AccessMode, Union}; use crate::types::dynamic::{AccessMode, Union};
@ -310,7 +310,11 @@ impl Engine {
// If statement // If statement
Stmt::If(x, ..) => { Stmt::If(x, ..) => {
let (expr, if_block, else_block) = &**x; let FlowControl {
expr,
body: if_block,
branch: else_block,
} = &**x;
let guard_val = self let guard_val = self
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)? .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
@ -402,8 +406,10 @@ impl Engine {
} }
// Loop // Loop
Stmt::While(x, ..) if matches!(x.0, Expr::Unit(..) | Expr::BoolConstant(true, ..)) => { Stmt::While(x, ..)
let (.., body) = &**x; if matches!(x.expr, Expr::Unit(..) | Expr::BoolConstant(true, ..)) =>
{
let FlowControl { body, .. } = &**x;
if body.is_empty() { if body.is_empty() {
loop { loop {
@ -427,7 +433,7 @@ impl Engine {
// While loop // While loop
Stmt::While(x, ..) => { Stmt::While(x, ..) => {
let (expr, body) = &**x; let FlowControl { expr, body, .. } = &**x;
loop { loop {
let condition = self let condition = self
@ -458,7 +464,7 @@ impl Engine {
// Do loop // Do loop
Stmt::Do(x, options, ..) => { Stmt::Do(x, options, ..) => {
let (expr, body) = &**x; let FlowControl { expr, body, .. } = &**x;
let is_while = !options.contains(ASTFlags::NEGATED); let is_while = !options.contains(ASTFlags::NEGATED);
loop { loop {
@ -488,7 +494,7 @@ impl Engine {
// For loop // For loop
Stmt::For(x, ..) => { Stmt::For(x, ..) => {
let (var_name, counter, expr, statements) = &**x; let (var_name, counter, FlowControl { expr, body, .. }) = &**x;
let iter_obj = self let iter_obj = self
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)? .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; *scope.get_mut_by_index(index).write_lock().unwrap() = value;
// Run block // Run block
self.track_operation(global, statements.position())?; self.track_operation(global, body.position())?;
if statements.is_empty() { if body.is_empty() {
continue; continue;
} }
let this_ptr = this_ptr.as_deref_mut(); 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(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
ERR::LoopBreak(false, ..) => (), ERR::LoopBreak(false, ..) => (),
@ -605,10 +611,10 @@ impl Engine {
// Try/Catch statement // Try/Catch statement
Stmt::TryCatch(x, ..) => { Stmt::TryCatch(x, ..) => {
let TryCatchBlock { let FlowControl {
try_block, body: try_block,
catch_var, expr: catch_var,
catch_block, branch: catch_block,
} = &**x; } = &**x;
match self.eval_stmt_block( match self.eval_stmt_block(
@ -659,10 +665,10 @@ impl Engine {
}; };
// Restore scope at end of block // 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() { if let Expr::Variable(x, ..) = catch_var {
scope.push(catch_var.name.clone(), err_value); scope.push(x.3.clone(), err_value);
} }
let this_ptr = this_ptr.as_deref_mut(); let this_ptr = this_ptr.as_deref_mut();

View File

@ -336,8 +336,8 @@ pub use parser::ParseState;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use ast::{ pub use ast::{
ASTFlags, ASTNode, BinaryExpr, ConditionalExpr, Expr, FnCallExpr, FnCallHashes, Ident, ASTFlags, ASTNode, BinaryExpr, ConditionalExpr, Expr, FlowControl, FnCallExpr, FnCallHashes,
OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, SwitchCasesCollection, TryCatchBlock, Ident, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, SwitchCasesCollection,
}; };
#[cfg(feature = "internals")] #[cfg(feature = "internals")]

View File

@ -12,8 +12,8 @@ use crate::module::ModuleFlags;
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode; use crate::types::dynamic::AccessMode;
use crate::{ use crate::{
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position, calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FlowControl, FnPtr, Identifier,
Scope, StaticVec, AST, ImmutableString, Position, Scope, StaticVec, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -430,8 +430,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
// if expr {} // if expr {}
Stmt::If(x, ..) if x.1.is_empty() && x.2.is_empty() => { Stmt::If(x, ..) if x.body.is_empty() && x.branch.is_empty() => {
let condition = &mut x.0; let condition = &mut x.expr;
state.set_dirty(); state.set_dirty();
let pos = condition.start_position(); 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 // if false { if_block } -> Noop
Stmt::If(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) && x.2.is_empty() => { Stmt::If(x, ..)
if let Expr::BoolConstant(false, pos) = x.0 { if matches!(x.expr, Expr::BoolConstant(false, ..)) && x.branch.is_empty() =>
{
if let Expr::BoolConstant(false, pos) = x.expr {
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(pos); *stmt = Stmt::Noop(pos);
} else { } 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 // 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(); state.set_dirty();
*stmt = let body = mem::take(&mut *x.branch);
match optimize_stmt_block(mem::take(&mut *x.2), state, preserve_result, true, false) *stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
{ statements if statements.is_empty() => Stmt::Noop(x.branch.position()),
statements if statements.is_empty() => Stmt::Noop(x.2.position()), statements => (statements, x.branch.span()).into(),
statements => (statements, x.2.span()).into(),
} }
} }
// if true { if_block } else { else_block } -> if_block // 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(); state.set_dirty();
*stmt = let body = mem::take(&mut *x.body);
match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false) *stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
{ statements if statements.is_empty() => Stmt::Noop(x.body.position()),
statements if statements.is_empty() => Stmt::Noop(x.1.position()), statements => (statements, x.body.span()).into(),
statements => (statements, x.1.span()).into(),
} }
} }
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
Stmt::If(x, ..) => { Stmt::If(x, ..) => {
let (condition, body, other) = &mut **x; let FlowControl { expr, body, branch } = &mut **x;
optimize_expr(condition, state, false); optimize_expr(expr, state, false);
**body = let statements = mem::take(&mut **body);
optimize_stmt_block(mem::take(&mut **body), state, preserve_result, true, false); **body = optimize_stmt_block(statements, state, preserve_result, true, false);
**other = let statements = mem::take(&mut **branch);
optimize_stmt_block(mem::take(&mut **other), state, preserve_result, true, false); **branch = optimize_stmt_block(statements, state, preserve_result, true, false);
} }
// switch const { ... } // 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 } // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut b.condition, state, false); optimize_expr(&mut b.condition, state, false);
let else_stmt = match def_case { let branch = match def_case {
Some(index) => { Some(index) => {
let mut def_stmt = let mut def_stmt =
Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); 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, _ => StmtBlock::NONE,
}; };
let body = Stmt::Expr(mem::take(&mut b.expr).into()).into();
let expr = mem::take(&mut b.condition);
*stmt = Stmt::If( *stmt = Stmt::If(
( FlowControl { expr, body, branch }.into(),
mem::take(&mut b.condition),
Stmt::Expr(mem::take(&mut b.expr).into()).into(),
else_stmt,
)
.into(),
match_expr.start_position(), 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); optimize_stmt(&mut statements, state, true);
*stmt = statements; *stmt = statements;
} else { } 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 } // 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) => { Some(index) => {
let mut def_stmt = let mut def_stmt =
Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); 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, _ => StmtBlock::NONE,
}; };
let if_stmt = let body =
Stmt::Expr(mem::take(&mut expressions[r.index()].expr).into()) Stmt::Expr(mem::take(&mut expressions[r.index()].expr).into())
.into(); .into();
*stmt = Stmt::If( *stmt = Stmt::If(
(condition, if_stmt, else_stmt).into(), FlowControl { expr, body, branch }.into(),
match_expr.start_position(), match_expr.start_position(),
); );
} }
@ -738,7 +735,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
// while false { block } -> Noop // 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) => { Expr::BoolConstant(false, pos) => {
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(pos); *stmt = Stmt::Noop(pos);
@ -747,22 +744,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
}, },
// while expr { block } // while expr { block }
Stmt::While(x, ..) => { Stmt::While(x, ..) => {
let (condition, body) = &mut **x; let FlowControl { expr, body, .. } = &mut **x;
optimize_expr(condition, state, false); optimize_expr(expr, state, false);
if let Expr::BoolConstant(true, pos) = condition { if let Expr::BoolConstant(true, pos) = expr {
*condition = Expr::Unit(*pos); *expr = Expr::Unit(*pos);
} }
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false); **body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
} }
// do { block } while|until expr // do { block } while|until expr
Stmt::Do(x, ..) => { Stmt::Do(x, ..) => {
optimize_expr(&mut x.0, state, false); optimize_expr(&mut x.expr, state, false);
*x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false); *x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
} }
// for id in expr { block } // for id in expr { block }
Stmt::For(x, ..) => { Stmt::For(x, ..) => {
optimize_expr(&mut x.2, state, false); optimize_expr(&mut x.2.expr, state, false);
*x.3 = optimize_stmt_block(mem::take(&mut *x.3), state, false, true, false); *x.2.body = optimize_stmt_block(mem::take(&mut *x.2.body), state, false, true, false);
} }
// let id = expr; // let id = expr;
Stmt::Var(x, options, ..) if !options.contains(ASTFlags::CONSTANT) => { 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 // 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 // If try block is pure, there will never be any exceptions
state.set_dirty(); state.set_dirty();
*stmt = ( *stmt = (
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false), optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false),
x.try_block.span(), x.body.span(),
) )
.into(); .into();
} }
// try { try_block } catch ( var ) { catch_block } // try { try_block } catch ( var ) { catch_block }
Stmt::TryCatch(x, ..) => { Stmt::TryCatch(x, ..) => {
*x.try_block = *x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false); *x.branch = optimize_stmt_block(mem::take(&mut *x.branch), state, false, true, false);
*x.catch_block =
optimize_stmt_block(mem::take(&mut *x.catch_block), state, false, true, false);
} }
// expr(stmt) // expr(stmt)

View File

@ -3,9 +3,9 @@
use crate::api::events::VarDefInfo; use crate::api::events::VarDefInfo;
use crate::api::options::LangOptions; use crate::api::options::LangOptions;
use crate::ast::{ use crate::ast::{
ASTFlags, BinaryExpr, CaseBlocksList, ConditionalExpr, Expr, FnCallExpr, FnCallHashes, Ident, ASTFlags, BinaryExpr, CaseBlocksList, ConditionalExpr, Expr, FlowControl, FnCallExpr,
Namespace, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer, FnCallHashes, Ident, Namespace, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock,
SwitchCasesCollection, TryCatchBlock, StmtBlockContainer, SwitchCasesCollection,
}; };
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
use crate::eval::{Caches, GlobalRuntimeState}; use crate::eval::{Caches, GlobalRuntimeState};
@ -2649,14 +2649,14 @@ impl Engine {
// if guard { if_body } // if guard { if_body }
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let guard = self let expr = self
.parse_expr(input, state, lib, settings)? .parse_expr(input, state, lib, settings)?
.ensure_bool_expr()?; .ensure_bool_expr()?;
ensure_not_assignment(input)?; 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 ... // 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 let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
// if guard { if_body } else if ... // if guard { if_body } else if ...
self.parse_if(input, state, lib, settings)? self.parse_if(input, state, lib, settings)?
@ -2666,10 +2666,11 @@ impl Engine {
} }
} else { } else {
Stmt::Noop(Position::NONE) Stmt::Noop(Position::NONE)
}; }
.into();
Ok(Stmt::If( Ok(Stmt::If(
(guard, if_body.into(), else_body.into()).into(), FlowControl { expr, body, branch }.into(),
settings.pos, settings.pos,
)) ))
} }
@ -2685,7 +2686,7 @@ impl Engine {
let mut settings = settings.level_up()?; let mut settings = settings.level_up()?;
// while|loops ... // 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) => { (Token::While, pos) => {
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let expr = self let expr = self
@ -2700,9 +2701,13 @@ impl Engine {
settings.pos = token_pos; settings.pos = token_pos;
settings.flags |= ParseSettingFlags::BREAKABLE; 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. /// Parse a do loop.
@ -2722,7 +2727,7 @@ impl Engine {
// do { body } [while|until] guard // 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) { let negated = match input.next().expect(NEVER_ENDS) {
(Token::While, ..) => ASTFlags::NONE, (Token::While, ..) => ASTFlags::NONE,
@ -2740,12 +2745,18 @@ impl Engine {
} }
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let guard = self let expr = self
.parse_expr(input, state, lib, settings)? .parse_expr(input, state, lib, settings)?
.ensure_bool_expr()?; .ensure_bool_expr()?;
ensure_not_assignment(input)?; 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. /// Parse a for loop.
@ -2837,12 +2848,14 @@ impl Engine {
}; };
settings.flags |= ParseSettingFlags::BREAKABLE; 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); state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
let branch = StmtBlock::NONE;
Ok(Stmt::For( Ok(Stmt::For(
Box::new((loop_var, counter_var, expr, body.into())), Box::new((loop_var, counter_var, FlowControl { expr, body, branch })),
settings.pos, settings.pos,
)) ))
} }
@ -3477,7 +3490,7 @@ impl Engine {
settings.pos = eat_token(input, Token::Try); settings.pos = eat_token(input, Token::Try);
// try { try_block } // 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 // try { try_block } catch
let (matched, catch_pos) = match_token(input, Token::Catch); let (matched, catch_pos) = match_token(input, Token::Catch);
@ -3516,20 +3529,23 @@ impl Engine {
}; };
// try { try_block } catch ( var ) { catch_block } // 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 // Remove the error variable from the stack
state.stack.as_deref_mut().unwrap().pop(); 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( Ok(Stmt::TryCatch(
TryCatchBlock { FlowControl { body, expr, branch }.into(),
try_block: try_block.into(),
catch_var,
catch_block: catch_block.into(),
}
.into(),
settings.pos, settings.pos,
)) ))
} }