Skip evaluate condition for loop statement.
This commit is contained in:
parent
e14bef4b10
commit
a126d05c3f
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user