Improve repl and rhai_runner examples with error messages.

This commit is contained in:
Stephen Chung 2020-03-10 11:25:34 +08:00
parent 2d80ee2f18
commit 711cd9bb1c
3 changed files with 154 additions and 51 deletions

View File

@ -55,7 +55,7 @@ Other cool projects to check out:
Examples Examples
-------- --------
The repository contains several examples in the `examples` folder: A number of examples can be found in the `examples` folder:
| Example | Description | | Example | Description |
| -------------------------- | ------------------------------------------------------------------------- | | -------------------------- | ------------------------------------------------------------------------- |
@ -65,7 +65,7 @@ The repository contains several examples in the `examples` folder:
| `reuse_scope` | evaluates two pieces of code in separate runs, but using a common scope | | `reuse_scope` | evaluates two pieces of code in separate runs, but using a common scope |
| `rhai_runner` | runs each filename passed to it as a Rhai script | | `rhai_runner` | runs each filename passed to it as a Rhai script |
| `simple_fn` | shows how to register a Rust function to a Rhai engine | | `simple_fn` | shows how to register a Rust function to a Rhai engine |
| `repl` | a simple REPL, see source code for what it can do at the moment | | `repl` | a simple REPL, interactively evaluate statements from stdin |
Examples can be run with the following command: Examples can be run with the following command:
@ -73,10 +73,13 @@ Examples can be run with the following command:
cargo run --example name cargo run --example name
``` ```
The `repl` example is a particularly good one as it allows you to interactively try out Rhai's
language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop).
Example Scripts Example Scripts
--------------- ---------------
We also have a few examples scripts that showcase Rhai's features, all stored in the `scripts` folder: There are also a number of examples scripts that showcase Rhai's features, all in the `scripts` folder:
| Script | Description | | Script | Description |
| --------------------- | ------------------------------------------------------------- | | --------------------- | ------------------------------------------------------------- |
@ -96,8 +99,7 @@ We also have a few examples scripts that showcase Rhai's features, all stored in
| `string.rhai` | string operations | | `string.rhai` | string operations |
| `while.rhai` | while loop | | `while.rhai` | while loop |
To run the scripts, you can either make your own tiny program, or make use of the `rhai_runner` To run the scripts, either make a tiny program or use of the `rhai_runner` example:
example program:
```bash ```bash
cargo run --example rhai_runner scripts/any_script.rhai cargo run --example rhai_runner scripts/any_script.rhai
@ -106,12 +108,13 @@ cargo run --example rhai_runner scripts/any_script.rhai
Hello world Hello world
----------- -----------
To get going with Rhai, you create an instance of the scripting engine and then run eval. To get going with Rhai, create an instance of the scripting engine and then call `eval`:
```rust ```rust
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult};
fn main() -> Result<(), EvalAltResult> { fn main() -> Result<(), EvalAltResult>
{
let mut engine = Engine::new(); let mut engine = Engine::new();
let result = engine.eval::<i64>("40 + 2")?; let result = engine.eval::<i64>("40 + 2")?;
@ -232,7 +235,8 @@ fn get_an_any() -> Dynamic {
Box::new(42_i64) Box::new(42_i64)
} }
fn main() -> Result<(), EvalAltResult> { fn main() -> Result<(), EvalAltResult>
{
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_fn("add", add); engine.register_fn("add", add);
@ -278,7 +282,8 @@ fn showit<T: Display>(x: &mut T) -> () {
println!("{}", x) println!("{}", x)
} }
fn main() { fn main()
{
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_fn("print", showit as fn(x: &mut i64)->()); engine.register_fn("print", showit as fn(x: &mut i64)->());
@ -310,7 +315,8 @@ fn safe_divide(x: i64, y: i64) -> Result<i64, EvalAltResult> {
} }
} }
fn main() { fn main()
{
let mut engine = Engine::new(); let mut engine = Engine::new();
// Fallible functions that return Result values must use register_result_fn() // Fallible functions that return Result values must use register_result_fn()
@ -360,7 +366,8 @@ impl TestStruct {
} }
} }
fn main() -> Result<(), EvalAltResult> { fn main() -> Result<(), EvalAltResult>
{
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
@ -492,7 +499,8 @@ In this example, we first create a state with a few initialized variables, then
```rust ```rust
use rhai::{Engine, Scope, EvalAltResult}; use rhai::{Engine, Scope, EvalAltResult};
fn main() -> Result<(), EvalAltResult> { fn main() -> Result<(), EvalAltResult>
{
let mut engine = Engine::new(); let mut engine = Engine::new();
// First create the state // First create the state
@ -505,13 +513,15 @@ fn main() -> Result<(), EvalAltResult> {
scope.push("z".into(), 999_i64); scope.push("z".into(), 999_i64);
// First invocation // First invocation
engine.eval_with_scope::<()>(&mut scope, r" // (the second boolean argument indicates that we don't need to retain function definitions
// because we didn't declare any!)
engine.eval_with_scope::<()>(&mut scope, false, r"
let x = 4 + 5 - y + z; let x = 4 + 5 - y + z;
y = 1; y = 1;
")?; ")?;
// Second invocation using the same state // Second invocation using the same state
let result = engine.eval_with_scope::<i64>(&mut scope, "x")?; let result = engine.eval_with_scope::<i64>(&mut scope, false, "x")?;
println!("result: {}", result); // should print 966 println!("result: {}", result); // should print 966
@ -545,7 +555,7 @@ let /* intruder comment */ name = "Bob";
Variables Variables
--------- ---------
Variables in `Rhai` follow normal naming rules (i.e. must contain only ASCII letters, digits and '`_`' underscores). Variables in Rhai follow normal naming rules (i.e. must contain only ASCII letters, digits and '`_`' underscores).
```rust ```rust
let x = 3; let x = 3;

View File

@ -1,25 +1,66 @@
use rhai::{Engine, RegisterFn, Scope}; use rhai::{Engine, EvalAltResult, Scope};
use std::io::{stdin, stdout, Write}; use std::{
use std::process::exit; io::{stdin, stdout, Write},
iter,
};
pub fn main() { fn print_error(input: &str, err: EvalAltResult) {
fn padding(pad: &str, len: usize) -> String {
iter::repeat(pad).take(len).collect::<String>()
}
let lines: Vec<_> = input.split("\n").collect();
// Print error
match err.position() {
p if p.is_eof() => {
// EOF
let last = lines[lines.len() - 2];
println!("{}", last);
println!("{}^ {}", padding(" ", last.len() - 1), err);
}
p if p.is_none() => {
// No position
println!("{}", err);
}
p => {
// Specific position
let pos_text = format!(
" (line {}, position {})",
p.line().unwrap(),
p.position().unwrap()
);
println!("{}", lines[p.line().unwrap() - 1]);
println!(
"{}^ {}",
padding(" ", p.position().unwrap() - 1),
err.to_string().replace(&pos_text, "")
);
}
}
}
fn main() {
let mut engine = Engine::new(); let mut engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
engine.register_fn("exit", || exit(0)); let mut input = String::new();
loop { loop {
print!("> "); print!("rhai> ");
let mut input = String::new();
stdout().flush().expect("couldn't flush stdout"); stdout().flush().expect("couldn't flush stdout");
if let Err(e) = stdin().read_line(&mut input) { input.clear();
println!("input error: {}", e);
if let Err(err) = stdin().read_line(&mut input) {
println!("input error: {}", err);
} }
if let Err(e) = engine.consume_with_scope(&mut scope, &input) { if let Err(err) = engine.consume_with_scope(&mut scope, true, &input) {
println!("error: {}", e); println!("");
print_error(&input, err);
println!("");
} }
} }
} }

View File

@ -1,27 +1,79 @@
use rhai::{Engine, RegisterFn}; use rhai::{Engine, EvalAltResult};
use std::env; use std::{env, fs::File, io::Read, iter, process::exit};
use std::fmt::Display;
fn showit<T: Display>(x: &mut T) -> () { fn padding(pad: &str, len: usize) -> String {
println!("{}", x) iter::repeat(pad).take(len).collect::<String>()
}
fn eprint_error(input: &str, err: EvalAltResult) {
fn eprint_line(lines: &Vec<&str>, line: usize, pos: usize, err: &str) {
let line_no = format!("{}: ", line);
let pos_text = format!(" (line {}, position {})", line, pos);
eprintln!("{}{}", line_no, lines[line - 1]);
eprintln!(
"{}^ {}",
padding(" ", line_no.len() + pos - 1),
err.replace(&pos_text, "")
);
eprintln!("");
}
let lines: Vec<_> = input.split("\n").collect();
// Print error
match err.position() {
p if p.is_eof() => {
// EOF
let line = lines.len() - 1;
let pos = lines[line - 1].len();
eprint_line(&lines, line, pos, &err.to_string());
}
p if p.is_none() => {
// No position
eprintln!("{}", err);
}
p => {
// Specific position
eprint_line(
&lines,
p.line().unwrap(),
p.position().unwrap(),
&err.to_string(),
)
}
}
} }
fn main() { fn main() {
for fname in env::args().skip(1) { for filename in env::args().skip(1) {
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_fn("print", showit as fn(x: &mut i32) -> ()); let mut f = match File::open(&filename) {
engine.register_fn("print", showit as fn(x: &mut i64) -> ()); Err(err) => {
engine.register_fn("print", showit as fn(x: &mut u32) -> ()); eprintln!("Error reading script file: {}\n{}", filename, err);
engine.register_fn("print", showit as fn(x: &mut u64) -> ()); exit(1);
engine.register_fn("print", showit as fn(x: &mut f32) -> ()); }
engine.register_fn("print", showit as fn(x: &mut f64) -> ()); Ok(f) => f,
engine.register_fn("print", showit as fn(x: &mut bool) -> ()); };
engine.register_fn("print", showit as fn(x: &mut String) -> ());
match engine.eval_file::<()>(&fname) { let mut contents = String::new();
Ok(_) => (),
Err(e) => println!("Error: {}", e), match f.read_to_string(&mut contents) {
Err(err) => {
eprintln!("Error reading script file: {}\n{}", filename, err);
exit(1);
}
_ => (),
}
if let Err(err) = engine.consume(&contents) {
eprintln!("{}", padding("=", filename.len()));
eprintln!("{}", filename);
eprintln!("{}", padding("=", filename.len()));
eprintln!("");
eprint_error(&contents, err);
} }
} }
} }