Improve error messages to lists.

This commit is contained in:
Stephen Chung 2020-03-16 23:51:32 +08:00
parent 5ad2d24251
commit 705fbd0c1b
5 changed files with 433 additions and 453 deletions

View File

@ -13,6 +13,7 @@ include = [
"scripts/*.rhai", "scripts/*.rhai",
"Cargo.toml" "Cargo.toml"
] ]
keywords = [ "scripting" ]
[dependencies] [dependencies]
num-traits = "*" num-traits = "*"

View File

@ -171,7 +171,7 @@ let ast = engine.compile("40 + 2")?;
for _ in 0..42 { for _ in 0..42 {
let result: i64 = engine.eval_ast(&ast)?; let result: i64 = engine.eval_ast(&ast)?;
println!("Answer: {}", result); // prints 42 println!("Answer #{}: {}", i, result); // prints 42
} }
``` ```
@ -193,14 +193,17 @@ use rhai::Engine;
let mut engine = Engine::new(); let mut engine = Engine::new();
// Define a function in a script and load it into the Engine. // Define a function in a script and load it into the Engine.
engine.consume(true, // pass true to 'retain_functions' otherwise these functions // Pass true to 'retain_functions' otherwise these functions will be cleared at the end of consume()
r" // will be cleared at the end of consume() engine.consume(true,
fn hello(x, y) { // a function with two parameters: String and i64 r"
x.len() + y // returning i64 // a function with two parameters: String and i64
fn hello(x, y) {
x.len() + y
} }
fn hello(x) { // functions can be overloaded: this one takes only one parameter // functions can be overloaded: this one takes only one parameter
x * 2 // returning i64 fn hello(x) {
x * 2
} }
")?; ")?;
@ -495,22 +498,21 @@ let result = engine.eval::<i64>("let x = new_ts(); x.foo()")?;
println!("result: {}", result); // prints 1 println!("result: {}", result); // prints 1
``` ```
`type_of` works fine with custom types and returns the name of the type: `type_of` works fine with custom types and returns the name of the type. If `register_type_with_name` is used to register the custom type
with a special "pretty-print" name, `type_of` will return that name instead.
```rust ```rust
let x = new_ts(); let x = new_ts();
print(x.type_of()); // prints "foo::bar::TestStruct" print(x.type_of()); // prints "foo::bar::TestStruct"
// prints "Hello" if TestStruct is registered with
// engine.register_type_with_name::<TestStruct>("Hello")?;
``` ```
If `register_type_with_name` is used to register the custom type with a special "pretty-print" name, `type_of` will return that name instead.
Getters and setters Getters and setters
------------------- -------------------
Similarly, custom types can expose members by registering a `get` and/or `set` function. Similarly, custom types can expose members by registering a `get` and/or `set` function.
For example:
```rust ```rust
#[derive(Clone)] #[derive(Clone)]
struct TestStruct { struct TestStruct {
@ -565,8 +567,8 @@ fn main() -> Result<(), EvalAltResult>
// Then push some initialized variables into the state // Then push some initialized variables into the state
// NOTE: Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64. // NOTE: Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64.
// Better stick to them or it gets hard working with the script. // Better stick to them or it gets hard working with the script.
scope.push("y".into(), 42_i64); scope.push("y", 42_i64);
scope.push("z".into(), 999_i64); scope.push("z", 999_i64);
// First invocation // First invocation
engine.eval_with_scope::<()>(&mut scope, r" engine.eval_with_scope::<()>(&mut scope, r"
@ -684,7 +686,7 @@ Numeric operators generally follow C styles.
| `%` | Modulo (remainder) | | | `%` | Modulo (remainder) | |
| `~` | Power | | | `~` | Power | |
| `&` | Binary _And_ bit-mask | Yes | | `&` | Binary _And_ bit-mask | Yes |
| `|` | Binary _Or_ bit-mask | Yes | | `\|` | Binary _Or_ bit-mask | Yes |
| `^` | Binary _Xor_ bit-mask | Yes | | `^` | Binary _Xor_ bit-mask | Yes |
| `<<` | Left bit-shift | Yes | | `<<` | Left bit-shift | Yes |
| `>>` | Right bit-shift | Yes | | `>>` | Right bit-shift | Yes |
@ -948,9 +950,9 @@ Boolean operators
| -------- | ------------------------------- | | -------- | ------------------------------- |
| `!` | Boolean _Not_ | | `!` | Boolean _Not_ |
| `&&` | Boolean _And_ (short-circuits) | | `&&` | Boolean _And_ (short-circuits) |
| `||` | Boolean _Or_ (short-circuits) | | `\|\|` | Boolean _Or_ (short-circuits) |
| `&` | Boolean _And_ (full evaluation) | | `&` | Boolean _And_ (full evaluation) |
| `|` | Boolean _Or_ (full evaluation) | | `\|` | Boolean _Or_ (full evaluation) |
Double boolean operators `&&` and `||` _short-circuit_, meaning that the second operand will not be evaluated Double boolean operators `&&` and `||` _short-circuit_, meaning that the second operand will not be evaluated
if the first one already proves the condition wrong. if the first one already proves the condition wrong.

View File

@ -1026,30 +1026,29 @@ impl Engine<'_> {
// Block scope // Block scope
Stmt::Block(block, _) => { Stmt::Block(block, _) => {
let prev_len = scope.len(); let prev_len = scope.len();
let mut last_result: Result<Dynamic, EvalAltResult> = Ok(().into_dynamic()); let mut result: Result<Dynamic, EvalAltResult> = Ok(().into_dynamic());
for block_stmt in block.iter() { for stmt in block.iter() {
last_result = self.eval_stmt(scope, block_stmt); result = self.eval_stmt(scope, stmt);
if let Err(x) = last_result { if result.is_err() {
last_result = Err(x);
break; break;
} }
} }
scope.rewind(prev_len); scope.rewind(prev_len);
last_result result
} }
// If-else statement // If-else statement
Stmt::IfElse(guard, body, else_body) => self Stmt::IfElse(guard, if_body, else_body) => self
.eval_expr(scope, guard)? .eval_expr(scope, guard)?
.downcast::<bool>() .downcast::<bool>()
.map_err(|_| EvalAltResult::ErrorIfGuard(guard.position())) .map_err(|_| EvalAltResult::ErrorIfGuard(guard.position()))
.and_then(|guard_val| { .and_then(|guard_val| {
if *guard_val { if *guard_val {
self.eval_stmt(scope, body) self.eval_stmt(scope, if_body)
} else if let Some(stmt) = else_body { } else if let Some(stmt) = else_body {
self.eval_stmt(scope, stmt.as_ref()) self.eval_stmt(scope, stmt.as_ref())
} else { } else {

View File

@ -58,6 +58,8 @@ pub enum ParseErrorType {
/// An open `[` is missing the corresponding closing `]`. /// An open `[` is missing the corresponding closing `]`.
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
MissingRightBracket(String), MissingRightBracket(String),
/// A list of expressions is missing the separating ','.
MissingComma(String),
/// An expression in function call arguments `()` has syntax error. /// An expression in function call arguments `()` has syntax error.
MalformedCallExpr(String), MalformedCallExpr(String),
/// An expression in indexing brackets `[]` has syntax error. /// An expression in indexing brackets `[]` has syntax error.
@ -116,6 +118,7 @@ impl ParseError {
ParseErrorType::MissingRightBrace(_) => "Expecting '}'", ParseErrorType::MissingRightBrace(_) => "Expecting '}'",
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
ParseErrorType::MissingRightBracket(_) => "Expecting ']'", ParseErrorType::MissingRightBracket(_) => "Expecting ']'",
ParseErrorType::MissingComma(_) => "Expecting ','",
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments", ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression", ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
@ -165,6 +168,8 @@ impl fmt::Display for ParseError {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
ParseErrorType::MissingRightBracket(ref s) => write!(f, "{} for {}", self.desc(), s)?, ParseErrorType::MissingRightBracket(ref s) => write!(f, "{} for {}", self.desc(), s)?,
ParseErrorType::MissingComma(ref s) => write!(f, "{} for {}", self.desc(), s)?,
ParseErrorType::AssignmentToConstant(ref s) if s.is_empty() => { ParseErrorType::AssignmentToConstant(ref s) if s.is_empty() => {
write!(f, "{}", self.desc())? write!(f, "{}", self.desc())?
} }

File diff suppressed because it is too large Load Diff