do loop.
This commit is contained in:
parent
b34e7840b0
commit
6069a4cf55
@ -14,11 +14,13 @@ Breaking changes
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Module::set_fn`, `Module::set_raw_fn` and `Module::set_fn_XXX_mut` all take an additional parameter of `FnNamespace`.
|
* `Module::set_fn`, `Module::set_raw_fn` and `Module::set_fn_XXX_mut` all take an additional parameter of `FnNamespace`.
|
||||||
|
* `unless` is now a reserved keyword.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* `switch` statement.
|
* `switch` statement.
|
||||||
|
* `do ... while` and `do ... until` statement.
|
||||||
* `Engine::register_module` to register a module as a sub-module in the global namespace.
|
* `Engine::register_module` to register a module as a sub-module in the global namespace.
|
||||||
* `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace.
|
* `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace.
|
||||||
* `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type.
|
* `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type.
|
||||||
|
@ -79,6 +79,7 @@ The Rhai Scripting Language
|
|||||||
9. [If Statement](language/if.md)
|
9. [If Statement](language/if.md)
|
||||||
10. [Switch Expression](language/switch.md)
|
10. [Switch Expression](language/switch.md)
|
||||||
11. [While Loop](language/while.md)
|
11. [While Loop](language/while.md)
|
||||||
|
11. [Do Loop](language/do.md)
|
||||||
12. [Loop Statement](language/loop.md)
|
12. [Loop Statement](language/loop.md)
|
||||||
13. [For Loop](language/for.md)
|
13. [For Loop](language/for.md)
|
||||||
14. [Return Values](language/return.md)
|
14. [Return Values](language/return.md)
|
||||||
|
@ -14,7 +14,9 @@ Keywords List
|
|||||||
| `if` | if statement | | no | |
|
| `if` | if statement | | no | |
|
||||||
| `else` | else block of if statement | | no | |
|
| `else` | else block of if statement | | no | |
|
||||||
| `switch` | matching | | no | |
|
| `switch` | matching | | no | |
|
||||||
| `while` | while loop | | no | |
|
| `do` | looping | | no | |
|
||||||
|
| `while` | 1) while loop<br/>2) condition for do loop | | no | |
|
||||||
|
| `until` | do loop | | no | |
|
||||||
| `loop` | infinite loop | | no | |
|
| `loop` | infinite loop | | no | |
|
||||||
| `for` | for loop | | no | |
|
| `for` | for loop | | no | |
|
||||||
| `in` | 1) containment test<br/>2) part of for loop | | no | |
|
| `in` | 1) containment test<br/>2) part of for loop | | no | |
|
||||||
@ -48,11 +50,11 @@ Reserved Keywords
|
|||||||
| `var` | variable declaration |
|
| `var` | variable declaration |
|
||||||
| `static` | variable declaration |
|
| `static` | variable declaration |
|
||||||
| `shared` | share value |
|
| `shared` | share value |
|
||||||
| `do` | looping |
|
|
||||||
| `each` | looping |
|
| `each` | looping |
|
||||||
| `then` | control flow |
|
| `then` | control flow |
|
||||||
| `goto` | control flow |
|
| `goto` | control flow |
|
||||||
| `exit` | control flow |
|
| `exit` | control flow |
|
||||||
|
| `unless` | control flow |
|
||||||
| `match` | matching |
|
| `match` | matching |
|
||||||
| `case` | matching |
|
| `case` | matching |
|
||||||
| `public` | function/field access |
|
| `public` | function/field access |
|
||||||
|
28
doc/src/language/do.md
Normal file
28
doc/src/language/do.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
`do` Loop
|
||||||
|
=========
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
`do` loops have two opposite variants: `do` ... `while` and `do` ... `until`.
|
||||||
|
|
||||||
|
Like the `while` loop, `continue` can be used to skip to the next iteration, by-passing all following statements;
|
||||||
|
`break` can be used to break out of the loop unconditionally.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 10;
|
||||||
|
|
||||||
|
do {
|
||||||
|
x -= 1;
|
||||||
|
if x < 6 { continue; } // skip to the next iteration
|
||||||
|
print(x);
|
||||||
|
if x == 5 { break; } // break out of do loop
|
||||||
|
} while x > 0;
|
||||||
|
|
||||||
|
|
||||||
|
do {
|
||||||
|
x -= 1;
|
||||||
|
if x < 6 { continue; } // skip to the next iteration
|
||||||
|
print(x);
|
||||||
|
if x == 5 { break; } // break out of do loop
|
||||||
|
} until x == 0;
|
||||||
|
```
|
17
src/ast.rs
17
src/ast.rs
@ -601,8 +601,8 @@ pub enum Stmt {
|
|||||||
),
|
),
|
||||||
/// `while` expr `{` stmt `}`
|
/// `while` expr `{` stmt `}`
|
||||||
While(Expr, Box<Stmt>, Position),
|
While(Expr, Box<Stmt>, Position),
|
||||||
/// `loop` `{` stmt `}`
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
Loop(Box<Stmt>, Position),
|
Do(Box<Stmt>, Expr, bool, Position),
|
||||||
/// `for` id `in` expr `{` stmt `}`
|
/// `for` id `in` expr `{` stmt `}`
|
||||||
For(Expr, Box<(String, Stmt)>, Position),
|
For(Expr, Box<(String, Stmt)>, Position),
|
||||||
/// \[`export`\] `let` id `=` expr
|
/// \[`export`\] `let` id `=` expr
|
||||||
@ -660,7 +660,7 @@ impl Stmt {
|
|||||||
| Self::If(_, _, pos)
|
| Self::If(_, _, pos)
|
||||||
| Self::Switch(_, _, pos)
|
| Self::Switch(_, _, pos)
|
||||||
| Self::While(_, _, pos)
|
| Self::While(_, _, pos)
|
||||||
| Self::Loop(_, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::Return((_, pos), _, _)
|
| Self::Return((_, pos), _, _)
|
||||||
| Self::Let(_, _, _, pos)
|
| Self::Let(_, _, _, pos)
|
||||||
@ -689,7 +689,7 @@ impl Stmt {
|
|||||||
| Self::If(_, _, pos)
|
| Self::If(_, _, pos)
|
||||||
| Self::Switch(_, _, pos)
|
| Self::Switch(_, _, pos)
|
||||||
| Self::While(_, _, pos)
|
| Self::While(_, _, pos)
|
||||||
| Self::Loop(_, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::Return((_, pos), _, _)
|
| Self::Return((_, pos), _, _)
|
||||||
| Self::Let(_, _, _, pos)
|
| Self::Let(_, _, _, pos)
|
||||||
@ -717,7 +717,6 @@ impl Stmt {
|
|||||||
Self::If(_, _, _)
|
Self::If(_, _, _)
|
||||||
| Self::Switch(_, _, _)
|
| Self::Switch(_, _, _)
|
||||||
| Self::While(_, _, _)
|
| Self::While(_, _, _)
|
||||||
| Self::Loop(_, _)
|
|
||||||
| Self::For(_, _, _)
|
| Self::For(_, _, _)
|
||||||
| Self::Block(_, _)
|
| Self::Block(_, _)
|
||||||
| Self::TryCatch(_, _, _) => true,
|
| Self::TryCatch(_, _, _) => true,
|
||||||
@ -729,6 +728,7 @@ impl Stmt {
|
|||||||
| Self::Const(_, _, _, _)
|
| Self::Const(_, _, _, _)
|
||||||
| Self::Assignment(_, _)
|
| Self::Assignment(_, _)
|
||||||
| Self::Expr(_)
|
| Self::Expr(_)
|
||||||
|
| Self::Do(_, _, _, _)
|
||||||
| Self::Continue(_)
|
| Self::Continue(_)
|
||||||
| Self::Break(_)
|
| Self::Break(_)
|
||||||
| Self::Return(_, _, _) => false,
|
| Self::Return(_, _, _) => false,
|
||||||
@ -737,7 +737,7 @@ impl Stmt {
|
|||||||
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(_) => false,
|
Self::Share(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this statement _pure_?
|
/// Is this statement _pure_?
|
||||||
@ -755,8 +755,9 @@ 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, _) => condition.is_pure() && block.is_pure(),
|
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
||||||
Self::Loop(block, _) => block.is_pure(),
|
condition.is_pure() && 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()),
|
||||||
|
@ -1028,7 +1028,7 @@ impl Engine {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Default::default())
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// xxx[rhs]
|
// xxx[rhs]
|
||||||
_ => {
|
_ => {
|
||||||
@ -1193,7 +1193,7 @@ impl Engine {
|
|||||||
|err| match *err {
|
|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because the property is read-only
|
// If there is no setter, no need to feed it back because the property is read-only
|
||||||
EvalAltResult::ErrorDotExpr(_, _) => {
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
Ok(Default::default())
|
Ok((Dynamic::UNIT, false))
|
||||||
}
|
}
|
||||||
_ => Err(err.fill_position(*x_pos)),
|
_ => Err(err.fill_position(*x_pos)),
|
||||||
},
|
},
|
||||||
@ -1902,7 +1902,7 @@ impl Engine {
|
|||||||
|
|
||||||
let result = match stmt {
|
let result = match stmt {
|
||||||
// No-op
|
// No-op
|
||||||
Stmt::Noop(_) => Ok(Default::default()),
|
Stmt::Noop(_) => Ok(Dynamic::UNIT),
|
||||||
|
|
||||||
// Expression as statement
|
// Expression as statement
|
||||||
Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level),
|
Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level),
|
||||||
@ -1935,7 +1935,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
*lhs_ptr.as_mut() = rhs_val;
|
*lhs_ptr.as_mut() = rhs_val;
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
// Op-assignment - in order of precedence:
|
// Op-assignment - in order of precedence:
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
@ -2003,7 +2003,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2045,7 +2045,7 @@ impl Engine {
|
|||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||||
)?;
|
)?;
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
// dot_lhs.dot_rhs op= rhs
|
// dot_lhs.dot_rhs op= rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -2053,7 +2053,7 @@ impl Engine {
|
|||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||||
)?;
|
)?;
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
// Non-lvalue expression (should be caught during parsing)
|
// Non-lvalue expression (should be caught during parsing)
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -2077,7 +2077,7 @@ impl Engine {
|
|||||||
} else if let Some(stmt) = else_block {
|
} else if let Some(stmt) = else_block {
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||||
} else {
|
} else {
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2115,28 +2115,40 @@ impl Engine {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::LoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => (),
|
||||||
EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()),
|
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(false) => return Ok(Default::default()),
|
Ok(false) => return Ok(Dynamic::UNIT),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Loop statement
|
// Do loop
|
||||||
Stmt::Loop(block, _) => loop {
|
Stmt::Do(body, expr, is_while, _) => loop {
|
||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, block, level) {
|
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::LoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => continue,
|
||||||
EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()),
|
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
|
.as_bool()
|
||||||
|
{
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
@ -2187,7 +2199,7 @@ impl Engine {
|
|||||||
|
|
||||||
state.scope_level -= 1;
|
state.scope_level -= 1;
|
||||||
scope.rewind(scope.len() - 1);
|
scope.rewind(scope.len() - 1);
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
EvalAltResult::ErrorFor(expr.position()).into()
|
EvalAltResult::ErrorFor(expr.position()).into()
|
||||||
}
|
}
|
||||||
@ -2312,7 +2324,7 @@ impl Engine {
|
|||||||
if let Some(alias) = _alias {
|
if let Some(alias) = _alias {
|
||||||
scope.add_entry_alias(scope.len() - 1, alias);
|
scope.add_entry_alias(scope.len() - 1, alias);
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import statement
|
// Import statement
|
||||||
@ -2344,7 +2356,7 @@ impl Engine {
|
|||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
|
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
Err(
|
Err(
|
||||||
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
|
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
|
||||||
@ -2369,7 +2381,7 @@ impl Engine {
|
|||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Share statement
|
// Share statement
|
||||||
@ -2386,7 +2398,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ fn optimize_stmt_block(
|
|||||||
fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||||
match stmt {
|
match stmt {
|
||||||
// expr op= expr
|
// expr op= expr
|
||||||
Stmt::Assignment(ref mut x, _) => match x.0 {
|
Stmt::Assignment(x, _) => match x.0 {
|
||||||
Expr::Variable(_) => optimize_expr(&mut x.2, state),
|
Expr::Variable(_) => optimize_expr(&mut x.2, state),
|
||||||
_ => {
|
_ => {
|
||||||
optimize_expr(&mut x.0, state);
|
optimize_expr(&mut x.0, state);
|
||||||
@ -290,7 +290,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
optimize_stmt(stmt, state, true);
|
optimize_stmt(stmt, state, true);
|
||||||
}
|
}
|
||||||
// if expr { Noop }
|
// if expr { Noop }
|
||||||
Stmt::If(ref mut condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => {
|
Stmt::If(condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
let pos = condition.position();
|
let pos = condition.position();
|
||||||
@ -309,7 +309,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// if expr { if_block }
|
// if expr { if_block }
|
||||||
Stmt::If(ref mut condition, ref mut x, _) if x.1.is_none() => {
|
Stmt::If(condition, x, _) if x.1.is_none() => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
optimize_stmt(&mut x.0, state, true);
|
optimize_stmt(&mut x.0, state, true);
|
||||||
}
|
}
|
||||||
@ -324,7 +324,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
optimize_stmt(stmt, state, true);
|
optimize_stmt(stmt, state, true);
|
||||||
}
|
}
|
||||||
// if expr { if_block } else { else_block }
|
// if expr { if_block } else { else_block }
|
||||||
Stmt::If(ref mut condition, ref mut x, _) => {
|
Stmt::If(condition, x, _) => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
optimize_stmt(&mut x.0, state, true);
|
optimize_stmt(&mut x.0, state, true);
|
||||||
if let Some(else_block) = x.1.as_mut() {
|
if let Some(else_block) = x.1.as_mut() {
|
||||||
@ -377,11 +377,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(*pos)
|
*stmt = Stmt::Noop(*pos)
|
||||||
}
|
}
|
||||||
// while true { block } -> loop { block }
|
|
||||||
Stmt::While(Expr::BoolConstant(true, _), block, pos) => {
|
|
||||||
optimize_stmt(block, state, false);
|
|
||||||
*stmt = Stmt::Loop(Box::new(mem::take(block)), *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);
|
||||||
@ -402,32 +397,30 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// loop { block }
|
// do { block } while false | do { block } until true -> { block }
|
||||||
Stmt::Loop(block, _) => {
|
Stmt::Do(block, Expr::BoolConstant(true, _), false, _)
|
||||||
optimize_stmt(block, state, false);
|
| Stmt::Do(block, Expr::BoolConstant(false, _), true, _) => {
|
||||||
|
|
||||||
match **block {
|
|
||||||
// loop { break; } -> Noop
|
|
||||||
Stmt::Break(pos) => {
|
|
||||||
// Only a single break statement
|
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(pos)
|
optimize_stmt(block.as_mut(), state, false);
|
||||||
}
|
*stmt = mem::take(block.as_mut());
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
// do { block } while|until expr
|
||||||
|
Stmt::Do(block, condition, _, _) => {
|
||||||
|
optimize_stmt(block.as_mut(), state, false);
|
||||||
|
optimize_expr(condition, state);
|
||||||
}
|
}
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(ref mut iterable, ref mut x, _) => {
|
Stmt::For(iterable, x, _) => {
|
||||||
optimize_expr(iterable, state);
|
optimize_expr(iterable, state);
|
||||||
optimize_stmt(&mut x.1, state, false);
|
optimize_stmt(&mut x.1, state, false);
|
||||||
}
|
}
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Let(_, Some(ref mut expr), _, _) => optimize_expr(expr, state),
|
Stmt::Let(_, Some(expr), _, _) => optimize_expr(expr, state),
|
||||||
// let id;
|
// let id;
|
||||||
Stmt::Let(_, None, _, _) => (),
|
Stmt::Let(_, None, _, _) => (),
|
||||||
// import expr as var;
|
// import expr as var;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(ref mut expr, _, _) => optimize_expr(expr, state),
|
Stmt::Import(expr, _, _) => optimize_expr(expr, state),
|
||||||
// { block }
|
// { block }
|
||||||
Stmt::Block(statements, pos) => {
|
Stmt::Block(statements, pos) => {
|
||||||
*stmt = optimize_stmt_block(mem::take(statements), *pos, state, preserve_result, true);
|
*stmt = optimize_stmt_block(mem::take(statements), *pos, state, preserve_result, true);
|
||||||
@ -446,7 +439,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
*stmt = Stmt::Block(statements, pos);
|
*stmt = Stmt::Block(statements, pos);
|
||||||
}
|
}
|
||||||
// try { block } catch ( var ) { block }
|
// try { block } catch ( var ) { block }
|
||||||
Stmt::TryCatch(ref mut x, _, _) => {
|
Stmt::TryCatch(x, _, _) => {
|
||||||
optimize_stmt(&mut x.0, state, false);
|
optimize_stmt(&mut x.0, state, false);
|
||||||
optimize_stmt(&mut x.2, state, false);
|
optimize_stmt(&mut x.2, state, false);
|
||||||
}
|
}
|
||||||
@ -461,7 +454,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
*stmt = Stmt::Block(mem::take(x).into_vec(), *pos);
|
*stmt = Stmt::Block(mem::take(x).into_vec(), *pos);
|
||||||
}
|
}
|
||||||
// expr;
|
// expr;
|
||||||
Stmt::Expr(ref mut expr) => optimize_expr(expr, state),
|
Stmt::Expr(expr) => optimize_expr(expr, state),
|
||||||
// return expr;
|
// return expr;
|
||||||
Stmt::Return(_, Some(ref mut expr), _) => optimize_expr(expr, state),
|
Stmt::Return(_, Some(ref mut expr), _) => optimize_expr(expr, state),
|
||||||
|
|
||||||
|
@ -1970,49 +1970,68 @@ fn parse_if(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a while loop.
|
/// Parse a while loop.
|
||||||
fn parse_while(
|
fn parse_while_loop(
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
mut settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// while ...
|
|
||||||
let token_pos = eat_token(input, Token::While);
|
|
||||||
settings.pos = token_pos;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
// while guard { body }
|
// while|loops ...
|
||||||
|
let (guard, token_pos) = match input.next().unwrap() {
|
||||||
|
(Token::While, pos) => {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = parse_expr(input, state, lib, settings.level_up())?;
|
(parse_expr(input, state, lib, settings.level_up())?, pos)
|
||||||
ensure_not_assignment(input)?;
|
}
|
||||||
|
(Token::Loop, pos) => (Expr::BoolConstant(true, pos), pos),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
settings.pos = token_pos;
|
||||||
|
|
||||||
|
ensure_not_assignment(input)?;
|
||||||
settings.is_breakable = true;
|
settings.is_breakable = true;
|
||||||
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
||||||
|
|
||||||
Ok(Stmt::While(guard, body, token_pos))
|
Ok(Stmt::While(guard, body, token_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a loop statement.
|
/// Parse a do loop.
|
||||||
fn parse_loop(
|
fn parse_do(
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
mut settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// loop ...
|
// do ...
|
||||||
let token_pos = eat_token(input, Token::Loop);
|
let token_pos = eat_token(input, Token::Do);
|
||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
// loop { body }
|
// do { body } [while|until] guard
|
||||||
settings.is_breakable = true;
|
settings.is_breakable = true;
|
||||||
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
||||||
|
|
||||||
Ok(Stmt::Loop(body, token_pos))
|
let is_while = match input.next().unwrap() {
|
||||||
|
(Token::While, _) => true,
|
||||||
|
(Token::Until, _) => false,
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken(Token::While.into(), "for the do statement".into())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
|
settings.is_breakable = false;
|
||||||
|
let guard = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
|
ensure_not_assignment(input)?;
|
||||||
|
|
||||||
|
Ok(Stmt::Do(body, guard, is_while, token_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a for loop.
|
/// Parse a for loop.
|
||||||
@ -2413,8 +2432,10 @@ fn parse_stmt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Token::If => parse_if(input, state, lib, settings.level_up()).map(Some),
|
Token::If => parse_if(input, state, lib, settings.level_up()).map(Some),
|
||||||
Token::While => parse_while(input, state, lib, settings.level_up()).map(Some),
|
Token::While | Token::Loop => {
|
||||||
Token::Loop => parse_loop(input, state, lib, settings.level_up()).map(Some),
|
parse_while_loop(input, state, lib, settings.level_up()).map(Some)
|
||||||
|
}
|
||||||
|
Token::Do => parse_do(input, state, lib, settings.level_up()).map(Some),
|
||||||
Token::For => parse_for(input, state, lib, settings.level_up()).map(Some),
|
Token::For => parse_for(input, state, lib, settings.level_up()).map(Some),
|
||||||
|
|
||||||
Token::Continue if settings.is_breakable => {
|
Token::Continue if settings.is_breakable => {
|
||||||
|
18
src/token.rs
18
src/token.rs
@ -232,8 +232,12 @@ pub enum Token {
|
|||||||
Else,
|
Else,
|
||||||
/// `switch`
|
/// `switch`
|
||||||
Switch,
|
Switch,
|
||||||
|
/// `do`
|
||||||
|
Do,
|
||||||
/// `while`
|
/// `while`
|
||||||
While,
|
While,
|
||||||
|
/// `until`
|
||||||
|
Until,
|
||||||
/// `loop`
|
/// `loop`
|
||||||
Loop,
|
Loop,
|
||||||
/// `for`
|
/// `for`
|
||||||
@ -380,7 +384,9 @@ impl Token {
|
|||||||
If => "if",
|
If => "if",
|
||||||
Else => "else",
|
Else => "else",
|
||||||
Switch => "switch",
|
Switch => "switch",
|
||||||
|
Do => "do",
|
||||||
While => "while",
|
While => "while",
|
||||||
|
Until => "until",
|
||||||
Loop => "loop",
|
Loop => "loop",
|
||||||
For => "for",
|
For => "for",
|
||||||
In => "in",
|
In => "in",
|
||||||
@ -467,7 +473,9 @@ impl Token {
|
|||||||
"if" => If,
|
"if" => If,
|
||||||
"else" => Else,
|
"else" => Else,
|
||||||
"switch" => Switch,
|
"switch" => Switch,
|
||||||
|
"do" => Do,
|
||||||
"while" => While,
|
"while" => While,
|
||||||
|
"until" => Until,
|
||||||
"loop" => Loop,
|
"loop" => Loop,
|
||||||
"for" => For,
|
"for" => For,
|
||||||
"in" => In,
|
"in" => In,
|
||||||
@ -524,8 +532,8 @@ impl Token {
|
|||||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||||
|
|
||||||
"===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new"
|
"===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new"
|
||||||
| "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "do"
|
| "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "each"
|
||||||
| "each" | "then" | "goto" | "exit" | "match" | "case" | "default" | "void"
|
| "then" | "goto" | "unless" | "exit" | "match" | "case" | "default" | "void"
|
||||||
| "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => {
|
| "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => {
|
||||||
Reserved(syntax.into())
|
Reserved(syntax.into())
|
||||||
}
|
}
|
||||||
@ -586,7 +594,9 @@ impl Token {
|
|||||||
Ampersand |
|
Ampersand |
|
||||||
And |
|
And |
|
||||||
If |
|
If |
|
||||||
|
Do |
|
||||||
While |
|
While |
|
||||||
|
Until |
|
||||||
PlusAssign |
|
PlusAssign |
|
||||||
MinusAssign |
|
MinusAssign |
|
||||||
MultiplyAssign |
|
MultiplyAssign |
|
||||||
@ -690,8 +700,8 @@ impl Token {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import | Export | As => true,
|
Import | Export | As => true,
|
||||||
|
|
||||||
True | False | Let | Const | If | Else | While | Loop | For | In | Continue | Break
|
True | False | Let | Const | If | Else | Do | While | Until | Loop | For | In
|
||||||
| Return | Throw | Try | Catch => true,
|
| Continue | Break | Return | Throw | Try | Catch => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -24,3 +24,28 @@ fn test_while() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_do() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r"
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
x += 1;
|
||||||
|
if x > 5 { break; }
|
||||||
|
if x > 3 { continue; }
|
||||||
|
x += 3;
|
||||||
|
} while x < 10;
|
||||||
|
|
||||||
|
x
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user