Add throw.
This commit is contained in:
parent
e2cb111e4b
commit
9f80bf03c4
34
README.md
34
README.md
@ -563,6 +563,40 @@ fn do_addition(x) {
|
||||
}
|
||||
```
|
||||
|
||||
## Return
|
||||
|
||||
```rust
|
||||
return;
|
||||
|
||||
return 123 + 456;
|
||||
```
|
||||
|
||||
## Errors and Exceptions
|
||||
|
||||
```rust
|
||||
if error != "" {
|
||||
throw error; // `throw` takes a string to form the exception text
|
||||
}
|
||||
|
||||
throw; // no exception text
|
||||
```
|
||||
|
||||
All of `Engine`'s evaluation/consuming methods return `Result<T, rhai::EvalAltResult>` with `EvalAltResult` holding error information.
|
||||
|
||||
Exceptions thrown via `throw` in the script can be captured by matching `Err(EvalAltResult::ErrorRuntime(reason, position))` with the exception text captured by the `reason` parameter.
|
||||
|
||||
```rust
|
||||
let result = engine.eval::<i64>(&mut scope, r#"
|
||||
let x = 42;
|
||||
|
||||
if x > 0 {
|
||||
throw x + " is too large!";
|
||||
}
|
||||
"#);
|
||||
|
||||
println!(result); // prints "Runtime error: 42 is too large! (line 5, position 15)"
|
||||
```
|
||||
|
||||
## Arrays
|
||||
|
||||
You can create arrays of values, and then access them with numeric indices.
|
||||
|
@ -35,6 +35,7 @@ pub enum EvalAltResult {
|
||||
ErrorCantOpenScriptFile(String, std::io::Error),
|
||||
ErrorDotExpr(Position),
|
||||
ErrorArithmetic(String, Position),
|
||||
ErrorRuntime(String, Position),
|
||||
LoopBreak,
|
||||
Return(Dynamic, Position),
|
||||
}
|
||||
@ -70,6 +71,7 @@ impl Error for EvalAltResult {
|
||||
Self::ErrorCantOpenScriptFile(_, _) => "Cannot open script file",
|
||||
Self::ErrorDotExpr(_) => "Malformed dot expression",
|
||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||
Self::LoopBreak => "[Not Error] Breaks out of loop",
|
||||
Self::Return(_, _) => "[Not Error] Function returns value",
|
||||
}
|
||||
@ -95,6 +97,8 @@ impl std::fmt::Display for EvalAltResult {
|
||||
Self::ErrorMismatchOutputType(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
||||
Self::ErrorDotExpr(pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorArithmetic(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
||||
Self::ErrorRuntime(s, pos) if s.is_empty() => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorRuntime(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
||||
Self::LoopBreak => write!(f, "{}", desc),
|
||||
Self::Return(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorCantOpenScriptFile(filename, err) => {
|
||||
@ -879,11 +883,31 @@ impl Engine {
|
||||
|
||||
Stmt::Break(_) => Err(EvalAltResult::LoopBreak),
|
||||
|
||||
Stmt::Return(pos) => Err(EvalAltResult::Return(().into_dynamic(), *pos)),
|
||||
// Empty return
|
||||
Stmt::ReturnWithVal(None, true, pos) => {
|
||||
Err(EvalAltResult::Return(().into_dynamic(), *pos))
|
||||
}
|
||||
|
||||
Stmt::ReturnWithVal(a, pos) => {
|
||||
let result = self.eval_expr(scope, a)?;
|
||||
Err(EvalAltResult::Return(result, *pos))
|
||||
// Return value
|
||||
Stmt::ReturnWithVal(Some(a), true, pos) => {
|
||||
Err(EvalAltResult::Return(self.eval_expr(scope, a)?, *pos))
|
||||
}
|
||||
|
||||
// Empty throw
|
||||
Stmt::ReturnWithVal(None, false, pos) => {
|
||||
Err(EvalAltResult::ErrorRuntime("".into(), *pos))
|
||||
}
|
||||
|
||||
// Throw value
|
||||
Stmt::ReturnWithVal(Some(a), false, pos) => {
|
||||
let val = self.eval_expr(scope, a)?;
|
||||
Err(EvalAltResult::ErrorRuntime(
|
||||
(val.downcast_ref() as Option<&String>)
|
||||
.map(|s| s.as_ref())
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
*pos,
|
||||
))
|
||||
}
|
||||
|
||||
Stmt::Let(name, init, _) => {
|
||||
|
@ -222,8 +222,7 @@ pub enum Stmt {
|
||||
Block(Vec<Stmt>),
|
||||
Expr(Box<Expr>),
|
||||
Break(Position),
|
||||
Return(Position),
|
||||
ReturnWithVal(Box<Expr>, Position),
|
||||
ReturnWithVal(Option<Box<Expr>>, bool, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -310,6 +309,7 @@ pub enum Token {
|
||||
Fn,
|
||||
Break,
|
||||
Return,
|
||||
Throw,
|
||||
PlusAssign,
|
||||
MinusAssign,
|
||||
MultiplyAssign,
|
||||
@ -382,6 +382,7 @@ impl Token {
|
||||
Fn => "fn",
|
||||
Break => "break",
|
||||
Return => "return",
|
||||
Throw => "throw",
|
||||
PlusAssign => "+=",
|
||||
MinusAssign => "-=",
|
||||
MultiplyAssign => "*=",
|
||||
@ -456,6 +457,7 @@ impl Token {
|
||||
Modulo |
|
||||
ModuloAssign |
|
||||
Return |
|
||||
Throw |
|
||||
PowerOf |
|
||||
In |
|
||||
PowerOfAssign => true,
|
||||
@ -498,7 +500,7 @@ impl Token {
|
||||
use self::Token::*;
|
||||
|
||||
match *self {
|
||||
UnaryPlus | UnaryMinus | Equals | Bang | Return => true,
|
||||
UnaryPlus | UnaryMinus | Equals | Bang | Return | Throw => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -816,6 +818,7 @@ impl<'a> TokenIterator<'a> {
|
||||
"loop" => Token::Loop,
|
||||
"break" => Token::Break,
|
||||
"return" => Token::Return,
|
||||
"throw" => Token::Throw,
|
||||
"fn" => Token::Fn,
|
||||
"for" => Token::For,
|
||||
"in" => Token::In,
|
||||
@ -1743,6 +1746,7 @@ fn parse_block<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Pars
|
||||
// Parse statements inside the block
|
||||
statements.push(parse_stmt(input)?);
|
||||
|
||||
// Notice semicolons are optional
|
||||
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
||||
input.next();
|
||||
}
|
||||
@ -1778,15 +1782,25 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
|
||||
input.next();
|
||||
Ok(Stmt::Break(pos))
|
||||
}
|
||||
Some(&(Token::Return, _)) => {
|
||||
Some(&(ref token @ Token::Return, _)) | Some(&(ref token @ Token::Throw, _)) => {
|
||||
let is_return = match token {
|
||||
Token::Return => true,
|
||||
Token::Throw => false,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
input.next();
|
||||
|
||||
match input.peek() {
|
||||
Some(&(Token::SemiColon, pos)) => Ok(Stmt::Return(pos)),
|
||||
// return; or throw;
|
||||
Some(&(Token::SemiColon, pos)) => Ok(Stmt::ReturnWithVal(None, is_return, pos)),
|
||||
// Just a return/throw without anything at the end of script
|
||||
None => Ok(Stmt::ReturnWithVal(None, is_return, Position::eof())),
|
||||
// return or throw with expression
|
||||
Some(&(_, pos)) => {
|
||||
let ret = parse_expr(input)?;
|
||||
Ok(Stmt::ReturnWithVal(Box::new(ret), pos))
|
||||
Ok(Stmt::ReturnWithVal(Some(Box::new(ret)), is_return, pos))
|
||||
}
|
||||
_ => parse_expr_stmt(input),
|
||||
}
|
||||
}
|
||||
Some(&(Token::LeftBrace, _)) => parse_block(input),
|
||||
@ -1859,6 +1873,7 @@ fn parse_top_level<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<AST, P
|
||||
_ => statements.push(parse_stmt(input)?),
|
||||
}
|
||||
|
||||
// Notice semicolons are optional
|
||||
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
||||
input.next();
|
||||
}
|
||||
|
18
tests/throw.rs
Normal file
18
tests/throw.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
|
||||
#[test]
|
||||
fn test_throw() {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
match engine.eval::<i64>(r#"if true { throw "hello" }"#) {
|
||||
Ok(_) => panic!("not an error"),
|
||||
Err(EvalAltResult::ErrorRuntime(s, _)) if s == "hello" => (),
|
||||
Err(err) => panic!("wrong error: {}", err),
|
||||
}
|
||||
|
||||
match engine.eval::<i64>(r#"throw;"#) {
|
||||
Ok(_) => panic!("not an error"),
|
||||
Err(EvalAltResult::ErrorRuntime(s, _)) if s == "" => (),
|
||||
Err(err) => panic!("wrong error: {}", err),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user