diff --git a/README.md b/README.md index a135bcae..71f33028 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Other cool projects to check out: Examples -------- -The repository contains several examples in the `examples` folder: +A number of examples can be found in the `examples` folder: | 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 | | `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 | -| `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: @@ -73,10 +73,13 @@ Examples can be run with the following command: 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 --------------- -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 | | --------------------- | ------------------------------------------------------------- | @@ -96,8 +99,7 @@ We also have a few examples scripts that showcase Rhai's features, all stored in | `string.rhai` | string operations | | `while.rhai` | while loop | -To run the scripts, you can either make your own tiny program, or make use of the `rhai_runner` -example program: +To run the scripts, either make a tiny program or use of the `rhai_runner` example: ```bash cargo run --example rhai_runner scripts/any_script.rhai @@ -106,12 +108,13 @@ cargo run --example rhai_runner scripts/any_script.rhai 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 use rhai::{Engine, EvalAltResult}; -fn main() -> Result<(), EvalAltResult> { +fn main() -> Result<(), EvalAltResult> +{ let mut engine = Engine::new(); let result = engine.eval::("40 + 2")?; @@ -232,7 +235,8 @@ fn get_an_any() -> Dynamic { Box::new(42_i64) } -fn main() -> Result<(), EvalAltResult> { +fn main() -> Result<(), EvalAltResult> +{ let mut engine = Engine::new(); engine.register_fn("add", add); @@ -278,7 +282,8 @@ fn showit(x: &mut T) -> () { println!("{}", x) } -fn main() { +fn main() +{ let mut engine = Engine::new(); engine.register_fn("print", showit as fn(x: &mut i64)->()); @@ -310,7 +315,8 @@ fn safe_divide(x: i64, y: i64) -> Result { } } -fn main() { +fn main() +{ let mut engine = Engine::new(); // 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(); engine.register_type::(); @@ -492,7 +499,8 @@ In this example, we first create a state with a few initialized variables, then ```rust use rhai::{Engine, Scope, EvalAltResult}; -fn main() -> Result<(), EvalAltResult> { +fn main() -> Result<(), EvalAltResult> +{ let mut engine = Engine::new(); // First create the state @@ -505,13 +513,15 @@ fn main() -> Result<(), EvalAltResult> { scope.push("z".into(), 999_i64); // 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; y = 1; ")?; // Second invocation using the same state - let result = engine.eval_with_scope::(&mut scope, "x")?; + let result = engine.eval_with_scope::(&mut scope, false, "x")?; println!("result: {}", result); // should print 966 @@ -545,7 +555,7 @@ let /* intruder comment */ name = "Bob"; 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 let x = 3; diff --git a/examples/repl.rs b/examples/repl.rs index 1990afed..a4221614 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -1,25 +1,66 @@ -use rhai::{Engine, RegisterFn, Scope}; -use std::io::{stdin, stdout, Write}; -use std::process::exit; +use rhai::{Engine, EvalAltResult, Scope}; +use std::{ + io::{stdin, stdout, Write}, + iter, +}; -pub fn main() { - let mut engine = Engine::new(); - let mut scope = Scope::new(); +fn print_error(input: &str, err: EvalAltResult) { + fn padding(pad: &str, len: usize) -> String { + iter::repeat(pad).take(len).collect::() + } - engine.register_fn("exit", || exit(0)); + let lines: Vec<_> = input.split("\n").collect(); - loop { - print!("> "); - - let mut input = String::new(); - stdout().flush().expect("couldn't flush stdout"); - - if let Err(e) = stdin().read_line(&mut input) { - println!("input error: {}", e); + // 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() + ); - if let Err(e) = engine.consume_with_scope(&mut scope, &input) { - println!("error: {}", e); + 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 scope = Scope::new(); + + let mut input = String::new(); + + loop { + print!("rhai> "); + stdout().flush().expect("couldn't flush stdout"); + + input.clear(); + + if let Err(err) = stdin().read_line(&mut input) { + println!("input error: {}", err); + } + + if let Err(err) = engine.consume_with_scope(&mut scope, true, &input) { + println!(""); + print_error(&input, err); + println!(""); } } } diff --git a/examples/rhai_runner.rs b/examples/rhai_runner.rs index 2e0da794..d535f078 100644 --- a/examples/rhai_runner.rs +++ b/examples/rhai_runner.rs @@ -1,27 +1,79 @@ -use rhai::{Engine, RegisterFn}; -use std::env; -use std::fmt::Display; +use rhai::{Engine, EvalAltResult}; +use std::{env, fs::File, io::Read, iter, process::exit}; -fn showit(x: &mut T) -> () { - println!("{}", x) +fn padding(pad: &str, len: usize) -> String { + iter::repeat(pad).take(len).collect::() } -fn main() { - for fname in env::args().skip(1) { - let mut engine = Engine::new(); +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); - engine.register_fn("print", showit as fn(x: &mut i32) -> ()); - engine.register_fn("print", showit as fn(x: &mut i64) -> ()); - engine.register_fn("print", showit as fn(x: &mut u32) -> ()); - engine.register_fn("print", showit as fn(x: &mut u64) -> ()); - engine.register_fn("print", showit as fn(x: &mut f32) -> ()); - engine.register_fn("print", showit as fn(x: &mut f64) -> ()); - engine.register_fn("print", showit as fn(x: &mut bool) -> ()); - engine.register_fn("print", showit as fn(x: &mut String) -> ()); + eprintln!("{}{}", line_no, lines[line - 1]); + eprintln!( + "{}^ {}", + padding(" ", line_no.len() + pos - 1), + err.replace(&pos_text, "") + ); + eprintln!(""); + } - match engine.eval_file::<()>(&fname) { - Ok(_) => (), - Err(e) => println!("Error: {}", e), + 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() { + for filename in env::args().skip(1) { + let mut engine = Engine::new(); + + let mut f = match File::open(&filename) { + Err(err) => { + eprintln!("Error reading script file: {}\n{}", filename, err); + exit(1); + } + Ok(f) => f, + }; + + let mut contents = String::new(); + + 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); } } }