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`.
|
||||
* `unless` is now a reserved keyword.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
* `switch` statement.
|
||||
* `do ... while` and `do ... until` statement.
|
||||
* `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.
|
||||
* `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)
|
||||
10. [Switch Expression](language/switch.md)
|
||||
11. [While Loop](language/while.md)
|
||||
11. [Do Loop](language/do.md)
|
||||
12. [Loop Statement](language/loop.md)
|
||||
13. [For Loop](language/for.md)
|
||||
14. [Return Values](language/return.md)
|
||||
|
@ -14,7 +14,9 @@ Keywords List
|
||||
| `if` | if statement | | no | |
|
||||
| `else` | else block of if statement | | 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 | |
|
||||
| `for` | for loop | | no | |
|
||||
| `in` | 1) containment test<br/>2) part of for loop | | no | |
|
||||
@ -48,11 +50,11 @@ Reserved Keywords
|
||||
| `var` | variable declaration |
|
||||
| `static` | variable declaration |
|
||||
| `shared` | share value |
|
||||
| `do` | looping |
|
||||
| `each` | looping |
|
||||
| `then` | control flow |
|
||||
| `goto` | control flow |
|
||||
| `exit` | control flow |
|
||||
| `unless` | control flow |
|
||||
| `match` | matching |
|
||||
| `case` | matching |
|
||||
| `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, Box<Stmt>, Position),
|
||||
/// `loop` `{` stmt `}`
|
||||
Loop(Box<Stmt>, Position),
|
||||
/// `do` `{` stmt `}` `while`|`until` expr
|
||||
Do(Box<Stmt>, Expr, bool, Position),
|
||||
/// `for` id `in` expr `{` stmt `}`
|
||||
For(Expr, Box<(String, Stmt)>, Position),
|
||||
/// \[`export`\] `let` id `=` expr
|
||||
@ -660,7 +660,7 @@ impl Stmt {
|
||||
| Self::If(_, _, pos)
|
||||
| Self::Switch(_, _, pos)
|
||||
| Self::While(_, _, pos)
|
||||
| Self::Loop(_, pos)
|
||||
| Self::Do(_, _, _, pos)
|
||||
| Self::For(_, _, pos)
|
||||
| Self::Return((_, pos), _, _)
|
||||
| Self::Let(_, _, _, pos)
|
||||
@ -689,7 +689,7 @@ impl Stmt {
|
||||
| Self::If(_, _, pos)
|
||||
| Self::Switch(_, _, pos)
|
||||
| Self::While(_, _, pos)
|
||||
| Self::Loop(_, pos)
|
||||
| Self::Do(_, _, _, pos)
|
||||
| Self::For(_, _, pos)
|
||||
| Self::Return((_, pos), _, _)
|
||||
| Self::Let(_, _, _, pos)
|
||||
@ -717,7 +717,6 @@ impl Stmt {
|
||||
Self::If(_, _, _)
|
||||
| Self::Switch(_, _, _)
|
||||
| Self::While(_, _, _)
|
||||
| Self::Loop(_, _)
|
||||
| Self::For(_, _, _)
|
||||
| Self::Block(_, _)
|
||||
| Self::TryCatch(_, _, _) => true,
|
||||
@ -729,6 +728,7 @@ impl Stmt {
|
||||
| Self::Const(_, _, _, _)
|
||||
| Self::Assignment(_, _)
|
||||
| Self::Expr(_)
|
||||
| Self::Do(_, _, _, _)
|
||||
| Self::Continue(_)
|
||||
| Self::Break(_)
|
||||
| Self::Return(_, _, _) => false,
|
||||
@ -737,7 +737,7 @@ impl Stmt {
|
||||
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(_) => false,
|
||||
Self::Share(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
/// Is this statement _pure_?
|
||||
@ -755,8 +755,9 @@ impl Stmt {
|
||||
&& x.0.values().all(Stmt::is_pure)
|
||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||
}
|
||||
Self::While(condition, block, _) => condition.is_pure() && block.is_pure(),
|
||||
Self::Loop(block, _) => block.is_pure(),
|
||||
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
||||
condition.is_pure() && 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()),
|
||||
|
@ -1028,7 +1028,7 @@ impl Engine {
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(Default::default())
|
||||
Ok((Dynamic::UNIT, true))
|
||||
}
|
||||
// xxx[rhs]
|
||||
_ => {
|
||||
@ -1193,7 +1193,7 @@ impl Engine {
|
||||
|err| match *err {
|
||||
// If there is no setter, no need to feed it back because the property is read-only
|
||||
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||
Ok(Default::default())
|
||||
Ok((Dynamic::UNIT, false))
|
||||
}
|
||||
_ => Err(err.fill_position(*x_pos)),
|
||||
},
|
||||
@ -1902,7 +1902,7 @@ impl Engine {
|
||||
|
||||
let result = match stmt {
|
||||
// No-op
|
||||
Stmt::Noop(_) => Ok(Default::default()),
|
||||
Stmt::Noop(_) => Ok(Dynamic::UNIT),
|
||||
|
||||
// Expression as statement
|
||||
Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level),
|
||||
@ -1935,7 +1935,7 @@ impl Engine {
|
||||
} else {
|
||||
*lhs_ptr.as_mut() = rhs_val;
|
||||
}
|
||||
Ok(Default::default())
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
// Op-assignment - in order of precedence:
|
||||
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(
|
||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||
)?;
|
||||
Ok(Default::default())
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
// dot_lhs.dot_rhs op= rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -2053,7 +2053,7 @@ impl Engine {
|
||||
self.eval_dot_index_chain(
|
||||
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)
|
||||
_ => unreachable!(),
|
||||
@ -2077,7 +2077,7 @@ impl Engine {
|
||||
} else if let Some(stmt) = else_block {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||
} else {
|
||||
Ok(Default::default())
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -2115,28 +2115,40 @@ impl Engine {
|
||||
Ok(_) => (),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::LoopBreak(false, _) => (),
|
||||
EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()),
|
||||
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||
_ => return Err(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(false) => return Ok(Default::default()),
|
||||
Ok(false) => return Ok(Dynamic::UNIT),
|
||||
Err(err) => {
|
||||
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Loop statement
|
||||
Stmt::Loop(block, _) => loop {
|
||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, block, level) {
|
||||
// Do loop
|
||||
Stmt::Do(body, expr, is_while, _) => loop {
|
||||
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(Default::default()),
|
||||
EvalAltResult::LoopBreak(false, _) => continue,
|
||||
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||
_ => 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
|
||||
@ -2187,7 +2199,7 @@ impl Engine {
|
||||
|
||||
state.scope_level -= 1;
|
||||
scope.rewind(scope.len() - 1);
|
||||
Ok(Default::default())
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
EvalAltResult::ErrorFor(expr.position()).into()
|
||||
}
|
||||
@ -2312,7 +2324,7 @@ impl Engine {
|
||||
if let Some(alias) = _alias {
|
||||
scope.add_entry_alias(scope.len() - 1, alias);
|
||||
}
|
||||
Ok(Default::default())
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
|
||||
// Import statement
|
||||
@ -2344,7 +2356,7 @@ impl Engine {
|
||||
|
||||
state.modules += 1;
|
||||
|
||||
Ok(Default::default())
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
Err(
|
||||
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
|
||||
@ -2369,7 +2381,7 @@ impl Engine {
|
||||
.into();
|
||||
}
|
||||
}
|
||||
Ok(Default::default())
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
match stmt {
|
||||
// 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),
|
||||
_ => {
|
||||
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);
|
||||
}
|
||||
// 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();
|
||||
|
||||
let pos = condition.position();
|
||||
@ -309,7 +309,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||
};
|
||||
}
|
||||
// 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_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);
|
||||
}
|
||||
// if expr { if_block } else { else_block }
|
||||
Stmt::If(ref mut condition, ref mut x, _) => {
|
||||
Stmt::If(condition, x, _) => {
|
||||
optimize_expr(condition, state);
|
||||
optimize_stmt(&mut x.0, state, true);
|
||||
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();
|
||||
*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 }
|
||||
Stmt::While(condition, block, _) => {
|
||||
optimize_stmt(block, state, false);
|
||||
@ -402,32 +397,30 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// loop { block }
|
||||
Stmt::Loop(block, _) => {
|
||||
optimize_stmt(block, state, false);
|
||||
|
||||
match **block {
|
||||
// loop { break; } -> Noop
|
||||
Stmt::Break(pos) => {
|
||||
// Only a single break statement
|
||||
// do { block } while false | do { block } until true -> { block }
|
||||
Stmt::Do(block, Expr::BoolConstant(true, _), false, _)
|
||||
| Stmt::Do(block, Expr::BoolConstant(false, _), true, _) => {
|
||||
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 }
|
||||
Stmt::For(ref mut iterable, ref mut x, _) => {
|
||||
Stmt::For(iterable, x, _) => {
|
||||
optimize_expr(iterable, state);
|
||||
optimize_stmt(&mut x.1, state, false);
|
||||
}
|
||||
// let id = expr;
|
||||
Stmt::Let(_, Some(ref mut expr), _, _) => optimize_expr(expr, state),
|
||||
Stmt::Let(_, Some(expr), _, _) => optimize_expr(expr, state),
|
||||
// let id;
|
||||
Stmt::Let(_, None, _, _) => (),
|
||||
// import expr as var;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Stmt::Import(ref mut expr, _, _) => optimize_expr(expr, state),
|
||||
Stmt::Import(expr, _, _) => optimize_expr(expr, state),
|
||||
// { block }
|
||||
Stmt::Block(statements, pos) => {
|
||||
*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);
|
||||
}
|
||||
// try { block } catch ( var ) { block }
|
||||
Stmt::TryCatch(ref mut x, _, _) => {
|
||||
Stmt::TryCatch(x, _, _) => {
|
||||
optimize_stmt(&mut x.0, 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);
|
||||
}
|
||||
// expr;
|
||||
Stmt::Expr(ref mut expr) => optimize_expr(expr, state),
|
||||
Stmt::Expr(expr) => optimize_expr(expr, state),
|
||||
// return expr;
|
||||
Stmt::Return(_, Some(ref mut expr), _) => optimize_expr(expr, state),
|
||||
|
||||
|
@ -1970,49 +1970,68 @@ fn parse_if(
|
||||
}
|
||||
|
||||
/// Parse a while loop.
|
||||
fn parse_while(
|
||||
fn parse_while_loop(
|
||||
input: &mut TokenStream,
|
||||
state: &mut ParseState,
|
||||
lib: &mut FunctionsLib,
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<Stmt, ParseError> {
|
||||
// while ...
|
||||
let token_pos = eat_token(input, Token::While);
|
||||
settings.pos = token_pos;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
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")?;
|
||||
let guard = parse_expr(input, state, lib, settings.level_up())?;
|
||||
ensure_not_assignment(input)?;
|
||||
(parse_expr(input, state, lib, settings.level_up())?, pos)
|
||||
}
|
||||
(Token::Loop, pos) => (Expr::BoolConstant(true, pos), pos),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
settings.pos = token_pos;
|
||||
|
||||
ensure_not_assignment(input)?;
|
||||
settings.is_breakable = true;
|
||||
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
||||
|
||||
Ok(Stmt::While(guard, body, token_pos))
|
||||
}
|
||||
|
||||
/// Parse a loop statement.
|
||||
fn parse_loop(
|
||||
/// Parse a do loop.
|
||||
fn parse_do(
|
||||
input: &mut TokenStream,
|
||||
state: &mut ParseState,
|
||||
lib: &mut FunctionsLib,
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<Stmt, ParseError> {
|
||||
// loop ...
|
||||
let token_pos = eat_token(input, Token::Loop);
|
||||
// do ...
|
||||
let token_pos = eat_token(input, Token::Do);
|
||||
settings.pos = token_pos;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
// loop { body }
|
||||
// do { body } [while|until] guard
|
||||
settings.is_breakable = true;
|
||||
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.
|
||||
@ -2413,8 +2432,10 @@ fn parse_stmt(
|
||||
}
|
||||
|
||||
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::Loop => parse_loop(input, state, lib, settings.level_up()).map(Some),
|
||||
Token::While | Token::Loop => {
|
||||
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::Continue if settings.is_breakable => {
|
||||
|
18
src/token.rs
18
src/token.rs
@ -232,8 +232,12 @@ pub enum Token {
|
||||
Else,
|
||||
/// `switch`
|
||||
Switch,
|
||||
/// `do`
|
||||
Do,
|
||||
/// `while`
|
||||
While,
|
||||
/// `until`
|
||||
Until,
|
||||
/// `loop`
|
||||
Loop,
|
||||
/// `for`
|
||||
@ -380,7 +384,9 @@ impl Token {
|
||||
If => "if",
|
||||
Else => "else",
|
||||
Switch => "switch",
|
||||
Do => "do",
|
||||
While => "while",
|
||||
Until => "until",
|
||||
Loop => "loop",
|
||||
For => "for",
|
||||
In => "in",
|
||||
@ -467,7 +473,9 @@ impl Token {
|
||||
"if" => If,
|
||||
"else" => Else,
|
||||
"switch" => Switch,
|
||||
"do" => Do,
|
||||
"while" => While,
|
||||
"until" => Until,
|
||||
"loop" => Loop,
|
||||
"for" => For,
|
||||
"in" => In,
|
||||
@ -524,8 +532,8 @@ impl Token {
|
||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||
|
||||
"===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new"
|
||||
| "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "do"
|
||||
| "each" | "then" | "goto" | "exit" | "match" | "case" | "default" | "void"
|
||||
| "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "each"
|
||||
| "then" | "goto" | "unless" | "exit" | "match" | "case" | "default" | "void"
|
||||
| "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => {
|
||||
Reserved(syntax.into())
|
||||
}
|
||||
@ -586,7 +594,9 @@ impl Token {
|
||||
Ampersand |
|
||||
And |
|
||||
If |
|
||||
Do |
|
||||
While |
|
||||
Until |
|
||||
PlusAssign |
|
||||
MinusAssign |
|
||||
MultiplyAssign |
|
||||
@ -690,8 +700,8 @@ impl Token {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Import | Export | As => true,
|
||||
|
||||
True | False | Let | Const | If | Else | While | Loop | For | In | Continue | Break
|
||||
| Return | Throw | Try | Catch => true,
|
||||
True | False | Let | Const | If | Else | Do | While | Until | Loop | For | In
|
||||
| Continue | Break | Return | Throw | Try | Catch => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
|
@ -24,3 +24,28 @@ fn test_while() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
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