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"))]
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.

View File

@ -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;
}

View File

@ -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();

View File

@ -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")]

View File

@ -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)

View File

@ -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,
))
}