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) 1. [Comments](language/comments.md)
2. [Values and Types](language/values-and-types.md) 2. [Values and Types](language/values-and-types.md)
1. [Dynamic Values](language/dynamic.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) 3. [Numbers](language/numbers.md)
1. [Operators](language/num-op.md) 1. [Operators](language/num-op.md)
2. [Functions](language/num-fn.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 ```rust
let table = [..., ..., ..., ...]; let table = [..., ..., ..., ...];
// Syntax = calculate $ident$ $ident$ from $expr$ -> $ident$ : $expr$ // Syntax = calculate $ident$ ( $expr$ -> $ident$ ) => $ident$ : $expr$
let total = calculate sum price from table -> row : row.weight > 50; 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: // 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$ // Syntax = SELECT $ident$ ( $ident$ ) AS $ident$ FROM $expr$ WHERE $expr$
let total = SELECT sum(price) FROM table AS row WHERE row.weight > 50; 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 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[0] = "sum"` - math operator
* `inputs[1] = "price"` - field name * `inputs[1] = "price"` - field name
* `inputs[2] = Expression(table)` - data source * `inputs[2] = "row"` - loop variable name
* `inputs[3] = "row"` - loop variable name * `inputs[3] = Expression(table)` - data source
* `inputs[4] = Expression(row.wright > 50)` - filter predicate * `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. 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`. 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, 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. 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 ```rust
fn eval(script) { throw "eval is evil! I refuse to run " + script } 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: Or overload it from Rust:
```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()) 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; 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. `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 ```rust
// Iterate through string, yielding characters // Iterate through string, yielding characters
let s = "hello, world!"; 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. 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**). Everything exported from a module is **constant** (**read-only**).
```rust ```rust
// This is a module script. // 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 let x = 42; // this will be exported below
export x; // the variable 'x' is exported under its own name 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 Functions
--------- ---------

View File

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

View File

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

View File

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

View File

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

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. 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). 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. 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 [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. 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 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 code and metadata to allow Rhai access to its public (i.e. marked `pub`) functions, constants
is exactly what would need to be written by hand to achieve the same goal, and is custom fit and sub-modules.
to each exported item.
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 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. 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!` `#[export_module]` and `exported_module!`
---------------------------------------- ----------------------------------------
Apply `#[export_module]` onto a Rust module to convert all `pub` functions into Rhai plugin Apply `#[export_module]` onto a Rust module to register automatically construct a Rhai [module],
functions. 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 ```rust
use rhai::plugins::*; // a "prelude" import for macros use rhai::plugins::*; // a "prelude" import for macros
#[export_module] #[export_module]
mod my_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; pub const SOME_NUMBER: i64 = 42;
// This function will be registered as 'greet'. // This function will be registered as 'greet'.
@ -39,14 +45,23 @@ mod my_module {
pub fn increment(num: &mut i64) { pub fn increment(num: &mut i64) {
*num += 1; *num += 1;
} }
// This function is NOT registered. // This function is not 'pub', so NOT registered.
fn mystic_number() -> i64 { fn mystic_number() -> i64 {
42 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 ```rust
fn main() { fn main() {
@ -55,7 +70,7 @@ fn main() {
// The macro call creates the Rhai module. // The macro call creates the Rhai module.
let module = exported_module!(my_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); engine.load_package(module);
} }
``` ```
@ -77,9 +92,12 @@ increment(x);
x == 43; x == 43;
``` ```
Registering this as a custom package is almost the same, except that a module resolver must Notice that, when using a [module] as a [package], only functions registered at the _top level_
point to the module, rather than being loaded directly. See the [module] section for more can be accessed. Variables as well as sub-modules are ignored.
information.
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 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 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. 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. 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 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 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` 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 The easiest way is to use, for example, the [`StaticModuleResolver`][module resolver] to hold such
a custom module. 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 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 By far the easiest way to create a custom module is to call `rhai::plugins::combine_with_exported_module!`
`rhai::def_package!` which simply merges in all the functions defined within a [plugin 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. 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 Because of the specific requirements of a [package], all sub-modules are _flattened_
[plugins][plugin module] definition into the package itself. (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 Variables in the [plugin module] are ignored.
at the top level) and so there will not be any sub-modules added to the package.
```rust ```rust
// Import necessary types and traits. // Import necessary types and traits.
@ -81,6 +81,8 @@ use rhai::plugin::*;
// Define plugin module. // Define plugin module.
#[export_module] #[export_module]
mod my_module { mod my_module {
pub const MY_NUMBER: i64 = 42;
pub fn greet(name: &str) -> String { pub fn greet(name: &str) -> String {
format!("hello, {}!", name) 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. // 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 // The sub-module 'my_sub_module' is flattened and its functions registered at the top level.
// instead of in a sub-module.
// //
// The text string name in the middle parameter can be anything and is reserved for future use; // 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. // 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: // This call ends up registering three functions at the top level of the package:
// 1) greet // 1) greet
// 2) get_num // 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)); 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 A package is _static_ (i.e. pre-loaded into an [`Engine`]), while a module is _dynamic_ (i.e. loaded with
the `import` statement). 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. 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. 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 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. 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 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 On 64-bit targets this may not gain much, but on certain 32-bit targets this improves performance
requiring more CPU cycles to complete. due to 64-bit arithmetic requiring more CPU cycles to complete.
Minimize Size of `Dynamic` 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`] on 32-bit targets makes the critical [`Dynamic`] data type only 8 bytes long.
while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`. 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` 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). 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). 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. for the best performance with Rhai.
Disable Closures 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 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. 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. 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 Excluding unneeded functionalities can result in smaller, faster builds as well as
more control over what a script can (or cannot) do. more control over what a script can (or cannot) do.
| Feature | Description | | Feature | Additive? | 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! | | `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` | 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` | 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` | Disable [script optimization]. | | `no_optimize` | No | Disable [script optimization]. |
| `no_float` | Disable floating-point numbers and math. | | `no_float` | No | 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` | No | 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` | No | 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` | No | Disable [arrays] and indexing features. |
| `no_object` | Disable support for [custom types] and [object maps]. | | `no_object` | No | Disable support for [custom types] and [object maps]. |
| `no_function` | Disable script-defined [functions]. | | `no_function` | No | Disable script-defined [functions]. |
| `no_module` | Disable loading external [modules]. | | `no_module` | No | 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_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` | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. | | `no_std` | No | 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. | | `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` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. | | `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` | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. | | `unicode-xid-ident` | No | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. |
Example Example
@ -57,7 +57,7 @@ This configuration is perfect for an expression parser in a 32-bit embedded syst
Caveat - Features Are Not Additive 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. In fact, most features are _subtractive_ - i.e. they _remove_ functionalities.