Refine docs.

This commit is contained in:
Stephen Chung 2020-08-07 18:40:31 +08:00
parent 0b21d80641
commit c86a979601
16 changed files with 128 additions and 53 deletions

View File

@ -41,7 +41,7 @@ Standard features
Protection against attacks Protection against attacks
-------------------------- --------------------------
* Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless explicitly permitted (e.g. via a `RefCell`). * Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless [explicitly permitted](https://schungx.github.io/rhai/patterns/control.html).
* Rugged - protected against malicious attacks (such as [stack-overflow](https://schungx.github.io/rhai/safety/max-call-stack.html), [over-sized data](https://schungx.github.io/rhai/safety/max-string-size.html), and [runaway scripts](https://schungx.github.io/rhai/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts. * Rugged - protected against malicious attacks (such as [stack-overflow](https://schungx.github.io/rhai/safety/max-call-stack.html), [over-sized data](https://schungx.github.io/rhai/safety/max-string-size.html), and [runaway scripts](https://schungx.github.io/rhai/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts.
* Track script evaluation [progress](https://schungx.github.io/rhai/safety/progress.html) and manually terminate a script run. * Track script evaluation [progress](https://schungx.github.io/rhai/safety/progress.html) and manually terminate a script run.

View File

@ -101,10 +101,10 @@ The Rhai Scripting Language
9. [Maximum Statement Depth](safety/max-stmt-depth.md) 9. [Maximum Statement Depth](safety/max-stmt-depth.md)
7. [Advanced Topics](advanced.md) 7. [Advanced Topics](advanced.md)
1. [Advanced Patterns](patterns/index.md) 1. [Advanced Patterns](patterns/index.md)
1. [Loadable Configuration](patterns/config.md) 1. [Object-Oriented Programming (OOP)](patterns/oop.md)
2. [Control Layer](patterns/control.md) 2. [Loadable Configuration](patterns/config.md)
3. [Singleton Command](patterns/singleton.md) 3. [Control Layer](patterns/control.md)
4. [Object-Oriented Programming (OOP)](patterns/oop.md) 4. [Singleton Command](patterns/singleton.md)
2. [Capture Scope for Function Call](language/fn-capture.md) 2. [Capture Scope for Function Call](language/fn-capture.md)
3. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md) 3. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md)
4. [Script Optimization](engine/optimize/index.md) 4. [Script Optimization](engine/optimize/index.md)

View File

@ -46,7 +46,7 @@ Safe
* Relatively little `unsafe` code (yes there are some for performance reasons). * Relatively little `unsafe` code (yes there are some for performance reasons).
* Sand-boxed - the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless explicitly permitted (e.g. via a `RefCell`). * Sand-boxed - the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless [explicitly permitted]({{rootUrl}}/patterns/control.md).
Rugged Rugged
------ ------

View File

@ -3,9 +3,15 @@ Arrays
{{#include ../links.md}} {{#include ../links.md}}
Arrays are first-class citizens in Rhai. Like C, arrays are accessed with zero-based, non-negative integer indices. Arrays are first-class citizens in Rhai. Like C, arrays are accessed with zero-based, non-negative integer indices:
Array literals are built within square brackets '`[`' ... '`]`' and separated by commas '`,`'. > _array_ `[` _index_ `]`
Array literals are built within square brackets '`[`' ... '`]`' and separated by commas '`,`':
> `[` _value_ `,` _value_ `,` `...` `,` _value_ `]`
>
> `[` _value_ `,` _value_ `,` `...` `,` _value_ `,` `]` `// trailing comma is OK`
All elements stored in an array are [`Dynamic`], and the array can freely grow or shrink with elements added or removed. All elements stored in an array are [`Dynamic`], and the array can freely grow or shrink with elements added or removed.

View File

@ -19,21 +19,36 @@ Object Map Literals
------------------ ------------------
Object map literals are built within braces '`#{`' ... '`}`' (_name_ `:` _value_ syntax similar to Rust) Object map literals are built within braces '`#{`' ... '`}`' (_name_ `:` _value_ syntax similar to Rust)
and separated by commas '`,`'. The property _name_ can be a simple variable name following the same and separated by commas '`,`':
> `#{` _property_ `:` _value_ `,` `...` `,` _property_ `:` _value_ `}`
>
> `#{` _property_ `:` _value_ `,` `...` `,` _property_ `:` _value_ `,` `}` `// trailing comma is OK`
The property _name_ can be a simple variable name following the same
naming rules as [variables], or an arbitrary [string] literal. naming rules as [variables], or an arbitrary [string] literal.
Access Properties Access Properties
---------------- -----------------
Property values can be accessed via the _dot_ notation (_object_ `.` _property_) ### Dot Notation
or _index_ notation (_object_ `[` _property_ `]`).
The dot notation allows only property names that follow the same naming rules as [variables]. The _dot notation_ allows only property names that follow the same naming rules as [variables].
The index notation allows setting/getting properties of arbitrary names (even the empty [string]). > _object_ `.` _property_
**Important:** Trying to read a non-existent property returns [`()`] instead of causing an error. ### Index Notation
The _index notation_ allows setting/getting properties of arbitrary names (even the empty [string]).
> _object_ `[` _property_ `]`
### Non-Existence
Trying to read a non-existing property returns [`()`] instead of causing an error.
This is similar to JavaScript where accessing a non-existing property returns `undefined`.
Built-in Functions Built-in Functions
@ -89,7 +104,7 @@ let foo = #{ a:1, b:2, c:3 }["a"];
foo == 1; foo == 1;
fn abc() { fn abc() {
#{ a:1, b:2, c:3 } // a function returning an object map ##{ a:1, b:2, c:3 } // a function returning an object map
} }
let foo = abc().b; let foo = abc().b;

View File

@ -76,6 +76,8 @@
[function]: {{rootUrl}}/language/functions.md [function]: {{rootUrl}}/language/functions.md
[functions]: {{rootUrl}}/language/functions.md [functions]: {{rootUrl}}/language/functions.md
[function overloading]: {{rootUrl}}/rust/functions.md#function-overloading
[fallible functions]: {{rootUrl}}/rust/fallible.md
[function pointer]: {{rootUrl}}/language/fn-ptr.md [function pointer]: {{rootUrl}}/language/fn-ptr.md
[function pointers]: {{rootUrl}}/language/fn-ptr.md [function pointers]: {{rootUrl}}/language/fn-ptr.md
[currying]: {{rootUrl}}/language/fn-curry.md [currying]: {{rootUrl}}/language/fn-curry.md
@ -100,6 +102,7 @@
[OOP]: {{rootUrl}}/patterns/oop.md [OOP]: {{rootUrl}}/patterns/oop.md
[DSL]: {{rootUrl}}/engine/dsl.md [DSL]: {{rootUrl}}/engine/dsl.md
[sand-boxed]: {{rootUrl}}/safety/sandbox.md
[maximum statement depth]: {{rootUrl}}/safety/max-stmt-depth.md [maximum statement depth]: {{rootUrl}}/safety/max-stmt-depth.md
[maximum call stack depth]: {{rootUrl}}/safety/max-call-stack.md [maximum call stack depth]: {{rootUrl}}/safety/max-call-stack.md
[maximum number of operations]: {{rootUrl}}/safety/max-operations.md [maximum number of operations]: {{rootUrl}}/safety/max-operations.md

View File

@ -9,11 +9,11 @@ Usage Scenario
* A system where settings and configurations are complex and logic-driven. * A system where settings and configurations are complex and logic-driven.
* Where it is not possible to configure said system via standard configuration file formats such as `TOML` or `YAML`. * Where said system is too complex to configure via standard configuration file formats such as `JSON`, `TOML` or `YAML`.
* The system configuration is complex enough that it requires a full programming language. Essentially _configuration by code_. * The system is complex enough to require a full programming language to configure. Essentially _configuration by code_.
* Yet the configurations must be flexible, late-bound and dynamically loadable, just like a configuration file. * Yet the configuration must be flexible, late-bound and dynamically loadable, just like a configuration file.
Key Concepts Key Concepts
@ -23,6 +23,8 @@ Key Concepts
* Expose the configuration API. Use separate scripts to configure that API. Dynamically load scripts via the `import` statement. * Expose the configuration API. Use separate scripts to configure that API. Dynamically load scripts via the `import` statement.
* Leverage [function overloading] to simplify the API design.
* Since Rhai is _sand-boxed_, it cannot mutate the environment. To modify the external configuration object via an API, it must be wrapped in a `RefCell` (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`]. * Since Rhai is _sand-boxed_, it cannot mutate the environment. To modify the external configuration object via an API, it must be wrapped in a `RefCell` (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`].

View File

@ -19,12 +19,26 @@ Key Concepts
* Expose a Control API. * Expose a Control API.
* Since Rhai is _sand-boxed_, it cannot mutate the environment. To perform external actions via an API, the actual system must be wrapped in a `RefCell` (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`]. * Leverage [function overloading] to simplify the API design.
* Since Rhai is _[sand-boxed]_, it cannot mutate the environment. To perform external actions via an API, the actual system must be wrapped in a `RefCell` (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`].
Implementation Implementation
-------------- --------------
There are two broad ways for Rhai to control an external system, both of which involve
wrapping the system in a shared, interior-mutated object.
This is one way which does not involve exposing the data structures of the external system,
but only through exposing an abstract API primarily made up of functions.
Use this when the API is relatively simple and clean, and the number of functions is small enough.
For a complex API involving lots of functions, or an API that is object-based,
use the [Singleton Command Object]({{rootUrl}}/patterns/singleton.md) pattern instead.
### Functional API ### Functional API
Assume that a system provides the following functional API: Assume that a system provides the following functional API:

View File

@ -4,6 +4,4 @@ Advanced Patterns
{{#include ../links.md}} {{#include ../links.md}}
Use Rhai in different scenarios other than simply evaluating a user script. Leverage the full power and flexibility of Rhai in advanced scenarios.
These patterns are useful when Rhai needs to affect/control the external environment.

View File

@ -18,17 +18,17 @@ Rhai's [object maps] has [special support for OOP]({{rootUrl}}/language/object-m
| [Object map] properties that hold [function pointers] | methods | | [Object map] properties that hold [function pointers] | methods |
When a property of an [object map] is called like a method function, and if it happens to hold When a property of an [object map] is called like a method function, and if it happens to hold
a valid [function pointer] (perhaps defined via an [anonymous function]), then the call will be a valid [function pointer] (perhaps defined via an [anonymous function] or more commonly as a [closure]),
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 Anonymous Functions to Define Methods
---------------------------------------- ----------------------------------------
[Anonymous functions] defined as values for [object map] properties take on a syntactic shape [Anonymous functions] or [closures] defined as values for [object map] properties take on
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.
Anonymous functions can also _capture_ variables from the defining environment, which is a very Closures also _[capture][automatic currying]_ variables from the defining environment, which is a very
common OOP pattern. Capturing is accomplished via a feature called _[automatic currying]_ and common OOP pattern. Capturing is accomplished via a feature called _[automatic currying]_ and
can be turned off via the [`no_closure`] feature. can be turned off via the [`no_closure`] feature.
@ -40,9 +40,8 @@ Examples
let factor = 1; let factor = 1;
// Define the object // Define the object
let obj = let obj = #{
#{ data: 0, // object field
data: 0,
increment: |x| this.data += x, // 'this' binds to 'obj' increment: |x| this.data += x, // 'this' binds to 'obj'
update: |x| this.data = x * factor, // 'this' binds to 'obj', 'factor' is captured update: |x| this.data = x * factor, // 'this' binds to 'obj', 'factor' is captured
action: || print(this.data) // 'this' binds to 'obj' action: || print(this.data) // 'this' binds to 'obj'

View File

@ -1,5 +1,5 @@
Singleton Command Objects Singleton Command Object
======================== =======================
{{#include ../links.md}} {{#include ../links.md}}
@ -21,9 +21,11 @@ Usage Scenario
Key Concepts Key Concepts
------------ ------------
* Expose a Command type with an API. * Expose a Command type with an API. The [`no_object`] feature must not be on.
* Since Rhai is _sand-boxed_, it cannot mutate the environment. To perform external actions via an API, the command object type must be wrapped in a `RefCell` (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`]. * Leverage [function overloading] to simplify the API design.
* Since Rhai is _[sand-boxed]_, it cannot mutate the environment. To perform external actions via an API, the command object type must be wrapped in a `RefCell` (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`].
* Load each command object into a custom [`Scope`] as constant variables. * Load each command object into a custom [`Scope`] as constant variables.
@ -33,6 +35,18 @@ Key Concepts
Implementation Implementation
-------------- --------------
There are two broad ways for Rhai to control an external system, both of which involve
wrapping the system in a shared, interior-mutated object.
This is the other way which involves directly exposing the data structures of the external system
as a name singleton object in the scripting space.
Use this when the API is complex and clearly object-based.
For a relatively simple API that is action-based and not object-based,
use the [Control Layer]({{rootUrl}}/patterns/control.md) pattern instead.
### Functional API ### Functional API
Assume the following command object type: Assume the following command object type:

View File

@ -7,7 +7,7 @@ Rhai's scripting engine is very lightweight. It gets most of its abilities from
To call these functions, they need to be _registered_ with the [`Engine`] using `Engine::register_fn` To call these functions, they need to be _registered_ with the [`Engine`] using `Engine::register_fn`
(in the `RegisterFn` trait) and `Engine::register_result_fn` (in the `RegisterResultFn` trait, (in the `RegisterFn` trait) and `Engine::register_result_fn` (in the `RegisterResultFn` trait,
see [fallible functions]({{rootUrl}}/rust/fallible.md)). see [fallible functions]).
```rust ```rust
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString}; use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString};
@ -62,8 +62,12 @@ let x = (42_i64).into(); // 'into()' works for standard t
let y = Dynamic::from("hello!".to_string()); // remember &str is not supported by Rhai let y = Dynamic::from("hello!".to_string()); // remember &str is not supported by Rhai
``` ```
Function Overloading
--------------------
Functions registered with the [`Engine`] can be _overloaded_ as long as the _signature_ is unique, Functions registered with the [`Engine`] can be _overloaded_ as long as the _signature_ is unique,
i.e. different functions can have the same name as long as their parameters are of different types i.e. different functions can have the same name as long as their parameters are of different types
and/or different number. or different number.
New definitions _overwrite_ previous definitions of the same name and same number/types of parameters. New definitions _overwrite_ previous definitions of the same name and same number/types of parameters.

View File

@ -5,7 +5,9 @@ Custom Type Indexers
A custom type can also expose an _indexer_ by registering an indexer function. A custom type can also expose an _indexer_ by registering an indexer function.
A custom type with an indexer function defined can use the bracket '`[]`' notation to get a property value. A custom type with an indexer function defined can use the bracket notation to get a property value:
> _object_ `[` _index_ `]`
Like getters and setters, indexers take a `&mut` reference to the first parameter. Like getters and setters, indexers take a `&mut` reference to the first parameter.

View File

@ -91,7 +91,7 @@ struct MyStruct {
let engine = Engine::new(); let engine = Engine::new();
let result: Dynamic = engine.eval(r#" let result: Dynamic = engine.eval(r#"
#{ ##{
a: 42, a: 42,
b: [ "hello", "world" ], b: [ "hello", "world" ],
c: true, c: true,

View File

@ -5,13 +5,31 @@ Sand-Boxing - Block Access to External Data
Rhai is _sand-boxed_ so a script can never read from outside its own environment. Rhai is _sand-boxed_ so a script can never read from outside its own environment.
Furthermore, an [`Engine`] created non-`mut` cannot mutate any state outside of itself; Furthermore, an [`Engine`] created non-`mut` cannot mutate any state, including itself
so it is highly recommended that [`Engine`]'s are created immutable as much as possible. (and therefore it is also _re-entrant_).
It is highly recommended that [`Engine`]'s be created immutable as much as possible.
```rust ```rust
let mut engine = Engine::new(); // create mutable 'Engine' // Use the fluent API to configure an 'Engine' and then keep an immutable instance.
let engine = Engine::new()
.register_get("field", get_field)
.register_set("field", set_field)
.register_fn("do_work", action);
engine.register_get("add", add); // configure 'engine' // 'engine' is immutable...
let engine = engine; // shadow the variable so that 'engine' is now immutable
``` ```
Using Rhai to Control External Environment
-----------------------------------------
How does a _sand-boxed_, immutable [`Engine`] control the external environment?
This is necessary in order to use Rhai as a _dynamic control layer_ over a Rust core system.
There are two general patterns, both involving wrapping the external system
in a shared, interior-mutated object (e.g. `Rc<RefCell<T>>`):
* [Control Layer]({{rootUrl}}/patterns/control.md) pattern.
* [Singleton Command Object]({{rootUrl}}/patterns/singleton.md) pattern.

View File

@ -56,7 +56,7 @@ mod example {
let result: Dynamic = engine let result: Dynamic = engine
.eval( .eval(
r#" r#"
#{ ##{
a: 42, a: 42,
b: [ "hello", "world" ], b: [ "hello", "world" ],
c: true, c: true,