Skip to content

Commit

Permalink
Merge pull request #525 from schungx/master
Browse files Browse the repository at this point in the history
Release 1.5.0.
  • Loading branch information
schungx committed Feb 15, 2022
2 parents ea9b6b0 + fb9964e commit 40004ec
Show file tree
Hide file tree
Showing 52 changed files with 1,529 additions and 830 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.md
Expand Up @@ -6,6 +6,12 @@ Version 1.5.0

This version adds a debugging interface, which can be used to integrate a debugger.

Based on popular demand, an option is added to throw exceptions when invalid properties are accessed
on object maps (default is to return `()`).

Also based on popular demand, the `REPL` tool now uses
[`rustyline`](https://crates.io/crates/rustyline) for line editing and history.

Bug fixes
---------

Expand All @@ -15,20 +21,25 @@ Bug fixes
* Globally-defined constants are now encapsulated correctly inside a loaded module and no longer spill across call boundaries.
* Type names display is fixed.
* Exceptions thrown inside function calls now unwrap correctly when `catch`-ed.
* Error messages for certain invalid property accesses are fixed.

Script-breaking changes
-----------------------

* For consistency with the `import` statement, the `export` statement no longer exports multiple variables.
* Appending a BLOB to a string (via `+`, `+=`, `append` or string interpolation) now treats the BLOB as a UTF-8 encoded string.
* Appending a string/character to a BLOB (via `+=` or `append`) now adds the string/character as a UTF-8 encoded byte stream.

New features
------------

* A debugging interface is added.
* A new bin tool, `rhai-dbg` (aka _The Rhai Debugger_), is added to showcase the debugging interface.
* A new package, `DebuggingPackage`, is added which contains the `back_trace` function to get the current call stack anywhere in a script.
* `Engine::set_fail_on_invalid_map_property` is added to control whether to raise an error (new `EvalAltResult::ErrorPropertyNotFound`) when invalid properties are accessed on object maps.
* `Engine::set_allow_shadowing` is added to allow/disallow variables _shadowing_, with new errors `EvalAltResult::ErrorVariableExists` and `ParseErrorType::VariableExists`.
* `Engine::on_def_var` allows registering a closure which can decide whether a variable definition is allow to continue, or should fail with an error.
* `Engine::on_def_var` allows registering a closure which can decide whether a variable definition is allow to continue, during compilation or runtime, or should fail with an error (`ParseErrorType::ForbiddenVariable` or `EvalAltResult::ErrorForbiddenVariable`).
* A new syntax for defining custom packages is introduced that removes the need to specify the Rhai crate name (internally uses the `$crate` meta variable).

Enhancements
------------
Expand All @@ -42,6 +53,9 @@ Enhancements
* `Expr::start_position` is added to give the beginning of the expression (not the operator's position).
* `StmtBlock` and `Stmt::Block` now keep the position of the closing `}` as well.
* `EvalAltResult::unwrap_inner` is added to access the base error inside multiple layers of wrappings (e.g. `EvalAltResult::ErrorInFunction`).
* Yet another new syntax is introduced for `def_package!` that further simplifies the old syntax.
* A new method `to_blob` is added to convert a string into a BLOB as UTF-8 encoded bytes.
* A new method `to_array` is added to convert a BLOB into array of integers.

REPL tool changes
-----------------
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -31,7 +31,7 @@ unicode-xid = { version = "0.2", default-features = false, optional = true }
rust_decimal = { version = "1.16", default-features = false, features = ["maths"], optional = true }
# notice that a custom modified version of `rustyline` is used which supports bracketed paste on Windows
# this can be moved to the official version when bracketed paste is added
rustyline = { version = "9", optional = true, git = "https://github.com/schungx/rustyline", branch = "bracketed_paste" }
rustyline = { version = "9", optional = true, git = "https://github.com/schungx/rustyline" }

[dev-dependencies]
serde_bytes = "0.11"
Expand Down
38 changes: 33 additions & 5 deletions examples/README.md
@@ -1,12 +1,40 @@
Sample Applications
===================

Sample applications that use the Rhai scripting engine.
Standard Examples
-----------------

| Example | Description |
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| [`arrays_and_structs`](arrays_and_structs.rs) | shows how to register a Rust type and using it with arrays |
| [`callback`](callback.rs) | shows how to store a Rhai closure and call it later within Rust |
| [`custom_types_and_methods`](custom_types_and_methods.rs) | shows how to register a Rust type and methods/getters/setters for it |
| [`hello`](hello.rs) | simple example that evaluates an expression and prints the result |
| [`reuse_scope`](reuse_scope.rs) | evaluates two pieces of code in separate runs, but using a common `Scope` |
| [`serde`](serde.rs) | example to serialize and deserialize Rust types with [`serde`](https://crates.io/crates/serde) (requires the `serde` feature) |
| [`simple_fn`](simple_fn.rs) | shows how to register a simple Rust function |
| [`strings`](strings.rs) | shows different ways to register Rust functions taking string arguments |
| [`threading`](threading.rs) | shows how to communicate with an `Engine` running in a separate thread via an MPSC channel |

How to Run
----------

```bash
cargo run --example sample_app_to_run
Scriptable Event Handler With State Examples
-------------------------------------------

Because of its popularity, included are sample implementations for the pattern
[_Scriptable Event Handler With State_](https://rhai.rs/book/patterns/events.html) in different styles.

| Example | Handler Script | Description |
| ------------------------------------------ | ------------------------------------------------------------------ | :---------------------------------------------------------: |
| [`event_handler_main`](event_handler_main) | [`event_handler_main/script.rhai`](event_handler_main/script.rhai) | [_Main Style_](https://rhai.rs/book/patterns/events-1.html) |
| [`event_handler_js`](event_handler_js) | [`event_handler_js/script.rhai`](event_handler_js/script.rhai) | [_JS Style_](https://rhai.rs/book/patterns/events-2.html) |
| [`event_handler_map`](event_handler_map) | [`event_handler_map/script.rhai`](event_handler_map/script.rhai) | [_Map Style_](https://rhai.rs/book/patterns/events-3.html) |


Running Examples
----------------

Examples can be run with the following command:

```sh
cargo run --example {example_name}
```
44 changes: 29 additions & 15 deletions examples/arrays_and_structs.rs
@@ -1,29 +1,43 @@
use rhai::{Engine, EvalAltResult};

#[derive(Debug, Clone)]
struct TestStruct {
x: i64,
}
//! An example showing how to register a Rust type and use it with arrays.

impl TestStruct {
pub fn update(&mut self) {
self.x += 1000;
}
pub fn new() -> Self {
Self { x: 1 }
}
}
use rhai::{Engine, EvalAltResult};

#[cfg(not(feature = "no_index"))]
#[cfg(not(feature = "no_object"))]
fn main() -> Result<(), Box<EvalAltResult>> {
#[derive(Debug, Clone)]
struct TestStruct {
x: i64,
}

impl TestStruct {
pub fn new() -> Self {
Self { x: 1 }
}
pub fn update(&mut self) {
self.x += 1000;
}
}

let mut engine = Engine::new();

engine
.register_type::<TestStruct>()
.register_type_with_name::<TestStruct>("TestStruct")
.register_fn("new_ts", TestStruct::new)
.register_fn("update", TestStruct::update);

#[cfg(feature = "metadata")]
{
println!("Functions registered:");

engine
.gen_fn_signatures(false)
.into_iter()
.for_each(|func| println!("{}", func));

println!();
}

let result = engine.eval::<TestStruct>(
"
let x = new_ts();
Expand Down
34 changes: 34 additions & 0 deletions examples/callback.rs
@@ -0,0 +1,34 @@
//! This example stores a Rhai closure for later use as a callback.

use rhai::{Engine, EvalAltResult, FnPtr};

fn main() -> Result<(), Box<EvalAltResult>> {
// This script creates a closure which may capture variables.
let script = "
let x = 20;
// The following closure captures 'x'
return |a, b| (x + a) * b;
";

// To call a Rhai closure at a later time, you'd need three things:
// 1) an `Engine` (with all needed functions registered),
// 2) a compiled `AST`,
// 3) the closure (of type `FnPtr`).
let engine = Engine::new();

let ast = engine.compile(script)?;

let closure = engine.eval_ast::<FnPtr>(&ast)?;

// Create a closure that we can call any time, encapsulating the
// `Engine`, `AST` and `FnPtr`.
let func = move |x: i64, y: i64| -> Result<i64, _> { closure.call(&engine, &ast, (x, y)) };

// Now we can call `func` anywhere just like a normal function!
let result = func(1, 2)?;

println!("The Answer: {}", result); // prints 42

Ok(())
}
61 changes: 43 additions & 18 deletions examples/custom_types_and_methods.rs
@@ -1,38 +1,63 @@
use rhai::{Engine, EvalAltResult};
//! An example showing how to register a Rust type and methods/getters/setters for it.

#[derive(Debug, Clone)]
struct TestStruct {
x: i64,
}
use rhai::{Engine, EvalAltResult};

impl TestStruct {
pub fn update(&mut self) {
self.x += 1000;
#[cfg(not(feature = "no_object"))]
fn main() -> Result<(), Box<EvalAltResult>> {
#[derive(Debug, Clone)]
struct TestStruct {
x: i64,
}

pub fn new() -> Self {
Self { x: 1 }
impl TestStruct {
pub fn new() -> Self {
Self { x: 1 }
}
pub fn update(&mut self) {
self.x += 1000;
}
pub fn calculate(&mut self, data: i64) -> i64 {
self.x * data
}
pub fn get_x(&mut self) -> i64 {
self.x
}
pub fn set_x(&mut self, value: i64) {
self.x = value;
}
}
}

#[cfg(not(feature = "no_object"))]
fn main() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();

engine
.register_type::<TestStruct>()
.register_type_with_name::<TestStruct>("TestStruct")
.register_fn("new_ts", TestStruct::new)
.register_fn("update", TestStruct::update);
.register_fn("update", TestStruct::update)
.register_fn("calc", TestStruct::calculate)
.register_get_set("x", TestStruct::get_x, TestStruct::set_x);

#[cfg(feature = "metadata")]
{
println!("Functions registered:");

engine
.gen_fn_signatures(false)
.into_iter()
.for_each(|func| println!("{}", func));

println!();
}

let result = engine.eval::<TestStruct>(
let result = engine.eval::<i64>(
"
let x = new_ts();
x.x = 42;
x.update();
x
x.calc(x.x)
",
)?;

println!("result: {}", result.x); // prints 1001
println!("result: {}", result); // prints 1085764

Ok(())
}
Expand Down
4 changes: 3 additions & 1 deletion examples/hello.rs
@@ -1,3 +1,5 @@
//! A simple example that evaluates an expression and prints the result.

use rhai::{Engine, EvalAltResult};

fn main() -> Result<(), Box<EvalAltResult>> {
Expand All @@ -7,7 +9,7 @@ fn main() -> Result<(), Box<EvalAltResult>> {

let result = engine.eval::<i64>("40 + 2")?;

println!("Answer: {}", result); // prints 42
println!("The Answer: {}", result); // prints 42

Ok(())
}
2 changes: 2 additions & 0 deletions examples/reuse_scope.rs
@@ -1,3 +1,5 @@
//! An example that evaluates two pieces of code in separate runs, but using a common `Scope`.

use rhai::{Engine, EvalAltResult, Scope};

fn main() -> Result<(), Box<EvalAltResult>> {
Expand Down
2 changes: 2 additions & 0 deletions examples/serde.rs
@@ -1,3 +1,5 @@
//! An example to serialize and deserialize Rust types.

#[cfg(any(not(feature = "serde"), feature = "no_object"))]
fn main() {
println!("This example requires the 'serde' feature to run.");
Expand Down
2 changes: 2 additions & 0 deletions examples/simple_fn.rs
@@ -1,3 +1,5 @@
//! An example showing how to register a simple Rust function.

use rhai::{Engine, EvalAltResult};

fn add(x: i64, y: i64) -> i64 {
Expand Down
6 changes: 4 additions & 2 deletions examples/strings.rs
@@ -1,5 +1,6 @@
///! This example registers a variety of functions that operate on strings.
///! Remember to use `ImmutableString` or `&str` instead of `String` as parameters.
//! An example that registers a variety of functions that operate on strings.
//! Remember to use `ImmutableString` or `&str` instead of `String` as parameters.

use rhai::{Engine, EvalAltResult, ImmutableString, Scope};
use std::io::{stdin, stdout, Write};

Expand All @@ -12,6 +13,7 @@ fn trim_string(s: &mut ImmutableString) {

/// Notice this is different from the built-in Rhai 'len' function for strings
/// which counts the actual number of Unicode _characters_ in a string.
///
/// This version simply counts the number of _bytes_ in the UTF-8 representation.
///
/// This version uses `&str`.
Expand Down
3 changes: 3 additions & 0 deletions examples/threading.rs
@@ -1,3 +1,6 @@
//! An advanced example showing how to communicate with an `Engine` running in a separate thread via
//! an MPSC channel.

use rhai::Engine;

#[cfg(feature = "sync")]
Expand Down

0 comments on commit 40004ec

Please sign in to comment.