Refactor.

This commit is contained in:
Stephen Chung
2020-06-29 23:55:28 +08:00
parent 063851a6ad
commit d6a08be223
37 changed files with 386 additions and 172 deletions

View File

@@ -1,30 +1,31 @@
The Rhai Scripting Language
==========================
1. [What is Rhai](about.md)
1. [What is Rhai](about/index.md)
1. [Features](about/features.md)
2. [Supported Targets and Builds](about/targets.md)
3. [What Rhai Isn't](about/non-design.md)
4. [Related Resources](about/related.md)
2. [Getting Started](start.md)
1. [Install the Rhai Crate](start/install.md)
2. [Optional Features](start/features.md)
3. [Special Builds](start/builds/index.md)
3. [Getting Started](start/index.md)
1. [Online Playground](start/playground.md)
2. [Install the Rhai Crate](start/install.md)
3. [Optional Features](start/features.md)
4. [Special Builds](start/builds/index.md)
1. [Performance](start/builds/performance.md)
2. [Minimal](start/builds/minimal.md)
3. [no-std](start/builds/no-std.md)
4. [WebAssembly (WASM)](start/builds/wasm.md)
4. [Examples](start/examples/index.md)
5. [Examples](start/examples/index.md)
1. [Rust](start/examples/rust.md)
2. [Scripts](start/examples/scripts.md)
3. [Using the `Engine`](engine.md)
4. [Using the `Engine`](engine/index.md)
1. [Hello World in Rhai - Evaluate a Script](engine/hello-world.md)
2. [Compile a Script to AST for Repeated Evaluations](engine/compile.md)
3. [Call a Rhai Function from Rust](engine/call-fn.md)
4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md)
5. [Evaluate Expressions Only](engine/expressions.md)
6. [Raw Engine](engine/raw.md)
4. [Extend Rhai with Rust](rust.md)
5. [Extend Rhai with Rust](rust/index.md)
1. [Traits](rust/traits.md)
2. [Register a Rust Function](rust/functions.md)
1. [String Parameters in Rust Functions](rust/strings.md)
@@ -42,7 +43,7 @@ The Rhai Scripting Language
4. [Printing Custom Types](rust/print-custom.md)
9. [Scope - Initializing and Maintaining State](rust/scope.md)
10. [Engine Configuration Options](rust/options.md)
5. [Rhai Language Reference](language.md)
6. [Rhai Language Reference](language/index.md)
1. [Comments](language/comments.md)
2. [Values and Types](language/values-and-types.md)
1. [Dynamic Values](language/dynamic.md)
@@ -70,9 +71,10 @@ The Rhai Scripting Language
12. [Return Values](language/return.md)
13. [Throw Exception on Error](language/throw.md)
14. [Functions](language/functions.md)
1. [Function Overloading](language/overload.md)
2. [Call Method as Function](language/method.md)
3. [Function Pointers](language/fn-ptr.md)
1. [Call Method as Function](language/method.md)
2. [Overloading](language/overload.md)
3. [Namespaces](language/fn-namespaces.md)
4. [Function Pointers](language/fn-ptr.md)
15. [Print and Debug](language/print-debug.md)
16. [Modules](language/modules/index.md)
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
@@ -81,7 +83,7 @@ The Rhai Scripting Language
4. [Create from AST](language/modules/ast.md)
5. [Module Resolvers](language/modules/resolvers.md)
1. [Implement a Custom Module Resolver](language/modules/imp-resolver.md)
6. [Safety and Protection](safety/index.md)
7. [Safety and Protection](safety/index.md)
1. [Checked Arithmetic](safety/checked.md)
2. [Sand-Boxing](safety/sandbox.md)
3. [Maximum Length of Strings](safety/max-string-size.md)
@@ -92,7 +94,7 @@ The Rhai Scripting Language
7. [Maximum Number of Modules](safety/max-modules.md)
8. [Maximum Call Stack Depth](safety/max-call-stack.md)
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
7. [Advanced Topics](advanced.md)
8. [Advanced Topics](advanced.md)
1. [Object-Oriented Programming (OOP)](language/oop.md)
2. [Script Optimization](engine/optimize/index.md)
1. [Optimization Levels](engine/optimize/optimize-levels.md)
@@ -102,7 +104,7 @@ The Rhai Scripting Language
5. [Volatility Considerations](engine/optimize/volatility.md)
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
3. [Eval Statement](language/eval.md)
8. [Appendix](appendix/index.md)
9. [Appendix](appendix/index.md)
1. [Keywords](appendix/keywords.md)
2. [Operators](appendix/operators.md)
3. [Literals](appendix/literals.md)

View File

@@ -8,7 +8,7 @@ Easy
* Easy-to-use language similar to JavaScript+Rust with dynamic typing.
* Tight integration with native Rust [functions] and [types][custom types], including [getters/setters]({{rootUrl}}/rust/getters-setters.md), [methods][custom type] and [indexers]({{rootUrl}}/rust/indexers.md).
* Tight integration with native Rust [functions] and [types][custom types], including [getters/setters], [methods][custom type] and [indexers].
* Freely pass Rust variables/constants into a script via an external [`Scope`].

View File

@@ -1,7 +1,7 @@
What is Rhai
============
{{#include links.md}}
{{#include ../links.md}}
Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way
to add scripting to any application.

View File

@@ -11,6 +11,8 @@ Other online documentation resources for Rhai:
* [`LIB.RS`](https://lib.rs/crates/rhai) - Rhai library info
* [Online Playground][playground] - Run scripts directly from editor
Other cool projects to check out:
* [ChaiScript](http://chaiscript.com/) - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being led by my cousin.

View File

@@ -3,7 +3,7 @@ Compile a Script (to AST)
{{#include ../links.md}}
To repeatedly evaluate a script, _compile_ it first into an AST (abstract syntax tree) form:
To repeatedly evaluate a script, _compile_ it first into an `AST` (abstract syntax tree) form:
```rust
// Compile to an AST and store it for later evaluations

View File

@@ -1,7 +1,7 @@
Using the Engine
================
{{#include links.md}}
{{#include ../links.md}}
Rhai's interpreter resides in the [`Engine`] type under the master `rhai` namespace.

View File

@@ -4,13 +4,13 @@ Re-Optimize an AST
{{#include ../../links.md}}
Sometimes it is more efficient to store one single, large script with delimited code blocks guarded by
constant variables. This script is compiled once to an `AST`.
constant variables. This script is compiled once to an [`AST`].
Then, depending on the execution environment, constants are passed into the [`Engine`] and the `AST`
Then, depending on the execution environment, constants are passed into the [`Engine`] and the [`AST`]
is _re_-optimized based on those constants via the `Engine::optimize_ast` method,
effectively pruning out unused code sections.
The final, optimized `AST` is then used for evaluations.
The final, optimized [`AST`] is then used for evaluations.
```rust
// Compile master script to AST

View File

@@ -0,0 +1,141 @@
Function Namespaces
==================
{{#include ../links.md}}
Each Function is a Separate Compilation Unit
-------------------------------------------
[Functions] in Rhai are _pure_ and they form individual _compilation units_.
This means that individual functions can be separated, exported, re-grouped, imported,
and generally mix-'n-match-ed with other completely unrelated scripts.
For example, the `AST::merge` method allows merging all functions in one [`AST`] into another,
forming a new, combined, group of functions.
In general, there are two types of _namespaces_ where functions are looked up:
| Namespace | Source | Lookup method | How Many |
| --------- | ---------------------------------------------------------------------- | --------------------------------- | :----------------------: |
| Global | `Engine::register_XXX` API, [`AST`] being evaluated, [packages] loaded | Simple function name | One |
| Module | [`Module`] | Namespace-qualified function name | As many as [`import`]-ed |
Global Namespace
----------------
There is one _global_ namespace for every [`Engine`], which includes:
* All the native Rust functions registered via the `Engine::register_XXX` API.
* All the Rust functions defined in [packages] that are loaded into the [`Engine`].
In addition, during evaluation of an [`AST`], all script-defined functions bundled together within
the [`AST`] are added to the global namespace and override any existing registered functions of
the same names and number of parameters.
Anywhere in a Rhai script, when a function call is made, it is searched within the global namespace.
Therefore, function calls in Rhai are _late_ bound - meaning that the function called cannot be
determined or guaranteed and there is no way to _lock down_ the function being called.
This aspect is very similar to JavaScript before ES6 modules.
```rust
// Compile a script into AST
let ast1 = engine.compile(
r#"
fn message() { "Hello!" } // greeting message
fn say_hello() {
print(message()); // prints message
}
say_hello();
"#
)?;
// Compile another script with an overriding function
let ast2 = engine.compile(r#"fn message() { "Boo!" }"#)?;
// Merge the two AST's
let ast = ast1.merge(ast2); // 'message' will be overwritten
engine.consume_ast(&ast)?; // prints 'Boo!'
```
Therefore, care must be taken when _cross-calling_ functions to make sure that the correct
functions are called.
The only practical way to ensure that a function is a correct one is to use [modules] -
i.e. define the function in a separate module and then [`import`] it:
```rust
message.rhai:
fn message() { "Hello!" }
script.rhai:
fn say_hello() {
import "message" as msg;
print(msg::message());
}
say_hello();
```
Module Namespaces
-----------------
[Modules] can be dynamically loaded into a Rhai script using the [`import`] keyword.
When that happens, functions defined within the [module] can be called with a _qualified_ name.
There is a catch, though, if functions in a module script refer to global functions
defined _within the script_. When called later, those functions will be searched in the
current global namespace and may not be found.
```rust
greeting.rhai:
fn message() { "Hello!" };
fn say_hello() { print(message()); }
say_hello(); // 'message' is looked up in the global namespace
script.rhai:
import "greeting" as g;
g::say_hello(); // <- error: function not found - 'message'
```
In the example above, although the module `greeting.rhai` loads fine (`"Hello!"` is printed),
the subsequent call using the _namespace-qualified_ function name fails to find the same function
'`message`' which now essentially becomes `g::message`. The call fails as there is no more
function named '`message`' in the global namespace.
Therefore, when writing functions for a [module], make sure that those functions are as _pure_
as possible and avoid cross-calling them from each other. A [function pointer] is a valid technique
to call another function within a module-defined function:
```rust
greeting.rhai:
fn message() { "Hello!" };
fn say_hello(msg_func) { // 'msg_func' is a function pointer
print(msg_func.call()); // call via the function pointer
}
say_hello(); // 'message' is looked up in the global namespace
script.rhai:
import "greeting" as g;
fn my_msg() {
import "greeting" as g; // <- must import again here...
g::message() // <- ... otherwise will not find module 'g'
}
g::say_hello(Fn("my_msg")); // prints 'Hello!'
```

View File

@@ -54,7 +54,34 @@ let fn_name = "hello"; // the function name does not have to exist yet
let hello = Fn(fn_name + "_world");
hello.call(0); // error: function not found - "hello_world (i64)"
hello.call(0); // error: function not found - 'hello_world (i64)'
```
Global Namespace Only
--------------------
Because of their dynamic nature, function pointers cannot refer to functions in a _module_ [namespace][function namespace]
(i.e. functions in [`import`]-ed modules). They can only refer to functions within the global [namespace][function namespace].
See [function namespaces] for more details.
```rust
import "foo" as f; // assume there is 'f::do_something()'
f::do_something(); // works!
let p = Fn("f::do_something");
p.call(); // error: function not found - 'f::do_something'
fn do_something_now() { // call it from a local function
import "foo" as f;
f::do_something();
}
let p = Fn("do_something_now");
p.call(); // works!
```

View File

@@ -1,7 +1,7 @@
Rhai Language Reference
======================
{{#include links.md}}
{{#include ../links.md}}
This section outlines the Rhai language.

View File

@@ -3,9 +3,9 @@ Call Method as Function
{{#include ../links.md}}
Property getters/setters and methods in a Rust custom type registered with the [`Engine`] can be called
Property [getters/setters] and [methods][custom types] in a Rust custom type registered with the [`Engine`] can be called
just like a regular function. In fact, like Rust, property getters/setters and object methods
are registered as regular functions in Rhai that take a first `&mut` parameter.
are registered as regular [functions] in Rhai that take a first `&mut` parameter.
Unlike functions defined in script (for which all arguments are passed by _value_),
native Rust functions may mutate the object (or the first argument if called in normal function call style).

View File

@@ -3,7 +3,7 @@ Create a Module from an AST
{{#include ../../links.md}}
It is easy to convert a pre-compiled `AST` into a module: just use `Module::eval_ast_as_new`.
It is easy to convert a pre-compiled [`AST`] into a module: just use `Module::eval_ast_as_new`.
Don't forget the [`export`] statement, otherwise there will be no variables exposed by the module
other than non-[`private`] functions (unless that's intentional).

View File

@@ -3,9 +3,9 @@ Export Variables, Functions and Sub-Modules in Module
{{#include ../../links.md}}
A _module_ is a single script (or pre-compiled `AST`) containing global variables, functions and sub-modules.
A _module_ is a single script (or pre-compiled [`AST`]) containing global variables, functions and sub-modules.
A module can be created from a script via the `Module::eval_ast_as_new` method. When given an `AST`,
A module can be created from a script via the `Module::eval_ast_as_new` method. When given an [`AST`],
it is first evaluated, then the following items are exposed as members of the new module:
* Global variables - essentially all variables that remain in the [`Scope`] at the end of a script run - that are exported. Variables not exported (via the `export` statement) remain hidden.

View File

@@ -3,7 +3,7 @@ Function Overloading
{{#include ../links.md}}
Functions defined in script can be _overloaded_ by _arity_ (i.e. they are resolved purely upon the function's _name_
[Functions] defined in script can be _overloaded_ by _arity_ (i.e. they are resolved purely upon the function's _name_
and _number_ of parameters, but not parameter _types_ since all parameters are the same type - [`Dynamic`]).
New definitions _overwrite_ previous definitions of the same name and number of parameters.

View File

@@ -15,11 +15,13 @@
[minimal builds]: {{rootUrl}}/start/builds/minimal.md
[WASM]: {{rootUrl}}/start/builds/wasm.md
[playground]: https://alvinhochun.github.io/rhai-demo
[`Engine`]: {{rootUrl}}/engine/hello-world.md
[traits]: {{rootUrl}}/rust/traits.md
[`private`]: {{rootUrl}}/engine/call-fn.md
[`Func`]: {{rootUrl}}/engine/func.md
[`AST`]: {{rootUrl}}/engine/compile.md
[`eval_expression`]: {{rootUrl}}/engine/expressions.md
[`eval_expression_with_scope`]: {{rootUrl}}/engine/expressions.md
[raw `Engine`]: {{rootUrl}}/engine/raw.md
@@ -38,6 +40,8 @@
[custom type]: {{rootUrl}}/rust/custom.md
[custom types]: {{rootUrl}}/rust/custom.md
[getters/setters]: {{rootUrl}}/rust/getters-setters.md
[indexers]: {{rootUrl}}/rust/indexers.md
[`instant::Instant`]: https://crates.io/crates/instant
@@ -70,6 +74,8 @@
[functions]: {{rootUrl}}/language/functions.md
[function pointer]: {{rootUrl}}/language/fn-ptr.md
[function pointers]: {{rootUrl}}/language/fn-ptr.md
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
[`Module`]: {{rootUrl}}/language/modules/index.md
[module]: {{rootUrl}}/language/modules/index.md
@@ -89,7 +95,7 @@
[maximum length of strings]: {{rootUrl}}/safety/max-string-size.md
[maximum size of arrays]: {{rootUrl}}/safety/max-array-size.md
[maximum size of object maps]: {{rootUrl}}/safety/max-map-size.md
[progress]:/safety/progress.md
[progress]: {{rootUrl}}/safety/progress.md
[script optimization]: {{rootUrl}}/engine/optimize/index.md
[`OptimizationLevel::Full`]: {{rootUrl}}/engine/optimize/optimize-levels.md

View File

@@ -1,7 +1,7 @@
Extend Rhai with Rust
====================
{{#include links.md}}
{{#include ../links.md}}
Most features and functionalities required by a Rhai script should actually be coded in Rust,
which leverages the superior native run-time speed.

View File

@@ -1 +0,0 @@
# Safety and Protection

View File

@@ -25,7 +25,7 @@ This limit may be changed via the `Engine::set_max_expr_depths` method.
There are two limits to set, one for the maximum depth at global level, and the other for function bodies.
A script exceeding the maximum nesting depths will terminate with a parsing error.
The malicious `AST` will not be able to get past parsing in the first place.
The malicious [`AST`] will not be able to get past parsing in the first place.
This check can be disabled via the [`unchecked`] feature for higher performance (but higher risks as well).

View File

@@ -14,7 +14,7 @@ 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.<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` | 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`. |
@@ -24,7 +24,7 @@ more control over what a script can (or cannot) do.
| `no_function` | Disable script-defined [functions]. |
| `no_module` | Disable loading external [modules]. |
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
| `internals` | Expose internal data structures (e.g. `AST` nodes). Beware that Rhai internals are volatile and may change from version to version. |
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
Example

View File

@@ -1,6 +1,6 @@
Getting Started
===============
{{#include links.md}}
{{#include ../links.md}}
This section shows how to install the Rhai crate into a Rust application.

View File

@@ -3,8 +3,10 @@ Install the Rhai Crate
{{#include ../links.md}}
Install the Rhai crate from [`crates.io`](https:/crates.io/crates/rhai/), start by looking up the
latest version and adding this line under `dependencies` in `Cargo.toml`:
In order to use Rhai in a project, the Rhai crate must first be made a dependency.
The easiest way is to install the Rhai crate from [`crates.io`](https:/crates.io/crates/rhai/),
starting by looking up the latest version and adding this line under `dependencies` in the project's `Cargo.toml`:
```toml
[dependencies]

View File

@@ -0,0 +1,10 @@
Online Playground
=================
{{#include ../links.md}}
Rhai provides an [online playground][playground] to try out its language and engine features
without having to install anything.
The playground provides a syntax-highlighting script editor with example snippets.
Scripts can be evaluated directly from the editor.