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
|
## Arrays
|
||||||
|
|
||||||
You can create arrays of values, and then access them with numeric indices.
|
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),
|
ErrorCantOpenScriptFile(String, std::io::Error),
|
||||||
ErrorDotExpr(Position),
|
ErrorDotExpr(Position),
|
||||||
ErrorArithmetic(String, Position),
|
ErrorArithmetic(String, Position),
|
||||||
|
ErrorRuntime(String, Position),
|
||||||
LoopBreak,
|
LoopBreak,
|
||||||
Return(Dynamic, Position),
|
Return(Dynamic, Position),
|
||||||
}
|
}
|
||||||
@ -70,6 +71,7 @@ impl Error for EvalAltResult {
|
|||||||
Self::ErrorCantOpenScriptFile(_, _) => "Cannot open script file",
|
Self::ErrorCantOpenScriptFile(_, _) => "Cannot open script file",
|
||||||
Self::ErrorDotExpr(_) => "Malformed dot expression",
|
Self::ErrorDotExpr(_) => "Malformed dot expression",
|
||||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||||
|
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||||
Self::LoopBreak => "[Not Error] Breaks out of loop",
|
Self::LoopBreak => "[Not Error] Breaks out of loop",
|
||||||
Self::Return(_, _) => "[Not Error] Function returns value",
|
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::ErrorMismatchOutputType(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
||||||
Self::ErrorDotExpr(pos) => write!(f, "{} ({})", desc, pos),
|
Self::ErrorDotExpr(pos) => write!(f, "{} ({})", desc, pos),
|
||||||
Self::ErrorArithmetic(s, pos) => write!(f, "{}: {} ({})", desc, s, 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::LoopBreak => write!(f, "{}", desc),
|
||||||
Self::Return(_, pos) => write!(f, "{} ({})", desc, pos),
|
Self::Return(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||||
Self::ErrorCantOpenScriptFile(filename, err) => {
|
Self::ErrorCantOpenScriptFile(filename, err) => {
|
||||||
@ -879,11 +883,31 @@ impl Engine {
|
|||||||
|
|
||||||
Stmt::Break(_) => Err(EvalAltResult::LoopBreak),
|
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) => {
|
// Return value
|
||||||
let result = self.eval_expr(scope, a)?;
|
Stmt::ReturnWithVal(Some(a), true, pos) => {
|
||||||
Err(EvalAltResult::Return(result, *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, _) => {
|
Stmt::Let(name, init, _) => {
|
||||||
|
@ -222,8 +222,7 @@ pub enum Stmt {
|
|||||||
Block(Vec<Stmt>),
|
Block(Vec<Stmt>),
|
||||||
Expr(Box<Expr>),
|
Expr(Box<Expr>),
|
||||||
Break(Position),
|
Break(Position),
|
||||||
Return(Position),
|
ReturnWithVal(Option<Box<Expr>>, bool, Position),
|
||||||
ReturnWithVal(Box<Expr>, Position),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -310,6 +309,7 @@ pub enum Token {
|
|||||||
Fn,
|
Fn,
|
||||||
Break,
|
Break,
|
||||||
Return,
|
Return,
|
||||||
|
Throw,
|
||||||
PlusAssign,
|
PlusAssign,
|
||||||
MinusAssign,
|
MinusAssign,
|
||||||
MultiplyAssign,
|
MultiplyAssign,
|
||||||
@ -382,6 +382,7 @@ impl Token {
|
|||||||
Fn => "fn",
|
Fn => "fn",
|
||||||
Break => "break",
|
Break => "break",
|
||||||
Return => "return",
|
Return => "return",
|
||||||
|
Throw => "throw",
|
||||||
PlusAssign => "+=",
|
PlusAssign => "+=",
|
||||||
MinusAssign => "-=",
|
MinusAssign => "-=",
|
||||||
MultiplyAssign => "*=",
|
MultiplyAssign => "*=",
|
||||||
@ -456,6 +457,7 @@ impl Token {
|
|||||||
Modulo |
|
Modulo |
|
||||||
ModuloAssign |
|
ModuloAssign |
|
||||||
Return |
|
Return |
|
||||||
|
Throw |
|
||||||
PowerOf |
|
PowerOf |
|
||||||
In |
|
In |
|
||||||
PowerOfAssign => true,
|
PowerOfAssign => true,
|
||||||
@ -498,7 +500,7 @@ impl Token {
|
|||||||
use self::Token::*;
|
use self::Token::*;
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
UnaryPlus | UnaryMinus | Equals | Bang | Return => true,
|
UnaryPlus | UnaryMinus | Equals | Bang | Return | Throw => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -816,6 +818,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
"loop" => Token::Loop,
|
"loop" => Token::Loop,
|
||||||
"break" => Token::Break,
|
"break" => Token::Break,
|
||||||
"return" => Token::Return,
|
"return" => Token::Return,
|
||||||
|
"throw" => Token::Throw,
|
||||||
"fn" => Token::Fn,
|
"fn" => Token::Fn,
|
||||||
"for" => Token::For,
|
"for" => Token::For,
|
||||||
"in" => Token::In,
|
"in" => Token::In,
|
||||||
@ -1743,6 +1746,7 @@ fn parse_block<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Pars
|
|||||||
// Parse statements inside the block
|
// Parse statements inside the block
|
||||||
statements.push(parse_stmt(input)?);
|
statements.push(parse_stmt(input)?);
|
||||||
|
|
||||||
|
// Notice semicolons are optional
|
||||||
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
||||||
input.next();
|
input.next();
|
||||||
}
|
}
|
||||||
@ -1778,15 +1782,25 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
|
|||||||
input.next();
|
input.next();
|
||||||
Ok(Stmt::Break(pos))
|
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();
|
input.next();
|
||||||
|
|
||||||
match input.peek() {
|
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)) => {
|
Some(&(_, pos)) => {
|
||||||
let ret = parse_expr(input)?;
|
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),
|
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)?),
|
_ => statements.push(parse_stmt(input)?),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notice semicolons are optional
|
||||||
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
||||||
input.next();
|
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