Refine docs.
This commit is contained in:
parent
0b21d80641
commit
c86a979601
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
------
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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`].
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
```
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -91,7 +91,7 @@ struct MyStruct {
|
||||
let engine = Engine::new();
|
||||
|
||||
let result: Dynamic = engine.eval(r#"
|
||||
#{
|
||||
##{
|
||||
a: 42,
|
||||
b: [ "hello", "world" ],
|
||||
c: true,
|
||||
|
@ -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.
|
||||
|
@ -56,7 +56,7 @@ mod example {
|
||||
let result: Dynamic = engine
|
||||
.eval(
|
||||
r#"
|
||||
#{
|
||||
##{
|
||||
a: 42,
|
||||
b: [ "hello", "world" ],
|
||||
c: true,
|
||||
|
Loading…
Reference in New Issue
Block a user