diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1e63aa4b..cfa3a943 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -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. diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 8d13c1dd..af4ff064 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -292,18 +292,6 @@ pub struct SwitchCasesCollection { pub def_case: Option, } -/// _(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 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, 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, Position), /// `do` `{` stmt `}` `while`|`until` expr /// /// ### Flags /// /// * [`NONE`][ASTFlags::NONE] = `while` /// * [`NEGATED`][ASTFlags::NEGATED] = `until` - Do(Box<(Expr, StmtBlock)>, ASTFlags, Position), + Do(Box, 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), /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` - TryCatch(Box, Position), + TryCatch(Box, Position), /// [expression][Expr] Expr(Box), /// `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; } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 7ef26bf4..21fa73b3 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -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(); diff --git a/src/lib.rs b/src/lib.rs index f8ac7dbd..b6be66eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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")] diff --git a/src/optimizer.rs b/src/optimizer.rs index 9c421857..516747d6 100644 --- a/src/optimizer.rs +++ b/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) diff --git a/src/parser.rs b/src/parser.rs index 046ce895..8089c78d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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, )) }