diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index db96844b..23f39757 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -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) diff --git a/doc/src/engine/dsl.md b/doc/src/engine/dsl.md index 8f326ed1..593a7eb0 100644 --- a/doc/src/engine/dsl.md +++ b/doc/src/engine/dsl.md @@ -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. diff --git a/doc/src/language/dynamic.md b/doc/src/language/dynamic.md index 9a3c27b7..7909d162 100644 --- a/doc/src/language/dynamic.md +++ b/doc/src/language/dynamic.md @@ -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. diff --git a/doc/src/language/eval.md b/doc/src/language/eval.md index 0af40142..fec9569a 100644 --- a/doc/src/language/eval.md +++ b/doc/src/language/eval.md @@ -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> { +engine.register_result_fn("eval", |script: String| -> Result<(), Box> { Err(format!("eval is evil! I refuse to run {}", script).into()) -} - -engine.register_result_fn("eval", alt_eval); +}); ``` diff --git a/doc/src/language/for.md b/doc/src/language/for.md index 15ea22e2..493d8759 100644 --- a/doc/src/language/for.md +++ b/doc/src/language/for.md @@ -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!"; diff --git a/doc/src/language/modules/export.md b/doc/src/language/modules/export.md index 2152dc74..546b5952 100644 --- a/doc/src/language/modules/export.md +++ b/doc/src/language/modules/export.md @@ -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 --------- diff --git a/doc/src/language/num-op.md b/doc/src/language/num-op.md index fa96f1e9..f1db4d46 100644 --- a/doc/src/language/num-op.md +++ b/doc/src/language/num-op.md @@ -10,7 +10,7 @@ Unary Operators | Operator | Description | | -------- | ----------- | -| `+` | Plus | +| `+` | Positive | | `-` | Negative | ```rust diff --git a/doc/src/language/timestamps.md b/doc/src/language/timestamps.md index 47ba302b..8fef7cdd 100644 --- a/doc/src/language/timestamps.md +++ b/doc/src/language/timestamps.md @@ -1,5 +1,5 @@ -`timestamp`'s -============= +`timestamp` +=========== {{#include ../links.md}} diff --git a/doc/src/language/type-of.md b/doc/src/language/type-of.md index a2869539..02ddbc28 100644 --- a/doc/src/language/type-of.md +++ b/doc/src/language/type-of.md @@ -1,5 +1,5 @@ -`type_of` -========= +`type_of()` +=========== {{#include ../links.md}} diff --git a/doc/src/patterns/config.md b/doc/src/patterns/config.md index e2d0db54..c87a0fb3 100644 --- a/doc/src/patterns/config.md +++ b/doc/src/patterns/config.md @@ -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; diff --git a/doc/src/patterns/control.md b/doc/src/patterns/control.md index af6021e6..52b6b23c 100644 --- a/doc/src/patterns/control.md +++ b/doc/src/patterns/control.md @@ -68,19 +68,19 @@ let bunny: Rc> = 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()) ); ``` diff --git a/doc/src/patterns/oop.md b/doc/src/patterns/oop.md index 823df31e..b0e454c6 100644 --- a/doc/src/patterns/oop.md +++ b/doc/src/patterns/oop.md @@ -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. diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 24107fff..65bb0014 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -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. diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 26441067..d2f68e54 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -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. diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 53ac17a0..01d11b12 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -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)); }); diff --git a/doc/src/rust/packages/index.md b/doc/src/rust/packages/index.md index a5df94cd..f2e6b598 100644 --- a/doc/src/rust/packages/index.md +++ b/doc/src/rust/packages/index.md @@ -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. diff --git a/doc/src/start/builds/performance.md b/doc/src/start/builds/performance.md index a0c65fe5..78ca762e 100644 --- a/doc/src/start/builds/performance.md +++ b/doc/src/start/builds/performance.md @@ -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` or `Arc` 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. diff --git a/doc/src/start/features.md b/doc/src/start/features.md index 3e8067fa..9cd86f52 100644 --- a/doc/src/start/features.md +++ b/doc/src/start/features.md @@ -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.
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.
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.