diff --git a/src/ast.rs b/src/ast.rs index 13127fe4..06d3b714 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -823,7 +823,7 @@ pub enum Stmt { Position, ), /// `while` expr `{` stmt `}` - While(Expr, Box, Position), + While(Option, Box, Position), /// `do` `{` stmt `}` `while`|`until` expr Do(Box, Expr, bool, Position), /// `for` id `in` expr `{` stmt `}` @@ -981,9 +981,10 @@ impl Stmt { && x.0.values().all(Stmt::is_pure) && x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) } - Self::While(condition, block, _) | Self::Do(block, condition, _, _) => { + Self::While(Some(condition), block, _) | Self::Do(block, condition, _, _) => { condition.is_pure() && block.is_pure() } + Self::While(None, block, _) => block.is_pure(), Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(), Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), @@ -1021,10 +1022,11 @@ impl Stmt { s.walk(path, on_node); } } - Self::While(e, s, _) | Self::Do(s, e, _, _) => { + Self::While(Some(e), s, _) | Self::Do(s, e, _, _) => { e.walk(path, on_node); s.walk(path, on_node); } + Self::While(None, s, _) => s.walk(path, on_node), Self::For(e, x, _) => { e.walk(path, on_node); x.1.walk(path, on_node); diff --git a/src/engine.rs b/src/engine.rs index 8c710c69..113150f9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2138,24 +2138,25 @@ impl Engine { // While loop Stmt::While(expr, body, _) => loop { - match self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? - .as_bool() - { - Ok(true) => { - match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) { - Ok(_) => (), - Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), - _ => return Err(err), - }, - } - } - Ok(false) => return Ok(Dynamic::UNIT), - Err(err) => { - return Err(self.make_type_mismatch_err::(err, expr.position())) + let condition = if let Some(expr) = expr { + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .as_bool() + .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))? + } else { + true + }; + + if condition { + match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) { + Ok(_) => (), + Err(err) => match *err { + EvalAltResult::LoopBreak(false, _) => (), + EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), + _ => return Err(err), + }, } + } else { + return Ok(Dynamic::UNIT); } }, @@ -2170,15 +2171,17 @@ impl Engine { }, } - match self + if self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() + .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))? { - Ok(true) if !*is_while => return Ok(Dynamic::UNIT), - Ok(false) if *is_while => return Ok(Dynamic::UNIT), - Ok(_) => (), - Err(err) => { - return Err(self.make_type_mismatch_err::(err, expr.position())) + if !*is_while { + return Ok(Dynamic::UNIT); + } + } else { + if *is_while { + return Ok(Dynamic::UNIT); } } }, diff --git a/src/optimize.rs b/src/optimize.rs index cff6580b..2f367a05 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -393,25 +393,32 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { } // while false { block } -> Noop - Stmt::While(Expr::BoolConstant(false, pos), _, _) => { + Stmt::While(Some(Expr::BoolConstant(false, pos)), _, _) => { state.set_dirty(); *stmt = Stmt::Noop(*pos) } // while expr { block } Stmt::While(condition, block, _) => { optimize_stmt(block, state, false); - optimize_expr(condition, state); + + if let Some(condition) = condition { + optimize_expr(condition, state); + } match **block { // 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 = vec![Stmt::Expr(mem::take(condition))]; - if preserve_result { - statements.push(Stmt::Noop(pos)) - } - *stmt = Stmt::Block(statements, pos); + if let Some(condition) = condition { + let mut statements = vec![Stmt::Expr(mem::take(condition))]; + if preserve_result { + statements.push(Stmt::Noop(pos)) + } + *stmt = Stmt::Block(statements, pos); + } else { + *stmt = Stmt::Noop(pos); + }; } _ => (), } diff --git a/src/parser.rs b/src/parser.rs index 96e1da4e..cb549a41 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2122,9 +2122,10 @@ fn parse_while_loop( let (guard, token_pos) = match input.next().unwrap() { (Token::While, pos) => { ensure_not_statement_expr(input, "a boolean")?; - (parse_expr(input, state, lib, settings.level_up())?, pos) + let expr = parse_expr(input, state, lib, settings.level_up())?; + (Some(expr), pos) } - (Token::Loop, pos) => (Expr::BoolConstant(true, pos), pos), + (Token::Loop, pos) => (None, pos), (t, _) => unreachable!("expecting Token::While or Token::Loop, but gets {:?}", t), }; settings.pos = token_pos;