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
--------------------------
* 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.
* 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)
7. [Advanced Topics](advanced.md)
1. [Advanced Patterns](patterns/index.md)
1. [Loadable Configuration](patterns/config.md)
2. [Control Layer](patterns/control.md)
3. [Singleton Command](patterns/singleton.md)
4. [Object-Oriented Programming (OOP)](patterns/oop.md)
1. [Object-Oriented Programming (OOP)](patterns/oop.md)
2. [Loadable Configuration](patterns/config.md)
3. [Control Layer](patterns/control.md)
4. [Singleton Command](patterns/singleton.md)
2. [Capture Scope for Function Call](language/fn-capture.md)
3. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.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).
* 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
------

View File

@ -3,9 +3,15 @@ Arrays
{{#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.

View File

@ -19,21 +19,36 @@ Object Map Literals
------------------
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.
Access Properties
----------------
-----------------
Property values can be accessed via the _dot_ notation (_object_ `.` _property_)
or _index_ notation (_object_ `[` _property_ `]`).
### Dot Notation
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
@ -89,7 +104,7 @@ let foo = #{ a:1, b:2, c:3 }["a"];
foo == 1;
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;

View File

@ -76,6 +76,8 @@
[function]: {{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 pointers]: {{rootUrl}}/language/fn-ptr.md
[currying]: {{rootUrl}}/language/fn-curry.md
@ -100,6 +102,7 @@
[OOP]: {{rootUrl}}/patterns/oop.md
[DSL]: {{rootUrl}}/engine/dsl.md
[sand-boxed]: {{rootUrl}}/safety/sandbox.md
[maximum statement depth]: {{rootUrl}}/safety/max-stmt-depth.md
[maximum call stack depth]: {{rootUrl}}/safety/max-call-stack.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.
* 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
@ -23,6 +23,8 @@ Key Concepts
* 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`].

View File

@ -19,12 +19,26 @@ Key Concepts
* 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
--------------
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
Assume that a system provides the following functional API:

View File

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

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 |
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
dispatched to the actual function with `this` binding to the [object map] itself.
a valid [function pointer] (perhaps defined via an [anonymous function] or more commonly as a [closure]),
then the call will be dispatched to the actual function with `this` binding to the [object map] itself.
Use Anonymous Functions to Define Methods
----------------------------------------
[Anonymous functions] defined as values for [object map] properties take on a syntactic shape
that resembles very closely that of class methods in an OOP language.
[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.
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
can be turned off via the [`no_closure`] feature.
@ -40,23 +40,22 @@ Examples
let factor = 1;
// Define the object
let obj =
#{
data: 0,
increment: |x| this.data += x, // 'this' binds to 'obj'
update: |x| this.data = x * factor, // 'this' binds to 'obj', 'factor' is captured
action: || print(this.data) // 'this' binds to 'obj'
};
let obj = #{
data: 0, // object field
increment: |x| this.data += x, // 'this' binds to 'obj'
update: |x| this.data = x * factor, // 'this' binds to 'obj', 'factor' is captured
action: || print(this.data) // 'this' binds to 'obj'
};
// Use the object
obj.increment(1);
obj.action(); // prints 1
obj.action(); // prints 1
obj.update(42);
obj.action(); // prints 42
obj.action(); // prints 42
factor = 2;
obj.update(42);
obj.action(); // prints 84
obj.action(); // prints 84
```

View File

@ -1,5 +1,5 @@
Singleton Command Objects
========================
Singleton Command Object
=======================
{{#include ../links.md}}
@ -21,9 +21,11 @@ Usage Scenario
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.
@ -33,6 +35,18 @@ Key Concepts
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
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`
(in the `RegisterFn` trait) and `Engine::register_result_fn` (in the `RegisterResultFn` trait,
see [fallible functions]({{rootUrl}}/rust/fallible.md)).
see [fallible functions]).
```rust
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
```
Function Overloading
--------------------
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
and/or different number.
or different number.
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 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.

View File

@ -91,7 +91,7 @@ struct MyStruct {
let engine = Engine::new();
let result: Dynamic = engine.eval(r#"
#{
##{
a: 42,
b: [ "hello", "world" ],
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.
Furthermore, an [`Engine`] created non-`mut` cannot mutate any state outside of itself;
so it is highly recommended that [`Engine`]'s are created immutable as much as possible.
Furthermore, an [`Engine`] created non-`mut` cannot mutate any state, including itself
(and therefore it is also _re-entrant_).
It is highly recommended that [`Engine`]'s be created immutable as much as possible.
```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'
let engine = engine; // shadow the variable so that 'engine' is now immutable
// 'engine' is 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
.eval(
r#"
#{
##{
a: 42,
b: [ "hello", "world" ],
c: true,