Add continue statement.
This commit is contained in:
parent
9aff10aca4
commit
4ea2fb88ae
19
README.md
19
README.md
@ -1242,9 +1242,10 @@ x == ();
|
||||
let x = 10;
|
||||
|
||||
while x > 0 {
|
||||
x = x - 1;
|
||||
if x < 6 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 5 { break; } // break out of while loop
|
||||
x = x - 1;
|
||||
}
|
||||
```
|
||||
|
||||
@ -1255,8 +1256,9 @@ Infinite `loop`
|
||||
let x = 10;
|
||||
|
||||
loop {
|
||||
print(x);
|
||||
x = x - 1;
|
||||
if x > 5 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 0 { break; } // break out of loop
|
||||
}
|
||||
```
|
||||
@ -1271,14 +1273,16 @@ let array = [1, 3, 5, 7, 9, 42];
|
||||
|
||||
// Iterate through array
|
||||
for x in array {
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; }
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
|
||||
// The 'range' function allows iterating from first to last-1
|
||||
for x in range(0, 50) {
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; }
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
```
|
||||
|
||||
@ -1305,7 +1309,7 @@ if some_bad_condition_has_happened {
|
||||
throw; // defaults to empty exception text: ""
|
||||
```
|
||||
|
||||
Exceptions thrown via `throw` in the script can be captured by matching `Err(EvalAltResult::ErrorRuntime(`_reason_`, `_position_`))`
|
||||
Exceptions thrown via `throw` in the script can be captured by matching `Err(EvalAltResult::ErrorRuntime(` _reason_ `,` _position_ `))`
|
||||
with the exception text captured by the first parameter.
|
||||
|
||||
```rust
|
||||
@ -1354,7 +1358,8 @@ print(add2(42)); // prints 44
|
||||
|
||||
### No access to external scope
|
||||
|
||||
Functions can only access their parameters. They cannot access external variables (even _global_ variables).
|
||||
Functions are not _closures_. They do not capture the calling environment and can only access their own parameters.
|
||||
They cannot access variables external to the function itself.
|
||||
|
||||
```rust
|
||||
let x = 42;
|
||||
@ -1390,7 +1395,7 @@ fn add(x, y) {
|
||||
|
||||
// The following will not compile
|
||||
fn do_addition(x) {
|
||||
fn add_y(n) { // functions cannot be defined inside another function
|
||||
fn add_y(n) { // <- syntax error: functions cannot be defined inside another function
|
||||
n + y
|
||||
}
|
||||
|
||||
|
@ -1349,19 +1349,12 @@ impl Engine<'_> {
|
||||
// While loop
|
||||
Stmt::While(guard, body) => loop {
|
||||
match self.eval_expr(scope, guard, level)?.downcast::<bool>() {
|
||||
Ok(guard_val) => {
|
||||
if *guard_val {
|
||||
match self.eval_stmt(scope, body, level) {
|
||||
Ok(_) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(_)) => {
|
||||
return Ok(().into_dynamic())
|
||||
}
|
||||
Ok(guard_val) if *guard_val => match self.eval_stmt(scope, body, level) {
|
||||
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()),
|
||||
Err(x) => return Err(x),
|
||||
}
|
||||
} else {
|
||||
return Ok(().into_dynamic());
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(_) => return Ok(().into_dynamic()),
|
||||
Err(_) => return Err(EvalAltResult::ErrorLogicGuard(guard.position())),
|
||||
}
|
||||
},
|
||||
@ -1369,8 +1362,8 @@ impl Engine<'_> {
|
||||
// Loop statement
|
||||
Stmt::Loop(body) => loop {
|
||||
match self.eval_stmt(scope, body, level) {
|
||||
Ok(_) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(_)) => return Ok(().into_dynamic()),
|
||||
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()),
|
||||
Err(x) => return Err(x),
|
||||
}
|
||||
},
|
||||
@ -1393,8 +1386,8 @@ impl Engine<'_> {
|
||||
*scope.get_mut(entry) = a;
|
||||
|
||||
match self.eval_stmt(scope, body, level) {
|
||||
Ok(_) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(_)) => break,
|
||||
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(true, _)) => break,
|
||||
Err(x) => return Err(x),
|
||||
}
|
||||
}
|
||||
@ -1406,8 +1399,11 @@ impl Engine<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Continue statement
|
||||
Stmt::Continue(pos) => Err(EvalAltResult::ErrorLoopBreak(false, *pos)),
|
||||
|
||||
// Break statement
|
||||
Stmt::Break(pos) => Err(EvalAltResult::ErrorLoopBreak(*pos)),
|
||||
Stmt::Break(pos) => Err(EvalAltResult::ErrorLoopBreak(true, *pos)),
|
||||
|
||||
// Empty return
|
||||
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => {
|
||||
|
@ -278,6 +278,8 @@ pub enum Stmt {
|
||||
Block(Vec<Stmt>, Position),
|
||||
/// { stmt }
|
||||
Expr(Box<Expr>),
|
||||
/// continue
|
||||
Continue(Position),
|
||||
/// break
|
||||
Break(Position),
|
||||
/// `return`/`throw`
|
||||
@ -292,6 +294,7 @@ impl Stmt {
|
||||
| Stmt::Let(_, _, pos)
|
||||
| Stmt::Const(_, _, pos)
|
||||
| Stmt::Block(_, pos)
|
||||
| Stmt::Continue(pos)
|
||||
| Stmt::Break(pos)
|
||||
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
||||
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
||||
@ -314,6 +317,7 @@ impl Stmt {
|
||||
Stmt::Let(_, _, _)
|
||||
| Stmt::Const(_, _, _)
|
||||
| Stmt::Expr(_)
|
||||
| Stmt::Continue(_)
|
||||
| Stmt::Break(_)
|
||||
| Stmt::ReturnWithVal(_, _, _) => false,
|
||||
}
|
||||
@ -334,7 +338,7 @@ impl Stmt {
|
||||
Stmt::For(_, range, block) => range.is_pure() && block.is_pure(),
|
||||
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => false,
|
||||
Stmt::Block(statements, _) => statements.iter().all(Stmt::is_pure),
|
||||
Stmt::Break(_) | Stmt::ReturnWithVal(_, _, _) => false,
|
||||
Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_, _, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -579,6 +583,7 @@ pub enum Token {
|
||||
And,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Fn,
|
||||
Continue,
|
||||
Break,
|
||||
Return,
|
||||
Throw,
|
||||
@ -653,6 +658,7 @@ impl Token {
|
||||
And => "&&",
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Fn => "fn",
|
||||
Continue => "continue",
|
||||
Break => "break",
|
||||
Return => "return",
|
||||
Throw => "throw",
|
||||
@ -1100,6 +1106,7 @@ impl<'a> TokenIterator<'a> {
|
||||
"else" => Token::Else,
|
||||
"while" => Token::While,
|
||||
"loop" => Token::Loop,
|
||||
"continue" => Token::Continue,
|
||||
"break" => Token::Break,
|
||||
"return" => Token::Return,
|
||||
"throw" => Token::Throw,
|
||||
@ -2419,6 +2426,8 @@ fn parse_stmt<'a>(
|
||||
// Semicolon - empty statement
|
||||
(Token::SemiColon, pos) => Ok(Stmt::Noop(*pos)),
|
||||
|
||||
(Token::LeftBrace, _) => parse_block(input, breakable, allow_stmt_expr),
|
||||
|
||||
// fn ...
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
(Token::Fn, pos) => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
||||
@ -2427,12 +2436,19 @@ fn parse_stmt<'a>(
|
||||
(Token::While, _) => parse_while(input, allow_stmt_expr),
|
||||
(Token::Loop, _) => parse_loop(input, allow_stmt_expr),
|
||||
(Token::For, _) => parse_for(input, allow_stmt_expr),
|
||||
|
||||
(Token::Continue, pos) if breakable => {
|
||||
let pos = *pos;
|
||||
input.next();
|
||||
Ok(Stmt::Continue(pos))
|
||||
}
|
||||
(Token::Break, pos) if breakable => {
|
||||
let pos = *pos;
|
||||
input.next();
|
||||
Ok(Stmt::Break(pos))
|
||||
}
|
||||
(Token::Break, pos) => Err(PERR::LoopBreak.into_err(*pos)),
|
||||
(Token::Continue, pos) | (Token::Break, pos) => Err(PERR::LoopBreak.into_err(*pos)),
|
||||
|
||||
(token @ Token::Return, pos) | (token @ Token::Throw, pos) => {
|
||||
let return_type = match token {
|
||||
Token::Return => ReturnType::Return,
|
||||
@ -2456,9 +2472,10 @@ fn parse_stmt<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
(Token::LeftBrace, _) => parse_block(input, breakable, allow_stmt_expr),
|
||||
|
||||
(Token::Let, _) => parse_let(input, ScopeEntryType::Normal, allow_stmt_expr),
|
||||
(Token::Const, _) => parse_let(input, ScopeEntryType::Constant, allow_stmt_expr),
|
||||
|
||||
_ => parse_expr_stmt(input, allow_stmt_expr),
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,9 @@ pub enum EvalAltResult {
|
||||
ErrorRuntime(String, Position),
|
||||
|
||||
/// Breaking out of loops - not an error if within a loop.
|
||||
ErrorLoopBreak(Position),
|
||||
/// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement).
|
||||
/// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement).
|
||||
ErrorLoopBreak(bool, Position),
|
||||
/// Not an error: Value returned from a script via the `return` keyword.
|
||||
/// Wrapped value is the result value.
|
||||
Return(Dynamic, Position),
|
||||
@ -118,7 +120,8 @@ impl EvalAltResult {
|
||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||
Self::ErrorLoopBreak(_) => "Break statement not inside a loop",
|
||||
Self::ErrorLoopBreak(true, _) => "Break statement not inside a loop",
|
||||
Self::ErrorLoopBreak(false, _) => "Continue statement not inside a loop",
|
||||
Self::Return(_, _) => "[Not Error] Function returns value",
|
||||
}
|
||||
}
|
||||
@ -160,7 +163,7 @@ impl fmt::Display for EvalAltResult {
|
||||
Self::ErrorMismatchOutputType(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
||||
Self::ErrorArithmetic(s, pos) => write!(f, "{} ({})", s, pos),
|
||||
|
||||
Self::ErrorLoopBreak(pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorLoopBreak(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::Return(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||
|
||||
Self::ErrorFunctionArgsMismatch(fn_name, 0, n, pos) => write!(
|
||||
@ -255,7 +258,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorArithmetic(_, pos)
|
||||
| Self::ErrorStackOverflow(pos)
|
||||
| Self::ErrorRuntime(_, pos)
|
||||
| Self::ErrorLoopBreak(pos)
|
||||
| Self::ErrorLoopBreak(_, pos)
|
||||
| Self::Return(_, pos) => *pos,
|
||||
}
|
||||
}
|
||||
@ -287,7 +290,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorArithmetic(_, ref mut pos)
|
||||
| Self::ErrorStackOverflow(ref mut pos)
|
||||
| Self::ErrorRuntime(_, ref mut pos)
|
||||
| Self::ErrorLoopBreak(ref mut pos)
|
||||
| Self::ErrorLoopBreak(_, ref mut pos)
|
||||
| Self::Return(_, ref mut pos) => *pos = new_position,
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,9 @@ fn test_loop() -> Result<(), EvalAltResult> {
|
||||
|
||||
loop {
|
||||
if i < 10 {
|
||||
i += 1;
|
||||
if x > 20 { continue; }
|
||||
x = x + i;
|
||||
i = i + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -22,7 +23,7 @@ fn test_loop() -> Result<(), EvalAltResult> {
|
||||
return x;
|
||||
"
|
||||
)?,
|
||||
45
|
||||
21
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -6,8 +6,18 @@ fn test_while() -> Result<(), EvalAltResult> {
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
"let x = 0; while x < 10 { x = x + 1; if x > 5 { \
|
||||
break } } x",
|
||||
r"
|
||||
let x = 0;
|
||||
|
||||
while x < 10 {
|
||||
x = x + 1;
|
||||
if x > 5 { break; }
|
||||
if x > 3 { continue; }
|
||||
x = x + 3;
|
||||
}
|
||||
|
||||
x
|
||||
",
|
||||
)?,
|
||||
6
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user