Refine docs.

This commit is contained in:
Stephen Chung 2020-06-22 22:02:49 +08:00
parent b08f85a8b1
commit a9b168ba99
32 changed files with 260 additions and 221 deletions

View File

@ -21,7 +21,7 @@ Supported targets and builds
Features Features
-------- --------
* Easy-to-use language similar to JS+Rust with dynamic typing. * Easy-to-use language similar to JavaScript+Rust with dynamic typing.
* Tight integration with native Rust [functions](https://schungx.github.io/rhai/rust/functions.html) and [types]([#custom-types-and-methods](https://schungx.github.io/rhai/rust/custom.html)), including [getters/setters](https://schungx.github.io/rhai/rust/getters-setters.html), [methods](https://schungx.github.io/rhai/rust/custom.html) and [indexers](https://schungx.github.io/rhai/rust/indexers.html). * Tight integration with native Rust [functions](https://schungx.github.io/rhai/rust/functions.html) and [types]([#custom-types-and-methods](https://schungx.github.io/rhai/rust/custom.html)), including [getters/setters](https://schungx.github.io/rhai/rust/getters-setters.html), [methods](https://schungx.github.io/rhai/rust/custom.html) and [indexers](https://schungx.github.io/rhai/rust/indexers.html).
* Freely pass Rust variables/constants into a script via an external [`Scope`](https://schungx.github.io/rhai/rust/scope.html). * Freely pass Rust variables/constants into a script via an external [`Scope`](https://schungx.github.io/rhai/rust/scope.html).
* Easily [call a script-defined function](https://schungx.github.io/rhai/engine/call-fn.html) from Rust. * Easily [call a script-defined function](https://schungx.github.io/rhai/engine/call-fn.html) from Rust.

View File

@ -6,7 +6,7 @@ Features
Easy Easy
---- ----
* Easy-to-use language similar to JS+Rust with dynamic typing. * Easy-to-use language similar to JavaScript+Rust with dynamic typing.
* Tight integration with native Rust [functions]({{rootUrl}}/rust/functions.md) and [types]({{rootUrl}}/rust/custom.md), including [getters/setters]({{rootUrl}}/rust/getters-setters.md), [methods]({{rootUrl}}/rust/custom.md) and [indexers]({{rootUrl}}/rust/indexers.md). * Tight integration with native Rust [functions]({{rootUrl}}/rust/functions.md) and [types]({{rootUrl}}/rust/custom.md), including [getters/setters]({{rootUrl}}/rust/getters-setters.md), [methods]({{rootUrl}}/rust/custom.md) and [indexers]({{rootUrl}}/rust/indexers.md).

View File

@ -26,7 +26,7 @@ Due to this intended usage, Rhai deliberately keeps the language simple and smal
such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc. such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc.
Avoid the temptation to write full-fledge program logic entirely in Rhai - that use case is best fulfilled by Avoid the temptation to write full-fledge program logic entirely in Rhai - that use case is best fulfilled by
more complete languages such as JS or Lua. more complete languages such as JavaScript or Lua.
Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call. Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call.
All your core functionalities should be in Rust. All your core functionalities should be in Rust.

View File

@ -4,8 +4,8 @@ Literals Syntax
{{#include ../links.md}} {{#include ../links.md}}
| Type | Literal syntax | | Type | Literal syntax |
| :--------------------------------: | :---------------------------------------: | | :--------------------------------: | :------------------------------------------------------------------------------: |
| `INT` | `42`, `-123`, `0` | | `INT` | `42`, `-123`, `0`,<br/>`0x????..` (hex), `0b????..` (binary), `0o????..` (octal) |
| `FLOAT` | `42.0`, `-123.456`, `0.0` | | `FLOAT` | `42.0`, `-123.456`, `0.0` |
| [String] | `"... \x?? \u???? \U???????? ..."` | | [String] | `"... \x?? \u???? \U???????? ..."` |
| Character | `"... \x?? \u???? \U???????? ..."` | | Character | `"... \x?? \u???? \U???????? ..."` |

View File

@ -4,7 +4,7 @@ Operators
{{#include ../links.md}} {{#include ../links.md}}
| Operator | Description | Binary? | | Operator | Description | Binary? |
| :---------------: | ---------------------------- | :-----: | | :---------------: | ------------------------------ | :-----: |
| `+` | Add | Yes | | `+` | Add | Yes |
| `-` | Subtract, Minus | Yes/No | | `-` | Subtract, Minus | Yes/No |
| `*` | Multiply | Yes | | `*` | Multiply | Yes |
@ -13,9 +13,9 @@ Operators
| `~` | Power | Yes | | `~` | Power | Yes |
| `>>` | Right bit-shift | Yes | | `>>` | Right bit-shift | Yes |
| `<<` | Left bit-shift | Yes | | `<<` | Left bit-shift | Yes |
| `&` | Bit-wise AND, Boolean AND | Yes | | `&` | Bit-wise _And_, Boolean _And_ | Yes |
| <code>\|</code> | Bit-wise OR, Boolean OR | Yes | | <code>\|</code> | Bit-wise _Or_, Boolean _Or_ | Yes |
| `^` | Bit-wise XOR | Yes | | `^` | Bit-wise _Xor_ | Yes |
| `==` | Equals to | Yes | | `==` | Equals to | Yes |
| `~=` | Not equals to | Yes | | `~=` | Not equals to | Yes |
| `>` | Greater than | Yes | | `>` | Greater than | Yes |
@ -23,8 +23,8 @@ Operators
| `<` | Less than | Yes | | `<` | Less than | Yes |
| `<=` | Less than or equals to | Yes | | `<=` | Less than or equals to | Yes |
| `>=` | Greater than or equals to | Yes | | `>=` | Greater than or equals to | Yes |
| `&&` | Boolean AND (short-circuits) | Yes | | `&&` | Boolean _And_ (short-circuits) | Yes |
| <code>\|\|</code> | Boolean OR (short-circuits) | Yes | | <code>\|\|</code> | Boolean _Or_ (short-circuits) | Yes |
| `~` | Boolean NOT | No | | `!` | Boolean _Not_ | No |
| `[` .. `]` | Indexing | Yes | | `[` .. `]` | Indexing | Yes |
| `.` | Property access, Method call | Yes | | `.` | Property access, Method call | Yes |

View File

@ -38,6 +38,7 @@ The following methods (mostly defined in the [`BasicArrayPackage`]({{rootUrl}}/r
| `clear` | _none_ | empties the array | | `clear` | _none_ | empties the array |
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) | | `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
Examples Examples
-------- --------

View File

@ -17,7 +17,7 @@ let script = "let y = x;"; // build a script
script += "y += foo(y);"; script += "y += foo(y);";
script += "x + y"; script += "x + y";
let result = eval(script); // <- look, JS, we can also do this! let result = eval(script); // <- look, JavaScript, we can also do this!
print("Answer: " + result); // prints 42 print("Answer: " + result); // prints 42

View File

@ -105,4 +105,4 @@ Unlike C/C++, functions in Rhai can be defined _anywhere_ at global level.
A function does not need to be defined prior to being used in a script; A function does not need to be defined prior to being used in a script;
a statement in the script can freely call a function defined afterwards. a statement in the script can freely call a function defined afterwards.
This is similar to Rust and many other modern languages, such as JS's `function` keyword. This is similar to Rust and many other modern languages, such as JavaScript's `function` keyword.

View File

@ -43,18 +43,14 @@ impl ModuleResolver for MyModuleResolver {
} }
} }
fn main() -> Result<(), Box<EvalAltResult>> { let mut engine = Engine::new();
let mut engine = Engine::new();
// Set the custom module resolver into the 'Engine'. // Set the custom module resolver into the 'Engine'.
engine.set_module_resolver(Some(MyModuleResolver {})); engine.set_module_resolver(Some(MyModuleResolver {}));
engine.consume(r#" engine.consume(r#"
import "hello" as foo; // this 'import' statement will call import "hello" as foo; // this 'import' statement will call
// 'MyModuleResolver::resolve' with "hello" as path // 'MyModuleResolver::resolve' with "hello" as path
foo:bar(); foo:bar();
"#)?; "#)?;
Ok(())
}
``` ```

View File

@ -23,16 +23,16 @@ Binary Operators
---------------- ----------------
| Operator | Description | Integers only | | Operator | Description | Integers only |
| -------- | ---------------------------------------------------- | :-----------: | | --------------- | ---------------------------------------------------- | :-----------: |
| `+` | Plus | | | `+` | Plus | |
| `-` | Minus | | | `-` | Minus | |
| `*` | Multiply | | | `*` | Multiply | |
| `/` | Divide (integer division if acting on integer types) | | | `/` | Divide (integer division if acting on integer types) | |
| `%` | Modulo (remainder) | | | `%` | Modulo (remainder) | |
| `~` | Power | | | `~` | Power | |
| `&` | Binary _And_ bit-mask | Yes | | `&` | Bit-wise _And_ | Yes |
| `|` | Binary _Or_ bit-mask | Yes | | <code>\|</code> | Bit-wise _Or_ | Yes |
| `^` | Binary _Xor_ bit-mask | Yes | | `^` | Bit-wise _Xor_ | Yes |
| `<<` | Left bit-shift | Yes | | `<<` | Left bit-shift | Yes |
| `>>` | Right bit-shift | Yes | | `>>` | Right bit-shift | Yes |

View File

@ -61,7 +61,7 @@ Examples
let y = #{ // object map literal with 3 properties let y = #{ // object map literal with 3 properties
a: 1, a: 1,
bar: "hello", bar: "hello",
"baz!$@": 123.456, // like JS, you can use any string as property names... "baz!$@": 123.456, // like JavaScript, you can use any string as property names...
"": false, // even the empty string! "": false, // even the empty string!
a: 42 // <- syntax error: duplicated property name a: 42 // <- syntax error: duplicated property name

View File

@ -14,7 +14,7 @@ The following primitive types are supported natively:
| **Immutable Unicode string** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. | | **Immutable Unicode string** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. |
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` | | **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` | | **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
| **Timestamp** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md), disabled with [`no_std`]) | `std::time::Instant` ([instant::Instant](https://crates.io/crates/instant) if not [WASM] build) | `"timestamp"` | _not supported_ | | **Timestamp** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md), disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`](https://crates.io/crates/instant) if not [WASM] build) | `"timestamp"` | _not supported_ |
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ | | **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. | | **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. | | **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |

View File

@ -69,6 +69,7 @@
[`Module`]: {{rootUrl}}/language/modules/index.md [`Module`]: {{rootUrl}}/language/modules/index.md
[module]: {{rootUrl}}/language/modules/index.md [module]: {{rootUrl}}/language/modules/index.md
[modules]: {{rootUrl}}/language/modules/index.md [modules]: {{rootUrl}}/language/modules/index.md
[module resolver]: {{rootUrl}}/language/modules/imp-resolver.md
[`export`]: {{rootUrl}}/language/modules/export.md [`export`]: {{rootUrl}}/language/modules/export.md
[`import`]: {{rootUrl}}/language/modules/import.md [`import`]: {{rootUrl}}/language/modules/import.md

View File

@ -1 +0,0 @@
# Built-in Packages

View File

@ -26,21 +26,16 @@ impl TestStruct {
} }
} }
fn main() -> Result<(), Box<EvalAltResult>> let mut engine = Engine::new();
{
let engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
engine.register_fn("update", TestStruct::update); engine.register_fn("update", TestStruct::update);
engine.register_fn("new_ts", TestStruct::new); engine.register_fn("new_ts", TestStruct::new);
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?; let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
println!("result: {}", result.field); // prints 42 println!("result: {}", result.field); // prints 42
Ok(())
}
``` ```
Register a Custom Type Register a Custom Type
@ -66,7 +61,7 @@ impl TestStruct {
} }
} }
let engine = Engine::new(); let mut engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
``` ```
@ -102,8 +97,9 @@ println!("result: {}", result.field); // prints 42
Method-Call Style vs. Function-Call Style Method-Call Style vs. Function-Call Style
---------------------------------------- ----------------------------------------
In fact, any function with a first argument that is a `&mut` reference can be used as method calls because In fact, any function with a first argument that is a `&mut` reference can be used
internally they are the same thing: methods on a type is implemented as a functions taking a `&mut` first argument. as method calls because internally they are the same thing: methods on a type is
implemented as a functions taking a `&mut` first argument.
```rust ```rust
fn foo(ts: &mut TestStruct) -> i64 { fn foo(ts: &mut TestStruct) -> i64 {
@ -119,8 +115,8 @@ let result = engine.eval::<i64>(
println!("result: {}", result); // prints 1 println!("result: {}", result); // prints 1
``` ```
Under [`no_object`], however, the _method_ style of function calls (i.e. calling a function as an object-method) Under [`no_object`], however, the _method_ style of function calls
is no longer supported. (i.e. calling a function as an object-method) is no longer supported.
```rust ```rust
// Below is a syntax error under 'no_object' because 'clear' cannot be called in method style. // Below is a syntax error under 'no_object' because 'clear' cannot be called in method style.

View File

@ -8,11 +8,6 @@ If a function is _fallible_ (i.e. it returns a `Result<_, Error>`), it can be re
The function must return `Result<Dynamic, Box<EvalAltResult>>`. The function must return `Result<Dynamic, Box<EvalAltResult>>`.
`Box<EvalAltResult>` implements `From<&str>` and `From<String>` etc.
and the error text gets converted into `Box<EvalAltResult::ErrorRuntime>`.
The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely.
```rust ```rust
use rhai::{Engine, EvalAltResult, Position}; use rhai::{Engine, EvalAltResult, Position};
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn' use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
@ -27,15 +22,20 @@ fn safe_divide(x: i64, y: i64) -> Result<Dynamic, Box<EvalAltResult>> {
} }
} }
fn main() let mut engine = Engine::new();
{
let 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()
engine.register_result_fn("divide", safe_divide); engine.register_result_fn("divide", safe_divide);
if let Err(error) = engine.eval::<i64>("divide(40, 0)") { if let Err(error) = engine.eval::<i64>("divide(40, 0)") {
println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)") println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
}
} }
``` ```
Create a `Box<EvalAltResult>`
----------------------------
`Box<EvalAltResult>` implements `From<&str>` and `From<String>` etc.
and the error text gets converted into `Box<EvalAltResult::ErrorRuntime>`.
The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely.

View File

@ -29,30 +29,25 @@ fn get_any_value() -> Result<Dynamic, Box<EvalAltResult>> {
Ok((42_i64).into()) // standard types can use 'into()' Ok((42_i64).into()) // standard types can use 'into()'
} }
fn main() -> Result<(), Box<EvalAltResult>> let mut engine = Engine::new();
{
let engine = Engine::new();
engine.register_fn("add", add_len); engine.register_fn("add", add_len);
engine.register_fn("add_str", add_len_str); engine.register_fn("add_str", add_len_str);
let result = engine.eval::<i64>(r#"add(40, "xx")"#)?; let result = engine.eval::<i64>(r#"add(40, "xx")"#)?;
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
let result = engine.eval::<i64>(r#"add_str(40, "xx")"#)?; let result = engine.eval::<i64>(r#"add_str(40, "xx")"#)?;
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
// Functions that return Dynamic values must use register_result_fn() // Functions that return Dynamic values must use register_result_fn()
engine.register_result_fn("get_any_value", get_any_value); engine.register_result_fn("get_any_value", get_any_value);
let result = engine.eval::<i64>("get_any_value()")?; let result = engine.eval::<i64>("get_any_value()")?;
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
Ok(())
}
``` ```
To create a [`Dynamic`] value, use the `Dynamic::from` method. To create a [`Dynamic`] value, use the `Dynamic::from` method.

View File

@ -17,14 +17,11 @@ fn show_it<T: Display>(x: &mut T) {
println!("put up a good show: {}!", x) println!("put up a good show: {}!", x)
} }
fn main() let mut engine = Engine::new();
{
let engine = Engine::new();
engine.register_fn("print", show_it::<i64>); engine.register_fn("print", show_it::<i64>);
engine.register_fn("print", show_it::<bool>); engine.register_fn("print", show_it::<bool>);
engine.register_fn("print", show_it::<ImmutableString>); engine.register_fn("print", show_it::<ImmutableString>);
}
``` ```
The above example shows how to register multiple functions The above example shows how to register multiple functions

View File

@ -28,7 +28,7 @@ impl TestStruct {
} }
} }
let engine = Engine::new(); let mut engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();

View File

@ -9,6 +9,9 @@ A custom type with an indexer function defined can use the bracket '`[]`' notati
Indexers are disabled when the [`no_index`] feature is used. Indexers are disabled when the [`no_index`] feature is used.
For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for
[arrays] and [object maps].
```rust ```rust
#[derive(Clone)] #[derive(Clone)]
struct TestStruct { struct TestStruct {
@ -28,7 +31,7 @@ impl TestStruct {
} }
} }
let engine = Engine::new(); let mut engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
@ -42,6 +45,3 @@ let result = engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?;
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
``` ```
For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for
[arrays] and [object maps].

View File

@ -17,6 +17,10 @@ Similarly, comparison operators including `==`, `!=` etc. are all implemented as
with the stark exception of `&&` and `||`. Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators), with the stark exception of `&&` and `||`. Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators),
`&&` and `||` are handled specially and _not_ via a function; as a result, overriding them has no effect at all. `&&` and `||` are handled specially and _not_ via a function; as a result, overriding them has no effect at all.
Overload Operator via Rust Function
----------------------------------
Operator functions cannot be defined as a script function (because operators syntax are not valid function names). Operator functions cannot be defined as a script function (because operators syntax are not valid function names).
However, operator functions _can_ be registered to the [`Engine`] via the methods However, operator functions _can_ be registered to the [`Engine`] via the methods
@ -48,6 +52,10 @@ engine.register_fn("+", mixed_add); // register '+' operator for
let result: i64 = engine.eval("1 + 1.0"); // prints 2.0 (normally an error) let result: i64 = engine.eval("1 + 1.0"); // prints 2.0 (normally an error)
``` ```
Considerations
--------------
Normally, use operator overloading for [custom types] only. Normally, use operator overloading for [custom types] only.
Be very careful when overriding built-in operators because script authors expect standard operators to behave in a Be very careful when overriding built-in operators because script authors expect standard operators to behave in a

View File

@ -3,8 +3,9 @@ Printing for Custom Types
{{#include ../links.md}} {{#include ../links.md}}
To use custom types for [`print`] and [`debug`], or convert its value into a [string], it is necessary that the following To use custom types for [`print`] and [`debug`], or convert its value into a [string],
functions be registered (assuming the custom type is `T : Display + Debug`): it is necessary that the following functions be registered (assuming the custom type
is `T : Display + Debug`):
| Function | Signature | Typical implementation | Usage | | Function | Signature | Typical implementation | Usage |
| ----------- | ------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- | | ----------- | ------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |

View File

@ -20,40 +20,35 @@ then the same state is threaded through multiple invocations:
```rust ```rust
use rhai::{Engine, Scope, EvalAltResult}; use rhai::{Engine, Scope, EvalAltResult};
fn main() -> Result<(), Box<EvalAltResult>> let engine = Engine::new();
{
let engine = Engine::new();
// First create the state // First create the state
let mut scope = Scope::new(); let mut scope = Scope::new();
// Then push (i.e. add) some initialized variables into the state. // Then push (i.e. add) some initialized variables into the state.
// Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64. // 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", 42_i64); scope.push("y", 42_i64);
scope.push("z", 999_i64); scope.push("z", 999_i64);
// 'set_value' adds a variable when one doesn't exist // 'set_value' adds a variable when one doesn't exist
scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str' scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str'
// First invocation // First invocation
engine.eval_with_scope::<()>(&mut scope, r" engine.eval_with_scope::<()>(&mut scope, r"
let x = 4 + 5 - y + z + s.len; let x = 4 + 5 - y + z + s.len;
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, "x")?;
println!("result: {}", result); // prints 979 println!("result: {}", result); // prints 979
// Variable y is changed in the script - read it with 'get_value' // Variable y is changed in the script - read it with 'get_value'
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 1); assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 1);
// We can modify scope variables directly with 'set_value' // We can modify scope variables directly with 'set_value'
scope.set_value("y", 42_i64); scope.set_value("y", 42_i64);
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 42); assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 42);
Ok(())
}
``` ```

View File

@ -8,28 +8,28 @@ resources used by a script so that it does not consume more resources that it is
The most important resources to watch out for are: The most important resources to watch out for are:
* **Memory**: A malicous script may continuously grow a [string], an [array] or [object map] until all memory is consumed. * **Memory**: A malicious script may continuously grow a [string], an [array] or [object map] until all memory is consumed.
It may also create a large [array] or [object map] literal that exhausts all memory during parsing. It may also create a large [array] or [object map] literal that exhausts all memory during parsing.
* **CPU**: A malicous script may run an infinite tight loop that consumes all CPU cycles. * **CPU**: A malicious script may run an infinite tight loop that consumes all CPU cycles.
* **Time**: A malicous script may run indefinitely, thereby blocking the calling system which is waiting for a result. * **Time**: A malicious script may run indefinitely, thereby blocking the calling system which is waiting for a result.
* **Stack**: A malicous script may attempt an infinite recursive call that exhausts the call stack. * **Stack**: A malicious script may attempt an infinite recursive call that exhausts the call stack.
Alternatively, it may create a degenerated deep expression with so many levels that the parser exhausts the call stack Alternatively, it may create a degenerated deep expression with so many levels that the parser exhausts the call stack
when parsing the expression; or even deeply-nested statement blocks, if nested deep enough. when parsing the expression; or even deeply-nested statement blocks, if nested deep enough.
Another way to cause a stack overflow is to load a [self-referencing module]({{rootUrl}}/language/modules/import.md). Another way to cause a stack overflow is to load a [self-referencing module]({{rootUrl}}/language/modules/import.md).
* **Overflows**: A malicous script may deliberately cause numeric over-flows and/or under-flows, divide by zero, and/or * **Overflows**: A malicious script may deliberately cause numeric over-flows and/or under-flows, divide by zero, and/or
create bad floating-point representations, in order to crash the system. create bad floating-point representations, in order to crash the system.
* **Files**: A malicous script may continuously [`import`] an external module within an infinite loop, * **Files**: A malicious script may continuously [`import`] an external module within an infinite loop,
thereby putting heavy load on the file-system (or even the network if the file is not local). thereby putting heavy load on the file-system (or even the network if the file is not local).
Even when modules are not created from files, they still typically consume a lot of resources to load. Even when modules are not created from files, they still typically consume a lot of resources to load.
* **Data**: A malicous script may attempt to read from and/or write to data that it does not own. If this happens, * **Data**: A malicious script may attempt to read from and/or write to data that it does not own. If this happens,
it is a severe security breach and may put the entire system at risk. it is a severe security breach and may put the entire system at risk.

View File

@ -17,6 +17,16 @@ opt-level = "z" # optimize for size
``` ```
Use `i32` Only
--------------
For embedded systems that must optimize for code size, the architecture is commonly 32-bit.
Use [`only_i32`] to prune away large sections of code implementing functions for other numeric types
(including `i64`).
If, for some reason, 64-bit long integers must be supported, use [`only_i64`] instead of [`only_i32`].
Opt-Out of Features Opt-Out of Features
------------------ ------------------
@ -28,13 +38,17 @@ Omitting arrays ([`no_index`]) yields the most code-size savings, followed by fl
([`no_float`]), checked arithmetic/script resource limits ([`unchecked`]) and finally object maps and custom types ([`no_object`]). ([`no_float`]), checked arithmetic/script resource limits ([`unchecked`]) and finally object maps and custom types ([`no_object`]).
Where the usage scenario does not call for loading externally-defined modules, use [`no_module`] to save some bytes. Where the usage scenario does not call for loading externally-defined modules, use [`no_module`] to save some bytes.
Disable script-defined functions ([`no_function`]) only when the feature is not needed because code size savings is minimal. Disable script-defined functions ([`no_function`]) when the feature is not needed.
Both of these have little code size savings.
Use a Raw [`Engine`] Use a Raw [`Engine`]
------------------- -------------------
[`Engine::new_raw`](#raw-engine) creates a _raw_ engine. [`Engine::new_raw`](#raw-engine) creates a _raw_ engine.
A _raw_ engine supports, out of the box, only a very [restricted set](#built-in-operators) of basic arithmetic and logical operators. A _raw_ engine supports, out of the box, only a very [restricted set]({{rootUrl}}/engine/raw.md#built-in-operators)
of basic arithmetic and logical operators.
Selectively include other necessary functionalities by loading specific [packages] to minimize the footprint. Selectively include other necessary functionalities by loading specific [packages] to minimize the footprint.
Packages are sharable (even across threads via the [`sync`] feature), so they only have to be created once. Packages are sharable (even across threads via the [`sync`] feature), so they only have to be created once.

View File

@ -7,14 +7,17 @@ Use Only One Integer Type
------------------------ ------------------------
Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`). Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`).
If only a single integer type is needed in scripts - most of the time this is the case - it is best to avoid registering If only a single integer type is needed in scripts - most of the time this is the case - it is best to avoid registering
lots of functions related to other integer types that will never be used. As a result, performance should improve. lots of functions related to other integer types that will never be used. As a result, [`Engine`] creation will be faster
because fewer functions need to be loaded.
Use Only 32-Bit Numbers Use Only 32-Bit Numbers
---------------------- ----------------------
If only 32-bit integers are needed - again, most of the time this is the case - using [`only_i32`] disables also `i64`. If only 32-bit integers are needed - again, most of the time this is the case - using [`only_i32`] disables also `i64`.
On 64-bit targets this may not gain much, but on some 32-bit targets this improves performance due to 64-bit arithmetic On 64-bit targets this may not gain much, but on some 32-bit targets this improves performance due to 64-bit arithmetic
requiring more CPU cycles to complete. requiring more CPU cycles to complete.
@ -24,4 +27,5 @@ Minimize Size of [`Dynamic`]
Turning on [`no_float`], and [`only_i32`] makes the key [`Dynamic`] data type only 8 bytes small on 32-bit targets Turning on [`no_float`], and [`only_i32`] makes the key [`Dynamic`] data type only 8 bytes small on 32-bit targets
while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`. while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`.
Making [`Dynamic`] small helps performance due to better cache efficiency. Making [`Dynamic`] small helps performance due to better cache efficiency.

View File

@ -14,8 +14,40 @@ But anyhow, do it because you _can_!
When building for WASM, certain features will not be available, such as the script file API's and loading modules When building for WASM, certain features will not be available, such as the script file API's and loading modules
from external script files. from external script files.
Also look into [minimal builds] to reduce generated WASM size. As of this version, a typical, full-featured
Rhai scripting engine compiles to a single WASM file less than 200KB gzipped. When excluding features that are Size
marginal in WASM environment, the gzipped payload can be further shrunk to 160KB. ----
Also look into [minimal builds] to reduce generated WASM size.
As of this version, a typical, full-featured Rhai scripting engine compiles to a single WASM file
less than 200KB gzipped.
When excluding features that are marginal in WASM environment, the gzipped payload can be
further shrunk to 160KB.
Speed
-----
In benchmark tests, a WASM build runs scripts roughly 1.7-2.2x slower than a native optimized release build. In benchmark tests, a WASM build runs scripts roughly 1.7-2.2x slower than a native optimized release build.
Common Features
---------------
Some Rhai functionalities are not necessary in a WASM environment, so the following features
are typically used for a WASM build:
| Feature | Description |
| :-----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`unchecked`] | When a WASM module panics, it doesn't crash the entire web app; however this also disables [maximum number of operations] and [progress] tracking so a script can still run indefinitely - the web app must terminate it itself. |
| [`only_i32`] | JavaScript has only one `number` type and we're only supporting `wasm32` here (so far). |
| [`no_module`] | A WASM module cannot load modules from the file system, so usually this is not needed, but the savings are minimal; alternatively, a custom [module resolver] can be provided that loads other Rhai scripts. |
The following features are typically _not_ used because they don't make sense in a WASM build:
| Feature | Why unnecessary |
| :--------: | ------------------------------- |
| [`sync`] | WASM is single-threaded. |
| [`no_std`] | `std` lib works fine with WASM. |

View File

@ -6,16 +6,16 @@ Rust Examples
A number of examples can be found in the `examples` folder: A number of examples can be found in the `examples` folder:
| Example | Description | | Example | Description |
| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | shows how to register a custom Rust type and using [arrays] on it | | [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | Shows how to register a custom Rust type and using [arrays] on it. |
| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | shows how to register a custom Rust type and methods for it | | [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | Shows how to register a custom Rust type and methods for it. |
| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | simple example that evaluates an expression and prints the result | | [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | Simple example that evaluates an expression and prints the result. |
| [`no_std`](https://github.com/jonathandturner/rhai/tree/master/examples/no_std.rs) | example to test out `no-std` builds | | [`no_std`](https://github.com/jonathandturner/rhai/tree/master/examples/no_std.rs) | Example to test out `no-std` builds. |
| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | evaluates two pieces of code in separate runs, but using a common [`Scope`] | | [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | Evaluates two pieces of code in separate runs, but using a common [`Scope`]. |
| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | runs each filename passed to it as a Rhai script | | [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | Runs each filename passed to it as a Rhai script. |
| [`simple_fn`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | shows how to register a simple function | | [`simple_fn`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | Shows how to register a simple function. |
| [`strings`](https://github.com/jonathandturner/rhai/tree/master/examples/strings.rs) | shows different ways to register functions taking string arguments | | [`strings`](https://github.com/jonathandturner/rhai/tree/master/examples/strings.rs) | Shows different ways to register functions taking string arguments. |
| [`repl`](https://github.com/jonathandturner/rhai/tree/master/examples/repl.rs) | a simple REPL, interactively evaluate statements from stdin | | [`repl`](https://github.com/jonathandturner/rhai/tree/master/examples/repl.rs) | A simple REPL, interactively evaluate statements from stdin. |
The `repl` example is a particularly good one as it allows one to interactively try out Rhai's The `repl` example is a particularly good one as it allows one to interactively try out Rhai's
language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop). language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop).

View File

@ -10,21 +10,21 @@ There are also a number of examples scripts that showcase Rhai's features, all i
| Script | Description | | Script | Description |
| -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [arrays] in Rhai | | [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [Arrays] |
| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | variable declarations | | [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | Variable declarations |
| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | just comments | | [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | Just comments |
| [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`](#for-loop) loops | | [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`](#for-loop) loops |
| [`for2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for2.rhai) | [`for`](#for-loop) loops on [arrays] | | [`for2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for2.rhai) | [`for`](#for-loop) loops on [arrays] |
| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | a [function] without parameters | | [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | A [function] without parameters |
| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | a [function] with two parameters | | [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | A [function] with two parameters |
| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | a [function] with many parameters | | [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | A [function] with many parameters |
| [`if1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/if1.rhai) | [`if`](#if-statement) example | | [`if1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/if1.rhai) | [`if`](#if-statement) example |
| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop | | [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | Count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop |
| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | just simple addition | | [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | Just simple addition |
| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | simple addition and multiplication | | [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | Simple addition and multiplication |
| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | change evaluation order with parenthesis | | [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | Change evaluation order with parenthesis |
| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [string] operations | | [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [String] operations |
| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [string] and [object map] operations | | [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [String] and [object map] operations |
| [`while.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/while.rhai) | [`while`](#while-loop) loop | | [`while.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/while.rhai) | [`while`](#while-loop) loop |
@ -34,11 +34,11 @@ Benchmark Scripts
The following scripts are for benchmarking the speed of Rhai: The following scripts are for benchmarking the speed of Rhai:
| Scripts | Description | | Scripts | Description |
| ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | a simple program to measure the speed of Rhai's interpreter (1 million iterations) | | [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | A simple program to measure the speed of Rhai's interpreter (1 million iterations). |
| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | use Sieve of Eratosthenes to find all primes smaller than a limit | | [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | Use Sieve of Eratosthenes to find all primes smaller than a limit. |
| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | calculate the n-th Fibonacci number using a really dumb algorithm | | [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | Calculate the n-th Fibonacci number using a really dumb algorithm. |
| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | matrix multiplication test to measure the speed of multi-dimensional array access | | [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. |
Running Example Scripts Running Example Scripts

View File

@ -12,17 +12,17 @@ Excluding unneeded functionalities can result in smaller, faster builds as well
more control over what a script can (or cannot) do. more control over what a script can (or cannot) do.
| Feature | Description | | Feature | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unchecked` | Disable arithmetic checking (such as over-flows and division by zero), call stack depth limit, operations count limit and modules loading limit. Beware that a bad script may panic the entire system! | | `unchecked` | Disable arithmetic checking (such as over-flows and division by zero), call stack depth limit, operations count limit and modules loading limit.<br/>Beware that a bad script may panic the entire system! |
| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and `AST`, are all `Send + Sync`. | | `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and `AST`, are all `Send + Sync`. |
| `no_optimize` | Disable the script optimizer. | | `no_optimize` | Disable [script optimization]. |
| `no_float` | Disable floating-point numbers and math. | | `no_float` | Disable floating-point numbers and math. |
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. | | `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. | | `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
| `no_index` | Disable [arrays] and indexing features. | | `no_index` | Disable [arrays] and indexing features. |
| `no_object` | Disable support for custom types and [object maps]. | | `no_object` | Disable support for [custom types] and [object maps]. |
| `no_function` | Disable script-defined functions. | | `no_function` | Disable script-defined [functions]. |
| `no_module` | Disable loading external modules. | | `no_module` | Disable loading external [modules]. |
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
@ -32,19 +32,19 @@ Example
The `Cargo.toml` configuration below turns on these six features: The `Cargo.toml` configuration below turns on these six features:
* `sync` (everything `Send + Sync`) * `sync` (everything `Send + Sync`)
* `unchecked` (no checked arithmetic - should not be used with untrusted user scripts) * `unchecked` (disable all checking - should not be used with untrusted user scripts)
* `only_i32` (only 32-bit signed integers) * `only_i32` (only 32-bit signed integers)
* `no_float` (no floating point numbers) * `no_float` (no floating point numbers)
* `no_module` (no loading external modules) * `no_module` (no loading external [modules])
* `no_function` (no defining functions) * `no_function` (no defining [functions])
```toml ```toml
[dependencies] [dependencies]
rhai = { version = "{{version}}", features = [ "sync", "unchecked", "only_i32", "no_float", "no_module", "no_function" ] } rhai = { version = "{{version}}", features = [ "sync", "unchecked", "only_i32", "no_float", "no_module", "no_function" ] }
``` ```
The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32` or `i16`), The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32`, `i16` or `i64`),
no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining functions no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining [functions]
nor loading external modules. nor loading external [modules].
This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware. This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware.

View File

@ -2,7 +2,7 @@
//! //!
//! Rhai is a tiny, simple and very fast embedded scripting language for Rust //! Rhai is a tiny, simple and very fast embedded scripting language for Rust
//! that gives you a safe and easy way to add scripting to your applications. //! that gives you a safe and easy way to add scripting to your applications.
//! It provides a familiar syntax based on JS and Rust and a simple Rust interface. //! It provides a familiar syntax based on JavaScript and Rust and a simple Rust interface.
//! Here is a quick example. //! Here is a quick example.
//! //!
//! First, the contents of `my_script.rhai`: //! First, the contents of `my_script.rhai`:

View File

@ -677,12 +677,12 @@ impl<'a> TokenIterator<'a> {
_ => unreachable!(), _ => unreachable!(),
}); });
while let Some(next_char_in_hex) = self.peek_next() { while let Some(next_char_in_escape_seq) = self.peek_next() {
if !valid.contains(&next_char_in_hex) { if !valid.contains(&next_char_in_escape_seq) {
break; break;
} }
result.push(next_char_in_hex); result.push(next_char_in_escape_seq);
self.eat_next(); self.eat_next();
} }
} }