diff --git a/src/ast.rs b/src/ast.rs index 64fe39e8..1e9f593c 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -46,7 +46,7 @@ pub enum FnAccess { #[derive(Debug, Clone)] pub struct ScriptFnDef { /// Function body. - pub body: Stmt, + pub body: StmtBlock, /// Encapsulated running environment, if any. pub lib: Option>, /// Encapsulated imported modules. @@ -692,7 +692,7 @@ impl AST { .chain({ #[cfg(not(feature = "no_function"))] { - self.iter_fn_def().map(|f| &f.body) + self.iter_fn_def().flat_map(|f| f.body.statements.iter()) } #[cfg(feature = "no_function")] { @@ -862,8 +862,8 @@ pub enum Stmt { Switch( Expr, Box<( - HashableHashMap, - Option, + HashableHashMap, + StmtBlock, )>, Position, ), @@ -914,6 +914,7 @@ impl Default for Stmt { } impl From for StmtBlock { + #[inline(always)] fn from(stmt: Stmt) -> Self { match stmt { Stmt::Block(block, pos) => Self { @@ -924,7 +925,11 @@ impl From for StmtBlock { statements: Default::default(), pos, }, - _ => panic!("cannot convert {:?} into a StmtBlock", stmt), + _ => { + let pos = stmt.position(); + let statements = vec![stmt].into(); + Self { statements, pos } + } } } } @@ -1043,8 +1048,11 @@ impl Stmt { } Self::Switch(expr, x, _) => { expr.is_pure() - && x.0.values().all(Stmt::is_pure) - && x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) + && x.0 + .values() + .flat_map(|block| block.statements.iter()) + .all(Stmt::is_pure) + && x.1.statements.iter().all(Stmt::is_pure) } Self::While(condition, block, _) | Self::Do(block, condition, _, _) => { condition.is_pure() && block.statements.iter().all(Stmt::is_pure) @@ -1083,10 +1091,10 @@ impl Stmt { } Self::Switch(e, x, _) => { e.walk(path, on_node); - x.0.values().for_each(|s| s.walk(path, on_node)); - if let Some(ref s) = x.1 { - s.walk(path, on_node); - } + x.0.values() + .flat_map(|block| block.statements.iter()) + .for_each(|s| s.walk(path, on_node)); + x.1.statements.iter().for_each(|s| s.walk(path, on_node)); } Self::While(e, s, _) | Self::Do(s, e, _, _) => { e.walk(path, on_node); @@ -1384,10 +1392,10 @@ pub enum Expr { Unit(Position), /// Variable access - (optional index, optional (hash, modules), variable name) Variable(Box<(Option, Option<(u64, NamespaceRef)>, Ident)>), - /// Property access - (getter, hash, setter, hash, prop) - Property(Box<(ImmutableString, u64, ImmutableString, u64, Ident)>), + /// Property access - ((getter, hash), (setter, hash), prop) + Property(Box<((ImmutableString, u64), (ImmutableString, u64), Ident)>), /// { [statement][Stmt] ... } - Stmt(Box>, Position), + Stmt(Box), /// func `(` expr `,` ... `)` FnCall(Box, Position), /// lhs `.` rhs @@ -1478,8 +1486,8 @@ impl Expr { Self::FnPointer(_, pos) => *pos, Self::Array(_, pos) => *pos, Self::Map(_, pos) => *pos, - Self::Property(x) => (x.4).pos, - Self::Stmt(_, pos) => *pos, + Self::Property(x) => (x.2).pos, + Self::Stmt(x) => x.pos, Self::Variable(x) => (x.2).pos, Self::FnCall(_, pos) => *pos, @@ -1508,8 +1516,8 @@ impl Expr { Self::Array(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos, Self::Variable(x) => (x.2).pos = new_pos, - Self::Property(x) => (x.4).pos = new_pos, - Self::Stmt(_, pos) => *pos = new_pos, + Self::Property(x) => (x.2).pos = new_pos, + Self::Stmt(x) => x.pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos, Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos, Self::Unit(pos) => *pos = new_pos, @@ -1533,7 +1541,7 @@ impl Expr { x.lhs.is_pure() && x.rhs.is_pure() } - Self::Stmt(x, _) => x.iter().all(Stmt::is_pure), + Self::Stmt(x) => x.statements.iter().all(Stmt::is_pure), Self::Variable(_) => true, @@ -1596,7 +1604,7 @@ impl Expr { Self::StringConstant(_, _) | Self::FnCall(_, _) - | Self::Stmt(_, _) + | Self::Stmt(_) | Self::Dot(_, _) | Self::Index(_, _) | Self::Array(_, _) @@ -1632,7 +1640,7 @@ impl Expr { on_node(path); match self { - Self::Stmt(x, _) => x.iter().for_each(|s| s.walk(path, on_node)), + Self::Stmt(x) => x.statements.iter().for_each(|s| s.walk(path, on_node)), Self::Array(x, _) => x.iter().for_each(|e| e.walk(path, on_node)), Self::Map(x, _) => x.iter().for_each(|(_, e)| e.walk(path, on_node)), Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => { diff --git a/src/engine.rs b/src/engine.rs index 297570ff..973cf56b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1181,7 +1181,7 @@ impl Engine { } // {xxx:map}.id op= ??? Expr::Property(x) if target_val.is::() && new_val.is_some() => { - let Ident { name, pos, .. } = &x.4; + let Ident { name, pos, .. } = &x.2; let index = name.clone().into(); let val = self.get_indexed_mut( mods, state, lib, target_val, index, *pos, true, is_ref, false, level, @@ -1194,7 +1194,7 @@ impl Engine { } // {xxx:map}.id Expr::Property(x) if target_val.is::() => { - let Ident { name, pos, .. } = &x.4; + let Ident { name, pos, .. } = &x.2; let index = name.clone().into(); let val = self.get_indexed_mut( mods, state, lib, target_val, index, *pos, false, is_ref, false, level, @@ -1204,7 +1204,7 @@ impl Engine { } // xxx.id = ??? Expr::Property(x) if new_val.is_some() => { - let (_, _, setter, hash_set, Ident { pos, .. }) = x.as_ref(); + let (_, (setter, hash_set), Ident { pos, .. }) = x.as_ref(); let hash = FnHash::from_native(*hash_set); let mut new_val = new_val; let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0]; @@ -1216,7 +1216,7 @@ impl Engine { } // xxx.id Expr::Property(x) => { - let (getter, hash_get, _, _, Ident { pos, .. }) = x.as_ref(); + let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref(); let hash = FnHash::from_native(*hash_get); let mut args = [target_val]; self.exec_fn_call( @@ -1229,7 +1229,7 @@ impl Engine { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::() => { let mut val = match &x.lhs { Expr::Property(p) => { - let Ident { name, pos, .. } = &p.4; + let Ident { name, pos, .. } = &p.2; let index = name.clone().into(); self.get_indexed_mut( mods, state, lib, target_val, index, *pos, false, is_ref, true, @@ -1264,7 +1264,7 @@ impl Engine { match &x.lhs { // xxx.prop[expr] | xxx.prop.expr Expr::Property(p) => { - let (getter, hash_get, setter, hash_set, Ident { pos, .. }) = + let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = p.as_ref(); let hash_get = FnHash::from_native(*hash_get); let hash_set = FnHash::from_native(*hash_set); @@ -1456,7 +1456,7 @@ impl Engine { } Expr::Property(x) if parent_chain_type == ChainType::Dot => { - idx_values.push(ChainArgument::Property(x.4.pos)) + idx_values.push(ChainArgument::Property(x.2.pos)) } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), @@ -1466,7 +1466,7 @@ impl Engine { // Evaluate in left-to-right order let lhs_val = match lhs { Expr::Property(x) if parent_chain_type == ChainType::Dot => { - ChainArgument::Property(x.4.pos) + ChainArgument::Property(x.2.pos) } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::FnCall(x, _) @@ -1655,16 +1655,11 @@ impl Engine { .map(|(val, _)| val.take_or_clone()), // Statement block - Expr::Stmt(x, _) => self.eval_stmt_block( - scope, - mods, - state, - lib, - this_ptr, - x.as_ref().as_ref(), - true, - level, - ), + Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), + Expr::Stmt(x) => { + let statements = &x.statements; + self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level) + } // lhs[idx_expr] #[cfg(not(feature = "no_index"))] @@ -1804,6 +1799,10 @@ impl Engine { restore_prev_state: bool, level: usize, ) -> RhaiResult { + if statements.is_empty() { + return Ok(Dynamic::UNIT); + } + let mut _extra_fn_resolution_cache = false; let prev_always_search = state.always_search; let prev_scope_len = scope.len(); @@ -2025,6 +2024,7 @@ impl Engine { } // Block scope + Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT), Stmt::Block(statements, _) => { self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level) } @@ -2046,15 +2046,21 @@ impl Engine { .map_err(|err| self.make_type_mismatch_err::(err, expr.position())) .and_then(|guard_val| { if guard_val { - self.eval_stmt_block( - scope, mods, state, lib, this_ptr, if_stmt, true, level, - ) - } else if !else_stmt.is_empty() { - self.eval_stmt_block( - scope, mods, state, lib, this_ptr, else_stmt, true, level, - ) + if !if_stmt.is_empty() { + self.eval_stmt_block( + scope, mods, state, lib, this_ptr, if_stmt, true, level, + ) + } else { + Ok(Dynamic::UNIT) + } } else { - Ok(Dynamic::UNIT) + if !else_stmt.is_empty() { + self.eval_stmt_block( + scope, mods, state, lib, this_ptr, else_stmt, true, level, + ) + } else { + Ok(Dynamic::UNIT) + } } }) } @@ -2070,21 +2076,29 @@ impl Engine { value.hash(hasher); let hash = hasher.finish(); - table - .get(&hash) - .map(|stmt| self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)) + table.get(&hash).map(|StmtBlock { statements, .. }| { + if !statements.is_empty() { + self.eval_stmt_block( + scope, mods, state, lib, this_ptr, statements, true, level, + ) + } else { + Ok(Dynamic::UNIT) + } + }) } else { // Non-hashable values never match any specific clause None } .unwrap_or_else(|| { // Default match clause - def_stmt.as_ref().map_or_else( - || Ok(Dynamic::UNIT), - |def_stmt| { - self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level) - }, - ) + let def_stmt = &def_stmt.statements; + if !def_stmt.is_empty() { + self.eval_stmt_block( + scope, mods, state, lib, this_ptr, def_stmt, true, level, + ) + } else { + Ok(Dynamic::UNIT) + } }) } @@ -2102,20 +2116,22 @@ impl Engine { true }; - if condition { - match self - .eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) - { - Ok(_) => (), - Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), - _ => return Err(err), - }, - } - } else { + if !condition { return Ok(Dynamic::UNIT); } + if body.is_empty() { + continue; + } + + match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) + { + Ok(_) => (), + Err(err) => match *err { + EvalAltResult::LoopBreak(false, _) => (), + EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), + _ => return Err(err), + }, + } } } @@ -2124,14 +2140,17 @@ impl Engine { let body = &body.statements; loop { - match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) - { - Ok(_) => (), - Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => continue, - EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), - _ => return Err(err), - }, + if !body.is_empty() { + match self + .eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) + { + Ok(_) => (), + Err(err) => match *err { + EvalAltResult::LoopBreak(false, _) => continue, + EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), + _ => return Err(err), + }, + } } if self @@ -2203,6 +2222,10 @@ impl Engine { self.inc_operations(state, *pos)?; + if statements.is_empty() { + continue; + } + match self.eval_stmt_block( scope, mods, state, lib, this_ptr, statements, true, level, ) { diff --git a/src/fn_call.rs b/src/fn_call.rs index 8a959115..8a008ee1 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -493,6 +493,10 @@ impl Engine { self.inc_operations(state, pos)?; + if fn_def.body.is_empty() { + return Ok(Dynamic::UNIT); + } + // Check for stack overflow #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "unchecked"))] @@ -539,10 +543,10 @@ impl Engine { } // Evaluate the function - let stmt = &fn_def.body; + let body = &fn_def.body.statements; let result = self - .eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level) + .eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level) .or_else(|err| match *err { // Convert return statement to return value EvalAltResult::Return(x, _) => Ok(x), @@ -722,6 +726,10 @@ impl Engine { let func = func.get_fn_def(); + if func.body.is_empty() { + return Ok((Dynamic::UNIT, false)); + } + let scope: &mut Scope = &mut Default::default(); // Move captured variables into scope @@ -1391,22 +1399,27 @@ impl Engine { match func { #[cfg(not(feature = "no_function"))] Some(f) if f.is_script() => { - let args = args.as_mut(); - let new_scope = &mut Default::default(); - let fn_def = f.get_fn_def().clone(); + let fn_def = f.get_fn_def(); - let mut source = module.id_raw().cloned(); - mem::swap(&mut state.source, &mut source); + if fn_def.body.is_empty() { + Ok(Dynamic::UNIT) + } else { + let args = args.as_mut(); + let new_scope = &mut Default::default(); - let level = level + 1; + let mut source = module.id_raw().cloned(); + mem::swap(&mut state.source, &mut source); - let result = self.call_script_fn( - new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level, - ); + let level = level + 1; - state.source = source; + let result = self.call_script_fn( + new_scope, mods, state, lib, &mut None, fn_def, args, pos, level, + ); - result + state.source = source; + + result + } } Some(f) if f.is_plugin_fn() => f diff --git a/src/optimize.rs b/src/optimize.rs index b70f72a1..07dc1d48 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,6 +1,6 @@ //! Module implementing the [`AST`] optimizer. -use crate::ast::{Expr, Ident, Stmt}; +use crate::ast::{Expr, Ident, Stmt, StmtBlock}; use crate::dynamic::AccessMode; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::fn_builtin::get_builtin_binary_op_fn; @@ -362,29 +362,54 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { let table = &mut x.0; - if let Some(stmt) = table.get_mut(&hash) { - optimize_stmt(stmt, state, true); - *expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos); - } else if let Some(def_stmt) = x.1.as_mut() { - optimize_stmt(def_stmt, state, true); - *expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos); + let (statements, new_pos) = if let Some(block) = table.get_mut(&hash) { + ( + optimize_stmt_block( + mem::take(&mut block.statements).into_vec(), + block.pos, + state, + true, + ) + .into(), + block.pos, + ) } else { - *expr = Expr::Unit(*pos); - } + ( + optimize_stmt_block( + mem::take(&mut x.1.statements).into_vec(), + x.1.pos, + state, + true, + ) + .into(), + if x.1.pos.is_none() { *pos } else { x.1.pos }, + ) + }; + + *expr = Expr::Stmt(Box::new(StmtBlock { + statements, + pos: new_pos, + })); } // switch Stmt::Switch(expr, x, _) => { optimize_expr(expr, state); - x.0.values_mut() - .for_each(|stmt| optimize_stmt(stmt, state, preserve_result)); - if let Some(def_stmt) = x.1.as_mut() { - optimize_stmt(def_stmt, state, preserve_result); - - match def_stmt { - Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None, - _ => (), - } - } + x.0.values_mut().for_each(|block| { + block.statements = optimize_stmt_block( + mem::take(&mut block.statements).into_vec(), + block.pos, + state, + preserve_result, + ) + .into() + }); + x.1.statements = optimize_stmt_block( + mem::take(&mut x.1.statements).into_vec(), + x.1.pos, + state, + preserve_result, + ) + .into() } // while false { block } -> Noop @@ -512,14 +537,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { .into(); } // {} - Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => { + Stmt::Expr(Expr::Stmt(x)) if x.statements.is_empty() => { state.set_dirty(); - *stmt = Stmt::Noop(*pos); + *stmt = Stmt::Noop(x.pos); } // {...}; - Stmt::Expr(Expr::Stmt(x, pos)) => { + Stmt::Expr(Expr::Stmt(x)) => { state.set_dirty(); - *stmt = Stmt::Block(mem::take(x).into_vec(), *pos); + *stmt = Stmt::Block(mem::take(&mut x.statements).into_vec(), x.pos); } // expr; Stmt::Expr(expr) => optimize_expr(expr, state), @@ -542,18 +567,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { match expr { // {} - Expr::Stmt(x, pos) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(*pos) } + Expr::Stmt(x) if x.statements.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.pos) } // { stmt; ... } - do not count promotion as dirty because it gets turned back into an array - Expr::Stmt(x, pos) => { - let statements = optimize_stmt_block(mem::take(x).into_vec(), *pos, state, true); - *expr = Expr::Stmt(Box::new(statements.into()), *pos); - } + Expr::Stmt(x) => x.statements = optimize_stmt_block(mem::take(&mut x.statements).into_vec(), x.pos, state, true).into(), // lhs.rhs #[cfg(not(feature = "no_object"))] Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) { // map.string (Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => { - let prop = &p.4.name; + let prop = &p.2.name; // Map literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); @@ -902,30 +924,34 @@ pub fn optimize_into_ast( _functions .into_iter() .map(|mut fn_def| { - let pos = fn_def.body.position(); + let pos = fn_def.body.pos; // Optimize the function body let mut body = optimize_top_level( - vec![fn_def.body], + fn_def.body.statements.into_vec(), engine, &Scope::new(), &[&lib2], level, ); - // {} -> Noop - fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { + match &mut body[..] { // { return val; } -> val - Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _) => { - Stmt::Expr(expr) + [Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _)] => { + body[0] = Stmt::Expr(mem::take(expr)) } // { return; } -> () - Stmt::Return(crate::ast::ReturnType::Return, None, pos) => { - Stmt::Expr(Expr::Unit(pos)) + [Stmt::Return(crate::ast::ReturnType::Return, None, _)] => { + body.clear(); } - // All others - stmt => stmt, + _ => (), + } + + fn_def.body = StmtBlock { + statements: body.into(), + pos, }; + fn_def }) .for_each(|fn_def| { diff --git a/src/parser.rs b/src/parser.rs index 1c19ab8c..db15c06a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -243,7 +243,11 @@ impl Expr { let setter = state.get_interned_string(crate::engine::make_setter(&ident.name)); let hash_set = calc_fn_hash(empty(), &setter, 2); - Self::Property(Box::new((getter, hash_get, setter, hash_set, ident.into()))) + Self::Property(Box::new(( + (getter, hash_get), + (setter, hash_set), + ident.into(), + ))) } _ => self, } @@ -809,7 +813,7 @@ fn parse_switch( } } - let mut table = HashMap::new(); + let mut table = HashMap::::new(); let mut def_stmt = None; loop { @@ -869,10 +873,10 @@ fn parse_switch( let need_comma = !stmt.is_self_terminated(); def_stmt = if let Some(hash) = hash { - table.insert(hash, stmt); + table.insert(hash, stmt.into()); None } else { - Some(stmt) + Some(stmt.into()) }; match input.peek().unwrap() { @@ -903,7 +907,10 @@ fn parse_switch( Ok(Stmt::Switch( item, - Box::new((final_table.into(), def_stmt)), + Box::new(( + final_table.into(), + def_stmt.unwrap_or_else(|| Stmt::Noop(Position::NONE).into()), + )), settings.pos, )) } @@ -954,7 +961,7 @@ fn parse_primary( // { - block statement as expression Token::LeftBrace if settings.allow_stmt_expr => { match parse_block(input, state, lib, settings.level_up())? { - Stmt::Block(statements, pos) => Expr::Stmt(Box::new(statements.into()), pos), + block @ Stmt::Block(_, _) => Expr::Stmt(Box::new(block.into())), stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt), } } @@ -962,15 +969,14 @@ fn parse_primary( Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?, // If statement is allowed to act as expressions - Token::If if settings.allow_if_expr => Expr::Stmt( - Box::new(vec![parse_if(input, state, lib, settings.level_up())?].into()), - settings.pos, - ), + Token::If if settings.allow_if_expr => Expr::Stmt(Box::new( + parse_if(input, state, lib, settings.level_up())?.into(), + )), // Switch statement is allowed to act as expressions - Token::Switch if settings.allow_switch_expr => Expr::Stmt( - Box::new(vec![parse_switch(input, state, lib, settings.level_up())?].into()), - settings.pos, - ), + Token::Switch if settings.allow_switch_expr => Expr::Stmt(Box::new( + parse_switch(input, state, lib, settings.level_up())?.into(), + )), + // | ... #[cfg(not(feature = "no_function"))] Token::Pipe | Token::Or if settings.allow_anonymous_fn => { @@ -1506,7 +1512,7 @@ fn make_dot_expr( let setter = state.get_interned_string(crate::engine::make_setter(&ident.name)); let hash_set = calc_fn_hash(empty(), &setter, 2); - let rhs = Expr::Property(Box::new((getter, hash_get, setter, hash_set, ident))); + let rhs = Expr::Property(Box::new(((getter, hash_get), (setter, hash_set), ident))); Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) } @@ -1861,8 +1867,8 @@ fn parse_custom_syntax( tokens.push(keyword); } MARKER_BLOCK => match parse_block(input, state, lib, settings)? { - Stmt::Block(statements, pos) => { - keywords.push(Expr::Stmt(Box::new(statements.into()), pos)); + block @ Stmt::Block(_, _) => { + keywords.push(Expr::Stmt(Box::new(block.into()))); let keyword = state.get_interned_string(MARKER_BLOCK); segments.push(keyword.clone()); tokens.push(keyword); @@ -2006,19 +2012,9 @@ fn parse_if( Stmt::Noop(Position::NONE) }; - let else_body = match else_body { - Stmt::If(_, _, pos) => { - let mut statements: StaticVec<_> = Default::default(); - statements.push(else_body); - StmtBlock { statements, pos } - } - Stmt::Block(_, _) | Stmt::Noop(_) => else_body.into(), - _ => unreachable!("should either be if or a block, not {:?}", else_body), - }; - Ok(Stmt::If( guard, - Box::new((if_body.into(), else_body)), + Box::new((if_body.into(), else_body.into())), settings.pos, )) } @@ -2741,7 +2737,8 @@ fn parse_fn( parse_block(input, state, lib, settings.level_up())? } (_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)), - }; + } + .into(); let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect(); @@ -2803,7 +2800,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po let mut statements: StaticVec<_> = Default::default(); statements.extend(externals.into_iter().map(Stmt::Share)); statements.push(Stmt::Expr(expr)); - Expr::Stmt(Box::new(statements), pos) + Expr::Stmt(Box::new(StmtBlock { statements, pos })) } /// Parse an anonymous function definition. @@ -2905,7 +2902,7 @@ fn parse_anon_fn( params, #[cfg(not(feature = "no_closure"))] externals: Default::default(), - body, + body: body.into(), lib: None, #[cfg(not(feature = "no_module"))] mods: Default::default(),