Refine docs.

This commit is contained in:
Stephen Chung 2020-09-19 12:14:02 +08:00
parent b795ce9f45
commit 2ff3a1fde5
18 changed files with 133 additions and 107 deletions

View File

@ -52,7 +52,7 @@ The Rhai Scripting Language
1. [Comments](language/comments.md)
2. [Values and Types](language/values-and-types.md)
1. [Dynamic Values](language/dynamic.md)
2. [type-of()](language/type-of.md)
2. [type_of()](language/type-of.md)
3. [Numbers](language/numbers.md)
1. [Operators](language/num-op.md)
2. [Functions](language/num-fn.md)

View File

@ -69,12 +69,12 @@ For example, the following is a SQL-like syntax for some obscure DSL operation:
```rust
let table = [..., ..., ..., ...];
// Syntax = calculate $ident$ $ident$ from $expr$ -> $ident$ : $expr$
let total = calculate sum price from table -> row : row.weight > 50;
// Syntax = calculate $ident$ ( $expr$ -> $ident$ ) => $ident$ : $expr$
let total = calculate sum(table->price) => row : row.weight > 50;
// Note: There is nothing special about those symbols; to make it look exactly like SQL:
// Syntax = SELECT $ident$ ( $ident$ ) FROM $expr$ AS $ident$ WHERE $expr$
let total = SELECT sum(price) FROM table AS row WHERE row.weight > 50;
// Syntax = SELECT $ident$ ( $ident$ ) AS $ident$ FROM $expr$ WHERE $expr$
let total = SELECT sum(price) AS row FROM table WHERE row.weight > 50;
```
After registering this custom syntax with Rhai, it can be used anywhere inside a script as
@ -84,9 +84,9 @@ For its evaluation, the callback function will receive the following list of inp
* `inputs[0] = "sum"` - math operator
* `inputs[1] = "price"` - field name
* `inputs[2] = Expression(table)` - data source
* `inputs[3] = "row"` - loop variable name
* `inputs[2] = "row"` - loop variable name
* `inputs[3] = Expression(table)` - data source
* `inputs[4] = Expression(row.wright > 50)` - filter predicate
Other identifiers, such as `"calculate"`, `"from"`, as well as symbols such as `->` and `:`,
Other identifiers, such as `"calculate"`, `"FROM"`, as well as symbols such as `->` and `:` etc.,
are parsed in the order defined within the custom syntax.

View File

@ -6,8 +6,8 @@ Dynamic Values
A `Dynamic` value can be _any_ type. However, under [`sync`], all types must be `Send + Sync`.
Use [`type_of()`] to Get Value Type
----------------------------------
Use `type_of()` to Get Value Type
--------------------------------
Because [`type_of()`] a `Dynamic` value returns the type of the actual value,
it is usually used to perform type-specific actions based on the actual value's type.

View File

@ -65,17 +65,15 @@ disable `eval` by overloading it, probably with something that throws.
```rust
fn eval(script) { throw "eval is evil! I refuse to run " + script }
let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2"
let x = eval("40 + 2"); // throws "eval is evil! I refuse to run 40 + 2"
```
Or overload it from Rust:
```rust
fn alt_eval(script: String) -> Result<(), Box<EvalAltResult>> {
engine.register_result_fn("eval", |script: String| -> Result<(), Box<EvalAltResult>> {
Err(format!("eval is evil! I refuse to run {}", script).into())
}
engine.register_result_fn("eval", alt_eval);
});
```

View File

@ -9,6 +9,9 @@ is provided by the `for` ... `in` loop.
Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;
`break` can be used to break out of the loop unconditionally.
To loop through a number sequence (with or without steps), use the `range` function to
return a numeric iterator.
```rust
// Iterate through string, yielding characters
let s = "hello, world!";

View File

@ -20,14 +20,15 @@ Global Variables
The `export` statement, which can only be at global level, exposes selected variables as members of a module.
Variables not exported are _private_ and hidden to the outside.
Variables not exported are _private_ and hidden. They are merely used to initialize the module,
but cannot be accessed from outside.
Everything exported from a module is **constant** (**read-only**).
```rust
// This is a module script.
let private = 123; // variable not exported - default hidden
let hidden = 123; // variable not exported - default hidden
let x = 42; // this will be exported below
export x; // the variable 'x' is exported under its own name
@ -43,9 +44,6 @@ export x as answer; // the variable 'x' is exported under the alias 'answer'
}
```
[`private`] variables are used to initialize the module.
They cannot be used apart from this.
Functions
---------

View File

@ -10,7 +10,7 @@ Unary Operators
| Operator | Description |
| -------- | ----------- |
| `+` | Plus |
| `+` | Positive |
| `-` | Negative |
```rust

View File

@ -1,5 +1,5 @@
`timestamp`'s
=============
`timestamp`
===========
{{#include ../links.md}}

View File

@ -1,5 +1,5 @@
`type_of`
=========
`type_of()`
===========
{{#include ../links.md}}

View File

@ -137,7 +137,7 @@ For example, the above configuration example may be expressed by this custom syn
id "hello";
// Add to list
list +"foo"
list + "foo";
// Add to map
map "bar" => true;

View File

@ -68,19 +68,19 @@ let bunny: Rc<RefCell<EnergizerBunny>> = Rc::new(RefCell::(EnergizerBunny::new()
// Notice 'move' is used to move the shared API object into the closure.
let b = bunny.clone();
engine.register_fn("bunny_power", move |on: bool| {
if on {
if b.borrow().is_going() {
println!("Still going...");
} else {
b.borrow_mut().go();
}
if on {
if b.borrow().is_going() {
println!("Still going...");
} else {
if b.borrow().is_going() {
b.borrow_mut().stop();
} else {
println!("Already out of battery!");
}
b.borrow_mut().go();
}
} else {
if b.borrow().is_going() {
b.borrow_mut().stop();
} else {
println!("Already out of battery!");
}
}
});
let b = bunny.clone();
@ -88,24 +88,24 @@ engine.register_fn("bunny_is_going", move || b.borrow().is_going());
let b = bunny.clone();
engine.register_fn("bunny_get_speed", move ||
if b.borrow().is_going() { b.borrow().get_speed() } else { 0 }
if b.borrow().is_going() { b.borrow().get_speed() } else { 0 }
);
let b = bunny.clone();
engine.register_result_fn("bunny_set_speed", move |speed: i64|
if speed <= 0 {
return Err("Speed must be positive!".into());
} else if speed > 100 {
return Err("Bunny will be going too fast!".into());
}
if speed <= 0 {
return Err("Speed must be positive!".into());
} else if speed > 100 {
return Err("Bunny will be going too fast!".into());
}
if b.borrow().is_going() {
b.borrow_mut().set_speed(speed)
} else {
return Err("Bunny is not yet going!".into());
}
if b.borrow().is_going() {
b.borrow_mut().set_speed(speed)
} else {
return Err("Bunny is not yet going!".into());
}
Ok(().into())
Ok(().into())
);
```

View File

@ -6,8 +6,8 @@ Object-Oriented Programming (OOP)
Rhai does not have _objects_ per se, but it is possible to _simulate_ object-oriented programming.
Use [Object Maps] to Simulate OOP
--------------------------------
Use Object Maps to Simulate OOP
------------------------------
Rhai's [object maps] has [special support for OOP]({{rootUrl}}/language/object-maps-oop.md).
@ -22,8 +22,8 @@ a valid [function pointer] (perhaps defined via an [anonymous function] or more
then the call will be dispatched to the actual function with `this` binding to the [object map] itself.
Use Anonymous Functions to Define Methods
----------------------------------------
Use Closures to Define Methods
-----------------------------
[Anonymous functions] or [closures] defined as values for [object map] properties take on
a syntactic shape that resembles very closely that of class methods in an OOP language.

View File

@ -5,9 +5,11 @@ Export a Rust Module to Rhai
When applied to a Rust module, the `#[export_module]` attribute generates the necessary
code and metadata to allow Rhai access to its public (i.e. marked `pub`) functions. This code
is exactly what would need to be written by hand to achieve the same goal, and is custom fit
to each exported item.
code and metadata to allow Rhai access to its public (i.e. marked `pub`) functions, constants
and sub-modules.
This code is exactly what would need to be written by hand to achieve the same goal,
and is custom fit to each exported item.
This Rust module can then either be loaded into an [`Engine`] as a normal [module] or
registered as a [custom package]. This is done by using the `exported_module!` macro.
@ -16,15 +18,19 @@ registered as a [custom package]. This is done by using the `exported_module!` m
`#[export_module]` and `exported_module!`
----------------------------------------
Apply `#[export_module]` onto a Rust module to convert all `pub` functions into Rhai plugin
functions.
Apply `#[export_module]` onto a Rust module to register automatically construct a Rhai [module],
which can then be loaded into an [`Engine`].
All `pub` functions become registered functions, all `pub` constants become [module] constant variables,
and all sub-modules become Rhai sub-modules.
```rust
use rhai::plugins::*; // a "prelude" import for macros
#[export_module]
mod my_module {
// This constant will be registered as a the constant variable 'SOME_NUMBER'.
// This constant will be registered as the constant variable 'SOME_NUMBER'.
// Ignored when loaded as a package.
pub const SOME_NUMBER: i64 = 42;
// This function will be registered as 'greet'.
@ -39,14 +45,23 @@ mod my_module {
pub fn increment(num: &mut i64) {
*num += 1;
}
// This function is NOT registered.
// This function is not 'pub', so NOT registered.
fn mystic_number() -> i64 {
42
}
// This sub-module is ignored when loaded as a package.
pub mod my_sub_module {
// This function is ignored when loaded as a package.
// Otherwise it is a valid registered function under a sub-module.
pub fn get_info() -> String {
"hello".to_string()
}
}
}
```
In order to load this into an [`Engine`], use the `load_package` method on the exported module:
The simplest way to load this into an [`Engine`] is to use the `load_package` method on the exported module:
```rust
fn main() {
@ -55,7 +70,7 @@ fn main() {
// The macro call creates the Rhai module.
let module = exported_module!(my_module);
// A module can simply be loaded, registering all public its contents.
// A module can simply be loaded, registering all public functions.
engine.load_package(module);
}
```
@ -77,9 +92,12 @@ increment(x);
x == 43;
```
Registering this as a custom package is almost the same, except that a module resolver must
point to the module, rather than being loaded directly. See the [module] section for more
information.
Notice that, when using a [module] as a [package], only functions registered at the _top level_
can be accessed. Variables as well as sub-modules are ignored.
Using this directly as a Rhai module is almost the same, except that a [module resolver] must
be used to serve the module, and the module is loaded via `import` statements.
See the [module] section for more information.
Function Overloading and Operators
@ -91,7 +109,8 @@ attribute to individual functions.
The text string given as the `name` parameter to `#[rhai_fn]` is used to register the function with
the [`Engine`], disregarding the actual name of the function.
With `#[rhai_fn(name = "...")]`, multiple functions may be registered under the same name in Rhai, so long as they have different parameters.
With `#[rhai_fn(name = "...")]`, multiple functions may be registered under the same name in Rhai,
so long as they have different parameters.
Operators (which require function names that are not valid for Rust) can also be registered this way.

View File

@ -7,7 +7,8 @@ Create a Module from Rust
Create via Plugin
-----------------
By far the simplest way to create a [module] is via a [plugin module].
By far the simplest way to create a [module] is via a [plugin module]
which converts a normal Rust module into a Rhai [module] via procedural macros.
Create via `Module` API
@ -21,7 +22,8 @@ For the complete `Module` API, refer to the [documentation](https://docs.rs/rhai
Make the `Module` Available to the `Engine`
------------------------------------------
In order to _use_ a custom module, there must be a [module resolver].
In order to _use_ a custom module, there must be a [module resolver], which serves the module when
loaded via `import` statements.
The easiest way is to use, for example, the [`StaticModuleResolver`][module resolver] to hold such
a custom module.

View File

@ -58,16 +58,16 @@ def_package!(rhai:MyPackage:"My own personal super package", module, {
Create a Custom Package from a Plugin Module
-------------------------------------------
By far the easiest way to create a custom module is to call `Module::merge_flatten` from within
`rhai::def_package!` which simply merges in all the functions defined within a [plugin module].
By far the easiest way to create a custom module is to call `rhai::plugins::combine_with_exported_module!`
from within `rhai::def_package!` which simply merges in all the functions defined within a [plugin module].
In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage`, are implemented.
`rhai::plugins::combine_with_exported_module!` adds all functions and constants from the
[plugins][plugin module] definition into the package itself.
Because of the specific requirements of a [package], all sub-modules are _flattened_
(i.e. all functions defined within sub-modules are pulled up and registered at the top level instead)
and so there will not be any sub-modules added to the package.
All sub-modules are _flattened_ (i.e. all functions and constants defined within sub-modules are registered
at the top level) and so there will not be any sub-modules added to the package.
Variables in the [plugin module] are ignored.
```rust
// Import necessary types and traits.
@ -81,6 +81,8 @@ use rhai::plugin::*;
// Define plugin module.
#[export_module]
mod my_module {
pub const MY_NUMBER: i64 = 42;
pub fn greet(name: &str) -> String {
format!("hello, {}!", name)
}
@ -107,16 +109,17 @@ def_package!(rhai:MyPackage:"My own personal super package", module, {
// Merge all registered functions and constants from the plugin module into the custom package.
//
// Functions in the sub-module 'my_sub_module' are flattened and registered at the top level
// instead of in a sub-module.
// The sub-module 'my_sub_module' is flattened and its functions registered at the top level.
//
// The text string name in the middle parameter can be anything and is reserved for future use;
// it is recommended to be an ID string that uniquely identifies the module.
//
// The constant variable, 'MY_NUMBER', is ignored.
//
// This call ends up registering three functions at the top level of the package:
// 1) greet
// 2) get_num
// 3) get_sub_num (flattened from sub-module 'my_sub_module')
// 3) get_sub_num (pulled up from 'my_sub_module')
//
combine_with_exported_module!(module, "my-functions", my_module));
});

View File

@ -37,7 +37,7 @@ namespace alias specified in an [`import`] statement (see also [modules]).
A package is _static_ (i.e. pre-loaded into an [`Engine`]), while a module is _dynamic_ (i.e. loaded with
the `import` statement).
Functions in a package are _flattened_, meaning that functions from sub-modules must be pulled up to the root level.
Sub-modules in a package are _flattened_, meaning that functions from them must be pulled up to the root level.
In other words, there can be no sub-modules in a package, and everything should reside in one, flat namespace.
Only functions matter for a package. Constant variables registered in a package are ignored.

View File

@ -13,23 +13,26 @@ If only a single integer type is needed in scripts - most of the time this is th
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.
The [`only_i32`] and [`only_i64`] features disable all integer types except `i32` or `i64` respectively.
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 - turn on [`only_i32`].
Under this feature, only `i32` is supported as a built-in integer type and no others.
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.
On 64-bit targets this may not gain much, but on certain 32-bit targets this improves performance
due to 64-bit arithmetic requiring more CPU cycles to complete.
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
while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`.
Turning on [`no_float`] and [`only_i32`] on 32-bit targets makes the critical [`Dynamic`] data type only 8 bytes long.
Normally [`Dynamic`] 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.
A small [`Dynamic`] helps performance due to better cache efficiency.
Use `ImmutableString`
@ -41,17 +44,17 @@ cloning when passing function arguments.
Rhai's internal string type is `ImmutableString` (basically `Rc<String>` or `Arc<String>` depending on the [`sync`] feature).
It is cheap to clone, but expensive to modify (a new copy of the string must be made in order to change it).
Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`)
Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (both map to `ImmutableString`)
for the best performance with Rhai.
Disable Closures
----------------
Support for [closures], including capturing shared values, adds material overhead to script evaluation.
Support for [closures] that capture shared variables adds material overhead to script evaluation.
This is because every data access must be checked whether it is a shared value, and if so, take a read
or write lock before reading it.
This is because every data access must be checked whether it is a shared value and, if so, take a read
lock before reading it.
Use [`no_closure`] to disable closure and capturing support and optimize the hot path
Use [`no_closure`] to disable closure and capturing support to optimize the hot path
because there is no need to take locks for shared data.

View File

@ -11,23 +11,23 @@ Notice that this deviates from Rust norm where features are _additive_.
Excluding unneeded functionalities can result in smaller, faster builds as well as
more control over what a script can (or cannot) do.
| 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.<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`. |
| `no_optimize` | Disable [script optimization]. |
| `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_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_object` | Disable support for [custom types] and [object maps]. |
| `no_function` | Disable script-defined [functions]. |
| `no_module` | Disable loading external [modules]. |
| `no_closure` | Disable [capturing][automatic currying] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
| `no_std` | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. |
| `serde` | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
| `unicode-xid-ident` | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. |
| Feature | Additive? | Description |
| ------------------- | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unchecked` | No | 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` | No | 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` | No | Disable [script optimization]. |
| `no_float` | No | Disable floating-point numbers and math. |
| `only_i32` | No | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
| `only_i64` | No | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
| `no_index` | No | Disable [arrays] and indexing features. |
| `no_object` | No | Disable support for [custom types] and [object maps]. |
| `no_function` | No | Disable script-defined [functions]. |
| `no_module` | No | Disable loading external [modules]. |
| `no_closure` | No | Disable [capturing][automatic currying] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
| `no_std` | No | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. |
| `serde` | Yes | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
| `internals` | Yes | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
| `unicode-xid-ident` | No | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. |
Example
@ -57,7 +57,7 @@ This configuration is perfect for an expression parser in a 32-bit embedded syst
Caveat - Features Are Not Additive
---------------------------------
Rhai features are not strictly _additive_ - i.e. they do not only add optional functionalities.
Most Rhai features are not strictly _additive_ - i.e. they do not only add optional functionalities.
In fact, most features are _subtractive_ - i.e. they _remove_ functionalities.