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;
|
let x = 10;
|
||||||
|
|
||||||
while x > 0 {
|
while x > 0 {
|
||||||
|
x = x - 1;
|
||||||
|
if x < 6 { continue; } // skip to the next iteration
|
||||||
print(x);
|
print(x);
|
||||||
if x == 5 { break; } // break out of while loop
|
if x == 5 { break; } // break out of while loop
|
||||||
x = x - 1;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1255,8 +1256,9 @@ Infinite `loop`
|
|||||||
let x = 10;
|
let x = 10;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
print(x);
|
|
||||||
x = x - 1;
|
x = x - 1;
|
||||||
|
if x > 5 { continue; } // skip to the next iteration
|
||||||
|
print(x);
|
||||||
if x == 0 { break; } // break out of loop
|
if x == 0 { break; } // break out of loop
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -1271,14 +1273,16 @@ let array = [1, 3, 5, 7, 9, 42];
|
|||||||
|
|
||||||
// Iterate through array
|
// Iterate through array
|
||||||
for x in array {
|
for x in array {
|
||||||
|
if x > 10 { continue; } // skip to the next iteration
|
||||||
print(x);
|
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
|
// The 'range' function allows iterating from first to last-1
|
||||||
for x in range(0, 50) {
|
for x in range(0, 50) {
|
||||||
|
if x > 10 { continue; } // skip to the next iteration
|
||||||
print(x);
|
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: ""
|
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.
|
with the exception text captured by the first parameter.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -1354,7 +1358,8 @@ print(add2(42)); // prints 44
|
|||||||
|
|
||||||
### No access to external scope
|
### 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
|
```rust
|
||||||
let x = 42;
|
let x = 42;
|
||||||
@ -1390,7 +1395,7 @@ fn add(x, y) {
|
|||||||
|
|
||||||
// The following will not compile
|
// The following will not compile
|
||||||
fn do_addition(x) {
|
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
|
n + y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1349,19 +1349,12 @@ impl Engine<'_> {
|
|||||||
// While loop
|
// While loop
|
||||||
Stmt::While(guard, body) => loop {
|
Stmt::While(guard, body) => loop {
|
||||||
match self.eval_expr(scope, guard, level)?.downcast::<bool>() {
|
match self.eval_expr(scope, guard, level)?.downcast::<bool>() {
|
||||||
Ok(guard_val) => {
|
Ok(guard_val) if *guard_val => match self.eval_stmt(scope, body, level) {
|
||||||
if *guard_val {
|
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
||||||
match self.eval_stmt(scope, body, level) {
|
Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()),
|
||||||
Ok(_) => (),
|
Err(x) => return Err(x),
|
||||||
Err(EvalAltResult::ErrorLoopBreak(_)) => {
|
},
|
||||||
return Ok(().into_dynamic())
|
Ok(_) => return Ok(().into_dynamic()),
|
||||||
}
|
|
||||||
Err(x) => return Err(x),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Ok(().into_dynamic());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => return Err(EvalAltResult::ErrorLogicGuard(guard.position())),
|
Err(_) => return Err(EvalAltResult::ErrorLogicGuard(guard.position())),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1369,8 +1362,8 @@ impl Engine<'_> {
|
|||||||
// Loop statement
|
// Loop statement
|
||||||
Stmt::Loop(body) => loop {
|
Stmt::Loop(body) => loop {
|
||||||
match self.eval_stmt(scope, body, level) {
|
match self.eval_stmt(scope, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
||||||
Err(EvalAltResult::ErrorLoopBreak(_)) => return Ok(().into_dynamic()),
|
Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()),
|
||||||
Err(x) => return Err(x),
|
Err(x) => return Err(x),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1393,8 +1386,8 @@ impl Engine<'_> {
|
|||||||
*scope.get_mut(entry) = a;
|
*scope.get_mut(entry) = a;
|
||||||
|
|
||||||
match self.eval_stmt(scope, body, level) {
|
match self.eval_stmt(scope, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
||||||
Err(EvalAltResult::ErrorLoopBreak(_)) => break,
|
Err(EvalAltResult::ErrorLoopBreak(true, _)) => break,
|
||||||
Err(x) => return Err(x),
|
Err(x) => return Err(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1406,8 +1399,11 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continue statement
|
||||||
|
Stmt::Continue(pos) => Err(EvalAltResult::ErrorLoopBreak(false, *pos)),
|
||||||
|
|
||||||
// Break statement
|
// Break statement
|
||||||
Stmt::Break(pos) => Err(EvalAltResult::ErrorLoopBreak(*pos)),
|
Stmt::Break(pos) => Err(EvalAltResult::ErrorLoopBreak(true, *pos)),
|
||||||
|
|
||||||
// Empty return
|
// Empty return
|
||||||
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => {
|
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => {
|
||||||
|
@ -278,6 +278,8 @@ pub enum Stmt {
|
|||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Expr(Box<Expr>),
|
Expr(Box<Expr>),
|
||||||
|
/// continue
|
||||||
|
Continue(Position),
|
||||||
/// break
|
/// break
|
||||||
Break(Position),
|
Break(Position),
|
||||||
/// `return`/`throw`
|
/// `return`/`throw`
|
||||||
@ -292,6 +294,7 @@ impl Stmt {
|
|||||||
| Stmt::Let(_, _, pos)
|
| Stmt::Let(_, _, pos)
|
||||||
| Stmt::Const(_, _, pos)
|
| Stmt::Const(_, _, pos)
|
||||||
| Stmt::Block(_, pos)
|
| Stmt::Block(_, pos)
|
||||||
|
| Stmt::Continue(pos)
|
||||||
| Stmt::Break(pos)
|
| Stmt::Break(pos)
|
||||||
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
||||||
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
||||||
@ -314,6 +317,7 @@ impl Stmt {
|
|||||||
Stmt::Let(_, _, _)
|
Stmt::Let(_, _, _)
|
||||||
| Stmt::Const(_, _, _)
|
| Stmt::Const(_, _, _)
|
||||||
| Stmt::Expr(_)
|
| Stmt::Expr(_)
|
||||||
|
| Stmt::Continue(_)
|
||||||
| Stmt::Break(_)
|
| Stmt::Break(_)
|
||||||
| Stmt::ReturnWithVal(_, _, _) => false,
|
| Stmt::ReturnWithVal(_, _, _) => false,
|
||||||
}
|
}
|
||||||
@ -334,7 +338,7 @@ impl Stmt {
|
|||||||
Stmt::For(_, range, block) => range.is_pure() && block.is_pure(),
|
Stmt::For(_, range, block) => range.is_pure() && block.is_pure(),
|
||||||
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => false,
|
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => false,
|
||||||
Stmt::Block(statements, _) => statements.iter().all(Stmt::is_pure),
|
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,
|
And,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Fn,
|
Fn,
|
||||||
|
Continue,
|
||||||
Break,
|
Break,
|
||||||
Return,
|
Return,
|
||||||
Throw,
|
Throw,
|
||||||
@ -653,6 +658,7 @@ impl Token {
|
|||||||
And => "&&",
|
And => "&&",
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Fn => "fn",
|
Fn => "fn",
|
||||||
|
Continue => "continue",
|
||||||
Break => "break",
|
Break => "break",
|
||||||
Return => "return",
|
Return => "return",
|
||||||
Throw => "throw",
|
Throw => "throw",
|
||||||
@ -1100,6 +1106,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
"else" => Token::Else,
|
"else" => Token::Else,
|
||||||
"while" => Token::While,
|
"while" => Token::While,
|
||||||
"loop" => Token::Loop,
|
"loop" => Token::Loop,
|
||||||
|
"continue" => Token::Continue,
|
||||||
"break" => Token::Break,
|
"break" => Token::Break,
|
||||||
"return" => Token::Return,
|
"return" => Token::Return,
|
||||||
"throw" => Token::Throw,
|
"throw" => Token::Throw,
|
||||||
@ -2419,6 +2426,8 @@ fn parse_stmt<'a>(
|
|||||||
// Semicolon - empty statement
|
// Semicolon - empty statement
|
||||||
(Token::SemiColon, pos) => Ok(Stmt::Noop(*pos)),
|
(Token::SemiColon, pos) => Ok(Stmt::Noop(*pos)),
|
||||||
|
|
||||||
|
(Token::LeftBrace, _) => parse_block(input, breakable, allow_stmt_expr),
|
||||||
|
|
||||||
// fn ...
|
// fn ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
(Token::Fn, pos) => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
(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::While, _) => parse_while(input, allow_stmt_expr),
|
||||||
(Token::Loop, _) => parse_loop(input, allow_stmt_expr),
|
(Token::Loop, _) => parse_loop(input, allow_stmt_expr),
|
||||||
(Token::For, _) => parse_for(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 => {
|
(Token::Break, pos) if breakable => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
input.next();
|
input.next();
|
||||||
Ok(Stmt::Break(pos))
|
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) => {
|
(token @ Token::Return, pos) | (token @ Token::Throw, pos) => {
|
||||||
let return_type = match token {
|
let return_type = match token {
|
||||||
Token::Return => ReturnType::Return,
|
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::Let, _) => parse_let(input, ScopeEntryType::Normal, allow_stmt_expr),
|
||||||
(Token::Const, _) => parse_let(input, ScopeEntryType::Constant, allow_stmt_expr),
|
(Token::Const, _) => parse_let(input, ScopeEntryType::Constant, allow_stmt_expr),
|
||||||
|
|
||||||
_ => parse_expr_stmt(input, allow_stmt_expr),
|
_ => parse_expr_stmt(input, allow_stmt_expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,9 @@ pub enum EvalAltResult {
|
|||||||
ErrorRuntime(String, Position),
|
ErrorRuntime(String, Position),
|
||||||
|
|
||||||
/// Breaking out of loops - not an error if within a loop.
|
/// 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.
|
/// Not an error: Value returned from a script via the `return` keyword.
|
||||||
/// Wrapped value is the result value.
|
/// Wrapped value is the result value.
|
||||||
Return(Dynamic, Position),
|
Return(Dynamic, Position),
|
||||||
@ -118,7 +120,8 @@ impl EvalAltResult {
|
|||||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||||
Self::ErrorStackOverflow(_) => "Stack overflow",
|
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
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",
|
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::ErrorMismatchOutputType(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
||||||
Self::ErrorArithmetic(s, pos) => write!(f, "{} ({})", 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::Return(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||||
|
|
||||||
Self::ErrorFunctionArgsMismatch(fn_name, 0, n, pos) => write!(
|
Self::ErrorFunctionArgsMismatch(fn_name, 0, n, pos) => write!(
|
||||||
@ -255,7 +258,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorArithmetic(_, pos)
|
| Self::ErrorArithmetic(_, pos)
|
||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::ErrorLoopBreak(pos)
|
| Self::ErrorLoopBreak(_, pos)
|
||||||
| Self::Return(_, pos) => *pos,
|
| Self::Return(_, pos) => *pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -287,7 +290,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorArithmetic(_, ref mut pos)
|
| Self::ErrorArithmetic(_, ref mut pos)
|
||||||
| Self::ErrorStackOverflow(ref mut pos)
|
| Self::ErrorStackOverflow(ref mut pos)
|
||||||
| Self::ErrorRuntime(_, 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,
|
| Self::Return(_, ref mut pos) => *pos = new_position,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,9 @@ fn test_loop() -> Result<(), EvalAltResult> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
if i < 10 {
|
if i < 10 {
|
||||||
|
i += 1;
|
||||||
|
if x > 20 { continue; }
|
||||||
x = x + i;
|
x = x + i;
|
||||||
i = i + 1;
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -22,7 +23,7 @@ fn test_loop() -> Result<(), EvalAltResult> {
|
|||||||
return x;
|
return x;
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
45
|
21
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -6,8 +6,18 @@ fn test_while() -> Result<(), EvalAltResult> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = 0; while x < 10 { x = x + 1; if x > 5 { \
|
r"
|
||||||
break } } x",
|
let x = 0;
|
||||||
|
|
||||||
|
while x < 10 {
|
||||||
|
x = x + 1;
|
||||||
|
if x > 5 { break; }
|
||||||
|
if x > 3 { continue; }
|
||||||
|
x = x + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
x
|
||||||
|
",
|
||||||
)?,
|
)?,
|
||||||
6
|
6
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user