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,
),
/// `while` expr `{` stmt `}`
While(Expr, Box<Stmt>, Position),
While(Option<Expr>, Box<Stmt>, Position),
/// `do` `{` stmt `}` `while`|`until` expr
Do(Box<Stmt>, 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);

View File

@ -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::<bool>(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::<bool>(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::<bool>(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::<bool>(err, expr.position()))
if !*is_while {
return Ok(Dynamic::UNIT);
}
} else {
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
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);
};
}
_ => (),
}

View File

@ -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;