Skip evaluate condition for loop statement.

This commit is contained in:
Stephen Chung 2021-03-06 16:05:22 +08:00
parent e14bef4b10
commit a126d05c3f
4 changed files with 48 additions and 35 deletions

View File

@ -823,7 +823,7 @@ pub enum Stmt {
Position, Position,
), ),
/// `while` expr `{` stmt `}` /// `while` expr `{` stmt `}`
While(Expr, Box<Stmt>, Position), While(Option<Expr>, Box<Stmt>, Position),
/// `do` `{` stmt `}` `while`|`until` expr /// `do` `{` stmt `}` `while`|`until` expr
Do(Box<Stmt>, Expr, bool, Position), Do(Box<Stmt>, Expr, bool, Position),
/// `for` id `in` expr `{` stmt `}` /// `for` id `in` expr `{` stmt `}`
@ -981,9 +981,10 @@ impl Stmt {
&& x.0.values().all(Stmt::is_pure) && x.0.values().all(Stmt::is_pure)
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) && 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() 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::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false, Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
@ -1021,10 +1022,11 @@ impl Stmt {
s.walk(path, on_node); 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); e.walk(path, on_node);
s.walk(path, on_node); s.walk(path, on_node);
} }
Self::While(None, s, _) => s.walk(path, on_node),
Self::For(e, x, _) => { Self::For(e, x, _) => {
e.walk(path, on_node); e.walk(path, on_node);
x.1.walk(path, on_node); x.1.walk(path, on_node);

View File

@ -2138,24 +2138,25 @@ impl Engine {
// While loop // While loop
Stmt::While(expr, body, _) => loop { Stmt::While(expr, body, _) => loop {
match self let condition = if let Some(expr) = expr {
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
{ .map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?
Ok(true) => { } else {
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) { true
Ok(_) => (), };
Err(err) => match *err {
EvalAltResult::LoopBreak(false, _) => (), if condition {
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
_ => return Err(err), Ok(_) => (),
}, Err(err) => match *err {
} EvalAltResult::LoopBreak(false, _) => (),
} EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
Ok(false) => return Ok(Dynamic::UNIT), _ => return Err(err),
Err(err) => { },
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
} }
} 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)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?
{ {
Ok(true) if !*is_while => return Ok(Dynamic::UNIT), if !*is_while {
Ok(false) if *is_while => return Ok(Dynamic::UNIT), return Ok(Dynamic::UNIT);
Ok(_) => (), }
Err(err) => { } else {
return Err(self.make_type_mismatch_err::<bool>(err, expr.position())) if *is_while {
return Ok(Dynamic::UNIT);
} }
} }
}, },

View File

@ -393,25 +393,32 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
} }
// while false { block } -> Noop // while false { block } -> Noop
Stmt::While(Expr::BoolConstant(false, pos), _, _) => { Stmt::While(Some(Expr::BoolConstant(false, pos)), _, _) => {
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(*pos) *stmt = Stmt::Noop(*pos)
} }
// while expr { block } // while expr { block }
Stmt::While(condition, block, _) => { Stmt::While(condition, block, _) => {
optimize_stmt(block, state, false); optimize_stmt(block, state, false);
optimize_expr(condition, state);
if let Some(condition) = condition {
optimize_expr(condition, state);
}
match **block { match **block {
// while expr { break; } -> { expr; } // while expr { break; } -> { expr; }
Stmt::Break(pos) => { Stmt::Break(pos) => {
// Only a single break statement - turn into running the guard expression once // Only a single break statement - turn into running the guard expression once
state.set_dirty(); state.set_dirty();
let mut statements = vec![Stmt::Expr(mem::take(condition))]; if let Some(condition) = condition {
if preserve_result { let mut statements = vec![Stmt::Expr(mem::take(condition))];
statements.push(Stmt::Noop(pos)) if preserve_result {
} statements.push(Stmt::Noop(pos))
*stmt = Stmt::Block(statements, pos); }
*stmt = Stmt::Block(statements, pos);
} else {
*stmt = Stmt::Noop(pos);
};
} }
_ => (), _ => (),
} }

View File

@ -2122,9 +2122,10 @@ fn parse_while_loop(
let (guard, token_pos) = match input.next().unwrap() { let (guard, token_pos) = match input.next().unwrap() {
(Token::While, pos) => { (Token::While, pos) => {
ensure_not_statement_expr(input, "a boolean")?; 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), (t, _) => unreachable!("expecting Token::While or Token::Loop, but gets {:?}", t),
}; };
settings.pos = token_pos; settings.pos = token_pos;