From 93b5df6b3c05697a0b5cf330aed90984b2a3749c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Oct 2020 18:18:19 +0800 Subject: [PATCH] Pack Stmt structure. --- src/engine.rs | 99 +++++++---------- src/optimize.rs | 286 +++++++++++++++++++++++------------------------- src/parser.rs | 207 +++++++++++++++++++---------------- 3 files changed, 290 insertions(+), 302 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index fb1bf33b..fdc1a71a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1809,12 +1809,12 @@ impl Engine { Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level), // Block scope - Stmt::Block(x) => { + Stmt::Block(statements, _) => { let prev_scope_len = scope.len(); let prev_mods_len = mods.len(); state.scope_level += 1; - let result = x.0.iter().try_fold(Default::default(), |_, stmt| { + let result = statements.iter().try_fold(Default::default(), |_, stmt| { self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) }); @@ -1830,27 +1830,22 @@ impl Engine { } // If-else statement - Stmt::IfThenElse(x) => { - let (expr, if_block, else_block, _) = x.as_ref(); - - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? - .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, expr.position())) - .and_then(|guard_val| { - if guard_val { - self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level) - } else if let Some(stmt) = else_block { - self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) - } else { - Ok(Default::default()) - } - }) - } + Stmt::IfThenElse(expr, if_block, else_block, _) => self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .as_bool() + .map_err(|err| self.make_type_mismatch_err::(err, expr.position())) + .and_then(|guard_val| { + if guard_val { + self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level) + } else if let Some(stmt) = else_block { + self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) + } else { + Ok(Default::default()) + } + }), // While loop - Stmt::While(x) => loop { - let (expr, body, _) = x.as_ref(); - + Stmt::While(expr, body, _) => loop { match self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() @@ -1873,8 +1868,8 @@ impl Engine { }, // Loop statement - Stmt::Loop(x) => loop { - match self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level) { + Stmt::Loop(block, _) => loop { + match self.eval_stmt(scope, mods, state, lib, this_ptr, block, level) { Ok(_) => (), Err(err) => match *err { EvalAltResult::LoopBreak(false, _) => (), @@ -1885,8 +1880,7 @@ impl Engine { }, // For loop - Stmt::For(x) => { - let (name, expr, stmt, _) = x.as_ref(); + Stmt::For(name, expr, stmt, _) => { let iter_obj = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let iter_type = iter_obj.type_id(); @@ -1929,7 +1923,7 @@ impl Engine { scope.rewind(scope.len() - 1); Ok(Default::default()) } else { - EvalAltResult::ErrorFor(x.1.position()).into() + EvalAltResult::ErrorFor(expr.position()).into() } } @@ -1941,10 +1935,10 @@ impl Engine { // Try/Catch statement Stmt::TryCatch(x) => { - let ((body, _), var_def, (catch_body, _)) = x.as_ref(); + let ((try_body, _), var_def, (catch_body, _)) = x.as_ref(); let result = self - .eval_stmt(scope, mods, state, lib, this_ptr, body, level) + .eval_stmt(scope, mods, state, lib, this_ptr, try_body, level) .map(|_| ().into()); match result { @@ -1992,40 +1986,33 @@ impl Engine { } // Return value - Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => { - let expr = x.1.as_ref().unwrap(); - EvalAltResult::Return( - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, - (x.0).1, - ) - .into() - } + Stmt::ReturnWithVal((ReturnType::Return, pos), Some(expr), _) => EvalAltResult::Return( + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, + *pos, + ) + .into(), // Empty return - Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Return => { - EvalAltResult::Return(Default::default(), (x.0).1).into() + Stmt::ReturnWithVal((ReturnType::Return, pos), None, _) => { + EvalAltResult::Return(Default::default(), *pos).into() } // Throw value - Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => { - let expr = x.1.as_ref().unwrap(); + Stmt::ReturnWithVal((ReturnType::Exception, pos), Some(expr), _) => { let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; - EvalAltResult::ErrorRuntime(val, (x.0).1).into() + EvalAltResult::ErrorRuntime(val, *pos).into() } // Empty throw - Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Exception => { - EvalAltResult::ErrorRuntime(().into(), (x.0).1).into() + Stmt::ReturnWithVal((ReturnType::Exception, pos), None, _) => { + EvalAltResult::ErrorRuntime(().into(), *pos).into() } - Stmt::ReturnWithVal(_) => unreachable!(), - // Let/const statement - Stmt::Let(x) | Stmt::Const(x) => { - let ((var_name, _), expr, _) = x.as_ref(); + Stmt::Let(var_def, expr, _) | Stmt::Const(var_def, expr, _) => { let entry_type = match stmt { - Stmt::Let(_) => ScopeEntryType::Normal, - Stmt::Const(_) => ScopeEntryType::Constant, + Stmt::Let(_, _, _) => ScopeEntryType::Normal, + Stmt::Const(_, _, _) => ScopeEntryType::Constant, _ => unreachable!(), }; @@ -2035,16 +2022,14 @@ impl Engine { } else { ().into() }; - let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state); + let var_name = unsafe_cast_var_name_to_lifetime(&var_def.0, &state); scope.push_dynamic_value(var_name, entry_type, val, false); Ok(Default::default()) } // Import statement #[cfg(not(feature = "no_module"))] - Stmt::Import(x) => { - let (expr, alias, _pos) = x.as_ref(); - + Stmt::Import(expr, alias, _pos) => { // Guard against too many modules #[cfg(not(feature = "unchecked"))] if state.modules >= self.max_modules() { @@ -2079,8 +2064,8 @@ impl Engine { // Export statement #[cfg(not(feature = "no_module"))] - Stmt::Export(x) => { - for ((id, id_pos), rename) in x.0.iter() { + Stmt::Export(list, _) => { + for ((id, id_pos), rename) in list.iter() { // Mark scope variables as public if let Some(index) = scope.get_index(id).map(|(i, _)| i) { let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id); @@ -2094,9 +2079,7 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] - Stmt::Share(x) => { - let (var_name, _) = x.as_ref(); - + Stmt::Share(var_name, _) => { match scope.get_index(var_name) { Some((index, ScopeEntryType::Normal)) => { let (val, _) = scope.get_mut(index); diff --git a/src/optimize.rs b/src/optimize.rs index 404a0497..9584277f 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -163,87 +163,87 @@ fn call_fn_with_constant_arguments( /// Optimize a statement. fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { match stmt { + // if false { if_block } -> Noop + Stmt::IfThenElse(Expr::False(pos), _, None, _) => { + state.set_dirty(); + Stmt::Noop(pos) + } + // if true { if_block } -> if_block + Stmt::IfThenElse(Expr::True(_), if_block, None, _) => optimize_stmt(*if_block, state, true), // if expr { Noop } - Stmt::IfThenElse(x) if matches!(x.1, Stmt::Noop(_)) && x.2.is_none() => { + Stmt::IfThenElse(condition, if_block, None, _) + if matches!(if_block.as_ref(), Stmt::Noop(_)) => + { state.set_dirty(); - let pos = x.0.position(); - let expr = optimize_expr(x.0, state); + let pos = condition.position(); + let expr = optimize_expr(condition, state); if preserve_result { // -> { expr, Noop } - let mut statements = StaticVec::new(); - statements.push(Stmt::Expr(Box::new(expr))); - statements.push(x.1); + let mut statements = Vec::new(); + statements.push(Stmt::Expr(expr)); + statements.push(*if_block); - Stmt::Block(Box::new((statements, pos))) + Stmt::Block(statements, pos) } else { // -> expr - Stmt::Expr(Box::new(expr)) + Stmt::Expr(expr) } } // if expr { if_block } - Stmt::IfThenElse(x) if x.2.is_none() => match x.0 { - // if false { if_block } -> Noop - Expr::False(pos) => { - state.set_dirty(); - Stmt::Noop(pos) - } - // if true { if_block } -> if_block - Expr::True(_) => optimize_stmt(x.1, state, true), - // if expr { if_block } - expr => Stmt::IfThenElse(Box::new(( - optimize_expr(expr, state), - optimize_stmt(x.1, state, true), - None, - x.3, - ))), - }, + Stmt::IfThenElse(condition, if_block, None, pos) => Stmt::IfThenElse( + optimize_expr(condition, state), + Box::new(optimize_stmt(*if_block, state, true)), + None, + pos, + ), + // if false { if_block } else { else_block } -> else_block + Stmt::IfThenElse(Expr::False(_), _, Some(else_block), _) => { + optimize_stmt(*else_block, state, true) + } + // if true { if_block } else { else_block } -> if_block + Stmt::IfThenElse(Expr::True(_), if_block, _, _) => optimize_stmt(*if_block, state, true), // if expr { if_block } else { else_block } - Stmt::IfThenElse(x) if x.2.is_some() => match x.0 { - // if false { if_block } else { else_block } -> else_block - Expr::False(_) => optimize_stmt(x.2.unwrap(), state, true), - // if true { if_block } else { else_block } -> if_block - Expr::True(_) => optimize_stmt(x.1, state, true), - // if expr { if_block } else { else_block } - expr => Stmt::IfThenElse(Box::new(( - optimize_expr(expr, state), - optimize_stmt(x.1, state, true), - match optimize_stmt(x.2.unwrap(), state, true) { - Stmt::Noop(_) => None, // Noop -> no else block - stmt => Some(stmt), - }, - x.3, - ))), - }, + Stmt::IfThenElse(condition, if_block, Some(else_block), pos) => Stmt::IfThenElse( + optimize_expr(condition, state), + Box::new(optimize_stmt(*if_block, state, true)), + match optimize_stmt(*else_block, state, true) { + Stmt::Noop(_) => None, // Noop -> no else block + stmt => Some(Box::new(stmt)), + }, + pos, + ), + + // while false { block } -> Noop + Stmt::While(Expr::False(pos), _, _) => { + state.set_dirty(); + Stmt::Noop(pos) + } + // while true { block } -> loop { block } + Stmt::While(Expr::True(_), block, pos) => { + Stmt::Loop(Box::new(optimize_stmt(*block, state, false)), pos) + } // while expr { block } - Stmt::While(x) => match x.0 { - // while false { block } -> Noop - Expr::False(pos) => { - state.set_dirty(); - Stmt::Noop(pos) - } - // while true { block } -> loop { block } - Expr::True(_) => Stmt::Loop(Box::new((optimize_stmt(x.1, state, false), x.2))), - // while expr { block } - expr => match optimize_stmt(x.1, state, false) { + Stmt::While(condition, block, pos) => { + match optimize_stmt(*block, state, false) { // while expr { break; } -> { expr; } Stmt::Break(pos) => { // Only a single break statement - turn into running the guard expression once state.set_dirty(); - let mut statements = StaticVec::new(); - statements.push(Stmt::Expr(Box::new(optimize_expr(expr, state)))); + let mut statements = Vec::new(); + statements.push(Stmt::Expr(optimize_expr(condition, state))); if preserve_result { statements.push(Stmt::Noop(pos)) } - Stmt::Block(Box::new((statements, pos))) + Stmt::Block(statements, pos) } // while expr { block } - stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt, x.2))), - }, - }, + stmt => Stmt::While(optimize_expr(condition, state), Box::new(stmt), pos), + } + } // loop { block } - Stmt::Loop(x) => match optimize_stmt(x.0, state, false) { + Stmt::Loop(block, pos) => match optimize_stmt(*block, state, false) { // loop { break; } -> Noop Stmt::Break(pos) => { // Only a single break statement @@ -251,59 +251,50 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { Stmt::Noop(pos) } // loop { block } - stmt => Stmt::Loop(Box::new((stmt, x.1))), + stmt => Stmt::Loop(Box::new(stmt), pos), }, // for id in expr { block } - Stmt::For(x) => Stmt::For(Box::new(( - x.0, - optimize_expr(x.1, state), - optimize_stmt(x.2, state, false), - x.3, - ))), + Stmt::For(var_name, iterable, block, pos) => Stmt::For( + var_name, + optimize_expr(iterable, state), + Box::new(optimize_stmt(*block, state, false)), + pos, + ), // let id = expr; - Stmt::Let(x) if x.1.is_some() => Stmt::Let(Box::new(( - x.0, - Some(optimize_expr(x.1.unwrap(), state)), - x.2, - ))), + Stmt::Let(name, Some(expr), pos) => Stmt::Let(name, Some(optimize_expr(expr, state)), pos), // let id; - stmt @ Stmt::Let(_) => stmt, + stmt @ Stmt::Let(_, None, _) => stmt, // import expr as var; #[cfg(not(feature = "no_module"))] - Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1, x.2))), + Stmt::Import(expr, alias, pos) => Stmt::Import(optimize_expr(expr, state), alias, pos), // { block } - Stmt::Block(x) => { - let orig_len = x.0.len(); // Original number of statements in the block, for change detection + Stmt::Block(statements, pos) => { + let orig_len = statements.len(); // Original number of statements in the block, for change detection let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later - let pos = x.1; // Optimize each statement in the block - let mut result: Vec<_> = - x.0.into_iter() - .map(|stmt| match stmt { - // Add constant literals into the state - Stmt::Const(mut v) => { - if let Some(expr) = v.1 { - let expr = optimize_expr(expr, state); - - if expr.is_literal() { - state.set_dirty(); - state.push_constant(&(v.0).0, expr); - Stmt::Noop(pos) // No need to keep constants - } else { - v.1 = Some(expr); - Stmt::Const(v) - } - } else { - state.set_dirty(); - state.push_constant(&(v.0).0, Expr::Unit((v.0).1)); - Stmt::Noop(pos) // No need to keep constants - } - } - // Optimize the statement - _ => optimize_stmt(stmt, state, preserve_result), - }) - .collect(); + let mut result: Vec<_> = statements + .into_iter() + .map(|stmt| match stmt { + // Add constant literals into the state + Stmt::Const(name, Some(expr), pos) if expr.is_literal() => { + state.set_dirty(); + state.push_constant(&name.0, expr); + Stmt::Noop(pos) // No need to keep constants + } + Stmt::Const(name, Some(expr), pos) if expr.is_literal() => { + let expr = optimize_expr(expr, state); + Stmt::Const(name, Some(expr), pos) + } + Stmt::Const(name, None, pos) => { + state.set_dirty(); + state.push_constant(&name.0, Expr::Unit(name.1)); + Stmt::Noop(pos) // No need to keep constants + } + // Optimize the statement + stmt => optimize_stmt(stmt, state, preserve_result), + }) + .collect(); // Remove all raw expression statements that are pure except for the very last statement let last_stmt = if preserve_result { result.pop() } else { None }; @@ -321,9 +312,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { while let Some(expr) = result.pop() { match expr { - Stmt::Let(x) => removed = x.1.as_ref().map(Expr::is_pure).unwrap_or(true), + Stmt::Let(_, expr, _) => { + removed = expr.as_ref().map(Expr::is_pure).unwrap_or(true) + } #[cfg(not(feature = "no_module"))] - Stmt::Import(x) => removed = x.0.is_pure(), + Stmt::Import(expr, _, _) => removed = expr.is_pure(), _ => { result.push(expr); break; @@ -341,7 +334,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { .into_iter() .rev() .enumerate() - .map(|(i, s)| optimize_stmt(s, state, i == 0)) + .map(|(i, stmt)| optimize_stmt(stmt, state, i == 0)) .rev() .collect(); } @@ -355,7 +348,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { } match stmt { - Stmt::ReturnWithVal(_) | Stmt::Break(_) => dead_code = true, + Stmt::ReturnWithVal(_, _, _) | Stmt::Break(_) => dead_code = true, _ => (), } @@ -370,23 +363,23 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { // Pop the stack and remove all the local constants state.restore_constants(orig_constants_len); - match result[..] { + match &result[..] { // No statements in block - change to No-op [] => { state.set_dirty(); Stmt::Noop(pos) } // Only one let statement - leave it alone - [Stmt::Let(_)] => Stmt::Block(Box::new((result.into(), pos))), + [x] if matches!(x, Stmt::Let(_, _, _)) => Stmt::Block(result, pos), // Only one import statement - leave it alone #[cfg(not(feature = "no_module"))] - [Stmt::Import(_)] => Stmt::Block(Box::new((result.into(), pos))), + [x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(result, pos), // Only one statement - promote [_] => { state.set_dirty(); result.remove(0) } - _ => Stmt::Block(Box::new((result.into(), pos))), + _ => Stmt::Block(result, pos), } } // try { block } catch ( var ) { block } @@ -394,25 +387,26 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { // If try block is pure, there will never be any exceptions state.set_dirty(); let pos = (x.0).0.position(); - let mut statements: StaticVec<_> = Default::default(); + let mut statements: Vec<_> = Default::default(); statements.push(optimize_stmt((x.0).0, state, preserve_result)); statements.push(Stmt::Noop(pos)); - Stmt::Block(Box::new((statements, pos))) + Stmt::Block(statements, pos) } // try { block } catch ( var ) { block } - Stmt::TryCatch(x) => Stmt::TryCatch(Box::new(( - (optimize_stmt((x.0).0, state, false), (x.0).1), - x.1, - (optimize_stmt((x.2).0, state, false), (x.2).1), - ))), + Stmt::TryCatch(x) => { + let ((try_block, try_pos), var_name, (catch_block, catch_pos)) = *x; + Stmt::TryCatch(Box::new(( + (optimize_stmt(try_block, state, false), try_pos), + var_name, + (optimize_stmt(catch_block, state, false), catch_pos), + ))) + } // expr; - Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))), + Stmt::Expr(expr) => Stmt::Expr(optimize_expr(expr, state)), // return expr; - Stmt::ReturnWithVal(x) if x.1.is_some() => Stmt::ReturnWithVal(Box::new(( - x.0, - Some(optimize_expr(x.1.unwrap(), state)), - x.2, - ))), + Stmt::ReturnWithVal(ret, Some(expr), pos) => { + Stmt::ReturnWithVal(ret, Some(optimize_expr(expr, state)), pos) + } // All other statements - skip stmt => stmt, } @@ -442,7 +436,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { // ( expr ) -> expr Stmt::Expr(expr) => { state.set_dirty(); - *expr + expr } // ( stmt ) stmt => Expr::Stmt(Box::new((stmt, x.1))), @@ -748,35 +742,35 @@ fn optimize( .enumerate() .map(|(i, stmt)| { match stmt { - Stmt::Const(mut v) => { + Stmt::Const(var_def, Some(expr), pos) => { // Load constants - if let Some(expr) = v.1 { - let expr = optimize_expr(expr, &mut state); + let expr = optimize_expr(expr, &mut state); - if expr.is_literal() { - state.push_constant(&(v.0).0, expr.clone()); - } - - v.1 = if expr.is_unit() { - state.set_dirty(); - None - } else { - Some(expr) - }; - } else { - state.push_constant(&(v.0).0, Expr::Unit((v.0).1)); + if expr.is_literal() { + state.push_constant(&var_def.0, expr.clone()); } // Keep it in the global scope - Stmt::Const(v) + if expr.is_unit() { + state.set_dirty(); + Stmt::Const(var_def, None, pos) + } else { + Stmt::Const(var_def, Some(expr), pos) + } + } + Stmt::Const(ref var_def, None, _) => { + state.push_constant(&var_def.0, Expr::Unit(var_def.1)); + + // Keep it in the global scope + stmt } _ => { // Keep all variable declarations at this level // and always keep the last return value let keep = match stmt { - Stmt::Let(_) => true, + Stmt::Let(_, _, _) => true, #[cfg(not(feature = "no_module"))] - Stmt::Import(_) => true, + Stmt::Import(_, _, _) => true, _ => i == num_statements - 1, }; optimize_stmt(stmt, &mut state, keep) @@ -859,16 +853,12 @@ pub fn optimize_into_ast( // {} -> Noop fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { // { return val; } -> val - Stmt::ReturnWithVal(x) - if x.1.is_some() && (x.0).0 == ReturnType::Return => - { - Stmt::Expr(Box::new(x.1.unwrap())) + Stmt::ReturnWithVal((ReturnType::Return, _), Some(expr), _) => { + Stmt::Expr(expr) } // { return; } -> () - Stmt::ReturnWithVal(x) - if x.1.is_none() && (x.0).0 == ReturnType::Return => - { - Stmt::Expr(Box::new(Expr::Unit((x.0).1))) + Stmt::ReturnWithVal((ReturnType::Return, pos), None, _) => { + Stmt::Expr(Expr::Unit(pos)) } // All others stmt => stmt, diff --git a/src/parser.rs b/src/parser.rs index 503fd1eb..251cb799 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -742,19 +742,19 @@ pub enum Stmt { /// No-op. Noop(Position), /// if expr { stmt } else { stmt } - IfThenElse(Box<(Expr, Stmt, Option, Position)>), + IfThenElse(Expr, Box, Option>, Position), /// while expr { stmt } - While(Box<(Expr, Stmt, Position)>), + While(Expr, Box, Position), /// loop { stmt } - Loop(Box<(Stmt, Position)>), + Loop(Box, Position), /// for id in expr { stmt } - For(Box<(String, Expr, Stmt, Position)>), + For(Box, Expr, Box, Position), /// let id = expr - Let(Box<((String, Position), Option, Position)>), + Let(Box<(String, Position)>, Option, Position), /// const id = expr - Const(Box<((String, Position), Option, Position)>), + Const(Box<(String, Position)>, Option, Position), /// { stmt; ... } - Block(Box<(StaticVec, Position)>), + Block(Vec, Position), /// try { stmt; ... } catch ( var ) { stmt; ... } TryCatch( Box<( @@ -764,27 +764,25 @@ pub enum Stmt { )>, ), /// expr - Expr(Box), + Expr(Expr), /// continue Continue(Position), /// break Break(Position), /// return/throw - ReturnWithVal(Box<((ReturnType, Position), Option, Position)>), + ReturnWithVal((ReturnType, Position), Option, Position), /// import expr as var #[cfg(not(feature = "no_module"))] - Import(Box<(Expr, Option<(ImmutableString, Position)>, Position)>), + Import(Expr, Option<(ImmutableString, Position)>, Position), /// export var as var, ... #[cfg(not(feature = "no_module"))] Export( - Box<( - StaticVec<((String, Position), Option<(String, Position)>)>, - Position, - )>, + Vec<((String, Position), Option<(String, Position)>)>, + Position, ), /// Convert a variable to shared. #[cfg(not(feature = "no_closure"))] - Share(Box<(String, Position)>), + Share(String, Position), } impl Default for Stmt { @@ -806,52 +804,58 @@ impl Stmt { /// Get the `Position` of this statement. pub fn position(&self) -> Position { match self { - Self::Noop(pos) | Self::Continue(pos) | Self::Break(pos) => *pos, - Self::Let(x) => (x.0).1, - Self::Const(x) => (x.0).1, - Self::Block(x) => x.1, - Self::IfThenElse(x) => x.3, - Self::Expr(x) => x.position(), - Self::While(x) => x.2, - Self::Loop(x) => x.1, - Self::For(x) => x.3, - Self::ReturnWithVal(x) => (x.0).1, + Self::Noop(pos) + | Self::Continue(pos) + | Self::Break(pos) + | Self::Block(_, pos) + | Self::IfThenElse(_, _, _, pos) + | Self::While(_, _, pos) + | Self::Loop(_, pos) + | Self::For(_, _, _, pos) + | Self::ReturnWithVal((_, pos), _, _) => *pos, + + Self::Let(x, _, _) | Self::Const(x, _, _) => x.1, Self::TryCatch(x) => (x.0).1, + Self::Expr(x) => x.position(), + #[cfg(not(feature = "no_module"))] - Self::Import(x) => x.2, + Self::Import(_, _, pos) => *pos, #[cfg(not(feature = "no_module"))] - Self::Export(x) => x.1, + Self::Export(_, pos) => *pos, #[cfg(not(feature = "no_closure"))] - Self::Share(x) => x.1, + Self::Share(_, pos) => *pos, } } /// Override the `Position` of this statement. pub fn set_position(&mut self, new_pos: Position) -> &mut Self { match self { - Self::Noop(pos) | Self::Continue(pos) | Self::Break(pos) => *pos = new_pos, - Self::Let(x) => (x.0).1 = new_pos, - Self::Const(x) => (x.0).1 = new_pos, - Self::Block(x) => x.1 = new_pos, - Self::IfThenElse(x) => x.3 = new_pos, + Self::Noop(pos) + | Self::Continue(pos) + | Self::Break(pos) + | Self::Block(_, pos) + | Self::IfThenElse(_, _, _, pos) + | Self::While(_, _, pos) + | Self::Loop(_, pos) + | Self::For(_, _, _, pos) + | Self::ReturnWithVal((_, pos), _, _) => *pos = new_pos, + + Self::Let(x, _, _) | Self::Const(x, _, _) => x.1 = new_pos, + Self::TryCatch(x) => (x.0).1 = new_pos, + Self::Expr(x) => { x.set_position(new_pos); } - Self::While(x) => x.2 = new_pos, - Self::Loop(x) => x.1 = new_pos, - Self::For(x) => x.3 = new_pos, - Self::ReturnWithVal(x) => (x.0).1 = new_pos, - Self::TryCatch(x) => (x.0).1 = new_pos, #[cfg(not(feature = "no_module"))] - Self::Import(x) => x.2 = new_pos, + Self::Import(_, _, pos) => *pos = new_pos, #[cfg(not(feature = "no_module"))] - Self::Export(x) => x.1 = new_pos, + Self::Export(_, pos) => *pos = new_pos, #[cfg(not(feature = "no_closure"))] - Self::Share(x) => x.1 = new_pos, + Self::Share(_, pos) => *pos = new_pos, } self @@ -860,28 +864,28 @@ impl Stmt { /// Is this statement self-terminated (i.e. no need for a semicolon terminator)? pub fn is_self_terminated(&self) -> bool { match self { - Self::IfThenElse(_) - | Self::While(_) - | Self::Loop(_) - | Self::For(_) - | Self::Block(_) + Self::IfThenElse(_, _, _, _) + | Self::While(_, _, _) + | Self::Loop(_, _) + | Self::For(_, _, _, _) + | Self::Block(_, _) | Self::TryCatch(_) => true, // A No-op requires a semicolon in order to know it is an empty statement! Self::Noop(_) => false, - Self::Let(_) - | Self::Const(_) + Self::Let(_, _, _) + | Self::Const(_, _, _) | Self::Expr(_) | Self::Continue(_) | Self::Break(_) - | Self::ReturnWithVal(_) => false, + | Self::ReturnWithVal(_, _, _) => false, #[cfg(not(feature = "no_module"))] - Self::Import(_) | Self::Export(_) => false, + Self::Import(_, _, _) | Self::Export(_, _) => false, #[cfg(not(feature = "no_closure"))] - Self::Share(_) => false, + Self::Share(_, _) => false, } } @@ -890,25 +894,27 @@ impl Stmt { match self { Self::Noop(_) => true, Self::Expr(expr) => expr.is_pure(), - Self::IfThenElse(x) if x.2.is_some() => { - x.0.is_pure() && x.1.is_pure() && x.2.as_ref().unwrap().is_pure() + Self::IfThenElse(condition, if_block, Some(else_block), _) => { + condition.is_pure() && if_block.is_pure() && else_block.is_pure() } - Self::IfThenElse(x) => x.1.is_pure(), - Self::While(x) => x.0.is_pure() && x.1.is_pure(), - Self::Loop(x) => x.0.is_pure(), - Self::For(x) => x.1.is_pure() && x.2.is_pure(), - Self::Let(_) | Self::Const(_) => false, - Self::Block(x) => x.0.iter().all(Self::is_pure), - Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_) => false, + Self::IfThenElse(condition, if_block, None, _) => { + condition.is_pure() && if_block.is_pure() + } + Self::While(condition, block, _) => condition.is_pure() && block.is_pure(), + Self::Loop(block, _) => block.is_pure(), + Self::For(_, iterable, block, _) => iterable.is_pure() && block.is_pure(), + Self::Let(_, _, _) | Self::Const(_, _, _) => false, + Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), + Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false, Self::TryCatch(x) => (x.0).0.is_pure() && (x.2).0.is_pure(), #[cfg(not(feature = "no_module"))] - Self::Import(_) => false, + Self::Import(_, _, _) => false, #[cfg(not(feature = "no_module"))] - Self::Export(_) => false, + Self::Export(_, _) => false, #[cfg(not(feature = "no_closure"))] - Self::Share(_) => false, + Self::Share(_, _) => false, } } } @@ -2859,24 +2865,22 @@ fn parse_if( ensure_not_statement_expr(input, "a boolean")?; let guard = parse_expr(input, state, lib, settings.level_up())?; ensure_not_assignment(input)?; - let if_body = parse_block(input, state, lib, settings.level_up())?; + let if_body = Box::new(parse_block(input, state, lib, settings.level_up())?); // if guard { if_body } else ... let else_body = if match_token(input, Token::Else).0 { - Some(if let (Token::If, _) = input.peek().unwrap() { + Some(Box::new(if let (Token::If, _) = input.peek().unwrap() { // if guard { if_body } else if ... parse_if(input, state, lib, settings.level_up())? } else { // if guard { if_body } else { else-body } parse_block(input, state, lib, settings.level_up())? - }) + })) } else { None }; - Ok(Stmt::IfThenElse(Box::new(( - guard, if_body, else_body, token_pos, - )))) + Ok(Stmt::IfThenElse(guard, if_body, else_body, token_pos)) } /// Parse a while loop. @@ -2899,9 +2903,9 @@ fn parse_while( ensure_not_assignment(input)?; settings.is_breakable = true; - let body = parse_block(input, state, lib, settings.level_up())?; + let body = Box::new(parse_block(input, state, lib, settings.level_up())?); - Ok(Stmt::While(Box::new((guard, body, token_pos)))) + Ok(Stmt::While(guard, body, token_pos)) } /// Parse a loop statement. @@ -2920,9 +2924,9 @@ fn parse_loop( // loop { body } settings.is_breakable = true; - let body = parse_block(input, state, lib, settings.level_up())?; + let body = Box::new(parse_block(input, state, lib, settings.level_up())?); - Ok(Stmt::Loop(Box::new((body, token_pos)))) + Ok(Stmt::Loop(body, token_pos)) } /// Parse a for loop. @@ -2973,11 +2977,11 @@ fn parse_for( state.stack.push((name.clone(), ScopeEntryType::Normal)); settings.is_breakable = true; - let body = parse_block(input, state, lib, settings.level_up())?; + let body = Box::new(parse_block(input, state, lib, settings.level_up())?); state.stack.truncate(prev_stack_len); - Ok(Stmt::For(Box::new((name, expr, body, token_pos)))) + Ok(Stmt::For(Box::new(name), expr, body, token_pos)) } /// Parse a variable definition statement. @@ -3017,12 +3021,12 @@ fn parse_let( // let name = expr ScopeEntryType::Normal => { state.stack.push((name.clone(), ScopeEntryType::Normal)); - Ok(Stmt::Let(Box::new(((name, pos), init_value, token_pos)))) + Ok(Stmt::Let(Box::new((name, pos)), init_value, token_pos)) } // const name = { expr:constant } ScopeEntryType::Constant => { state.stack.push((name.clone(), ScopeEntryType::Constant)); - Ok(Stmt::Const(Box::new(((name, pos), init_value, token_pos)))) + Ok(Stmt::Const(Box::new((name, pos)), init_value, token_pos)) } } } @@ -3047,7 +3051,7 @@ fn parse_import( // import expr as ... if !match_token(input, Token::As).0 { - return Ok(Stmt::Import(Box::new((expr, None, token_pos)))); + return Ok(Stmt::Import(expr, None, token_pos)); } // import expr as name ... @@ -3062,11 +3066,11 @@ fn parse_import( state.modules.push(name.clone()); - Ok(Stmt::Import(Box::new(( + Ok(Stmt::Import( expr, Some((name.into(), settings.pos)), token_pos, - )))) + )) } /// Parse an export statement. @@ -3083,7 +3087,7 @@ fn parse_export( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(_state.max_expr_depth)?; - let mut exports = StaticVec::new(); + let mut exports = Vec::new(); loop { let (id, id_pos) = match input.next().unwrap() { @@ -3138,7 +3142,7 @@ fn parse_export( }) .map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?; - Ok(Stmt::Export(Box::new((exports, token_pos)))) + Ok(Stmt::Export(exports, token_pos)) } /// Parse a statement block. @@ -3164,7 +3168,7 @@ fn parse_block( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let mut statements = StaticVec::new(); + let mut statements = Vec::new(); let prev_stack_len = state.stack.len(); #[cfg(not(feature = "no_module"))] @@ -3217,7 +3221,7 @@ fn parse_block( #[cfg(not(feature = "no_module"))] state.modules.truncate(prev_mods_len); - Ok(Stmt::Block(Box::new((statements, settings.pos)))) + Ok(Stmt::Block(statements, settings.pos)) } /// Parse an expression as a statement. @@ -3234,7 +3238,7 @@ fn parse_expr_stmt( let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_op_assignment_stmt(input, state, lib, expr, settings.level_up())?; - Ok(Stmt::Expr(Box::new(expr))) + Ok(Stmt::Expr(expr)) } /// Parse a single statement. @@ -3345,26 +3349,26 @@ fn parse_stmt( match input.peek().unwrap() { // `return`/`throw` at - (Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(Box::new(( + (Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal( (return_type, token_pos), None, *pos, - ))))), + ))), // `return;` or `throw;` - (Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(Box::new(( + (Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal( (return_type, token_pos), None, settings.pos, - ))))), + ))), // `return` or `throw` with expression (_, _) => { let expr = parse_expr(input, state, lib, settings.level_up())?; let pos = expr.position(); - Ok(Some(Stmt::ReturnWithVal(Box::new(( + Ok(Some(Stmt::ReturnWithVal( (return_type, token_pos), Some(expr), pos, - ))))) + ))) } } } @@ -3598,12 +3602,16 @@ fn make_curry_from_externals( #[cfg(not(feature = "no_closure"))] { // Statement block - let mut statements: StaticVec<_> = Default::default(); + let mut statements: Vec<_> = Default::default(); // Insert `Share` statements - statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x)))); + statements.extend( + externals + .into_iter() + .map(|(var_name, pos)| Stmt::Share(var_name, pos)), + ); // Final expression - statements.push(Stmt::Expr(Box::new(expr))); - Expr::Stmt(Box::new((Stmt::Block(Box::new((statements, pos))), pos))) + statements.push(Stmt::Expr(expr)); + Expr::Stmt(Box::new((Stmt::Block(statements, pos), pos))) } #[cfg(feature = "no_closure")] @@ -3781,7 +3789,7 @@ impl Engine { } } - let expr = vec![Stmt::Expr(Box::new(expr))]; + let expr = vec![Stmt::Expr(expr)]; Ok( // Optimize AST @@ -3924,3 +3932,10 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { _ => None, } } + +#[test] +fn test() { + println!("{}", std::mem::size_of::()); + println!("{}", std::mem::size_of::()); + println!("{}", std::mem::size_of::()); +}