Add linkcheck, fix typos and expand.

This commit is contained in:
Stephen Chung
2020-06-20 22:56:56 +08:00
parent ffe0c559be
commit 6121aaec9d
17 changed files with 285 additions and 81 deletions

View File

@@ -40,9 +40,9 @@ Boolean operators
| -------- | ------------------------------------- |
| `!` | Boolean _Not_ |
| `&&` | Boolean _And_ (short-circuits) |
| `\|\|` | Boolean _Or_ (short-circuits) |
| `||` | Boolean _Or_ (short-circuits) |
| `&` | Boolean _And_ (doesn't short-circuit) |
| `\|` | Boolean _Or_ (doesn't short-circuit) |
| `|` | Boolean _Or_ (doesn't short-circuit) |
Double boolean operators `&&` and `||` _short-circuit_, meaning that the second operand will not be evaluated
if the first one already proves the condition wrong.

View File

@@ -5,3 +5,14 @@ Modules
Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_.
Modules can be disabled via the [`no_module`] feature.
A module is of the type `Module` and encapsulates a Rhai script together with the functions defined
by that script.
The script text is run, variables are then selectively exposed via the [`export`] statement.
Functions defined by the script are automatically exported.
Modules loaded within this module at the global level become _sub-modules_ and are also automatically exported.
Other scripts can then load this module and use the variables and functions exported
as if they were defined inside the same script.

View File

@@ -1,17 +1,53 @@
Export Variables and Functions from Modules
==========================================
Export Variables, Functions and Sub-Modules in Module
===================================================
{{#include ../../links.md}}
A _module_ is a single script (or pre-compiled `AST`) containing global variables and functions.
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`,
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.
* Functions not specifically marked `private`.
* Global modules that remain in the [`Scope`] at the end of a script run.
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 invisible to the outside.
Variables not exported are _private_ and hidden to the outside.
On the other hand, all functions are automatically exported, _unless_ it is explicitly opt-out with the [`private`] prefix.
```rust
// This is a module script.
Functions declared [`private`] are invisible to the outside.
let private = 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
export x as answer; // the variable 'x' is exported under the alias 'answer'
// another script can load this module and access 'x' as 'module::answer'
{
let inner = 0; // local variable - it disappears when the statement block ends,
// therefore it is not 'global' and is not exported
export inner; // exporting an temporary variable has no effect
}
```
Functions
---------
All functions are automatically exported, _unless_ it is explicitly opt-out with the [`private`] prefix.
Functions declared [`private`] are hidden to the outside.
Everything exported from a module is **constant** (**read-only**).
@@ -20,13 +56,25 @@ Everything exported from a module is **constant** (**read-only**).
fn inc(x) { x + 1 } // script-defined function - default public
private fn foo() {} // private function - invisible to outside
let private = 123; // variable not exported - default invisible to outside
let x = 42; // this will be exported below
export x; // the variable 'x' is exported under its own name
export x as answer; // the variable 'x' is exported under the alias 'answer'
// another script can load this module and access 'x' as 'module::answer'
private fn foo() {} // private function - hidden
```
Sub-Modules
-----------
All loaded modules are automatically exported as sub-modules.
To prevent a module from being exported, load it inside a block statement so that it goes away at the
end of the block.
```rust
// This is a module script.
import "hello" as foo; // exported as sub-module 'foo'
{
import "world" as bar; // not exported - the module disappears at the end
// of the statement block and is not 'global'
}
```

View File

@@ -0,0 +1,60 @@
Implement a Custom Module Resolver
=================================
{{#include ../../links.md}}
For many applications in which Rhai is embedded, it is necessary to customize the way that modules
are resolved. For instance, modules may need to be loaded from script texts stored in a database,
not in the file system.
A module resolver must implement the trait `rhai::ModuleResolver`, which contains only one function:
`resolve`.
When Rhai prepares to load a module, `ModuleResolver::resolve` is called with the name
of the _module path_ (i.e. the path specified in the [`import`] statement). Upon success, it should
return a [`Module`]; if the module cannot be load, return `EvalAltResult::ErrorModuleNotFound`.
Example
-------
```rust
use rhai::{ModuleResolver, Module, Engine, EvalAltResult};
// Define a custom module resolver.
struct MyModuleResolver {}
// Implement the 'ModuleResolver' trait.
impl ModuleResolver for MyModuleResolver {
// Only required function.
fn resolve(
&self,
engine: &Engine, // reference to the current 'Engine'
path: &str, // the module path
pos: Position, // location of the 'import' statement
) -> Result<Module, Box<EvalAltResult>> {
// Check module path.
if is_valid_module_path(path) {
// Load the custom module.
let module: Module = load_secret_module(path);
Ok(module)
} else {
Err(Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos)))
}
}
}
fn main() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
// Set the custom module resolver into the 'Engine'.
engine.set_module_resolver(Some(MyModuleResolver {}));
engine.consume(r#"
import "hello" as foo; // this 'import' statement will call
// 'MyModuleResolver::resolve' with "hello" as path
foo:bar();
"#)?;
Ok(())
}
```

View File

@@ -18,10 +18,11 @@ lock::status = "off"; // <- runtime error - cannot modify a constant
```
`import` statements are _scoped_, meaning that they are only accessible inside the scope that they're imported.
They can appear anywhere a normal statement can be, but in the vast majority of cases `import` statements are
group at the beginning of a script.
It is, however, not advised to deviate from this common practice unless there is a _Very Good Reason™_.
They can appear anywhere a normal statement can be, but in the vast majority of cases `import` statements are
group at the beginning of a script. It is, however, not advised to deviate from this common practice unless
there is a _Very Good Reason™_.
Especially, do not place an `import` statement within a loop; doing so will repeatedly re-load the same module
during every iteration of the loop!
@@ -34,7 +35,7 @@ if secured { // new block scope
c::encrypt(key); // use a function in the module
} // the module disappears at the end of the block scope
crypto::encrypt(others); // <- this causes a run-time error because the 'crypto' module
c::encrypt(others); // <- this causes a run-time error because the 'crypto' module
// is no longer available!
for x in range(0, 1000) {

View File

@@ -31,7 +31,7 @@ Binary Operators
| `%` | Modulo (remainder) | |
| `~` | Power | |
| `&` | Binary _And_ bit-mask | Yes |
| `\|` | Binary _Or_ bit-mask | Yes |
| `|` | Binary _Or_ bit-mask | Yes |
| `^` | Binary _Xor_ bit-mask | Yes |
| `<<` | Left bit-shift | Yes |
| `>>` | Right bit-shift | Yes |

View File

@@ -5,20 +5,20 @@ Values and Types
The following primitive types are supported natively:
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | --------------------- |
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
| **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. |
| **Immutable Unicode string** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. |
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
| **Timestamp** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md)) | `std::time::Instant` | `"timestamp"` | _not supported_ |
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
| **Nothing/void/nil/null** (or whatever it is called) | `()` | `"()"` | `""` _(empty string)_ |
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | --------------------- |
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
| **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. |
| **Immutable Unicode string** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. |
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
| **Timestamp** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md), disabled with [`no_std`]) | `std::time::Instant` ([instant::Instant](https://crates.io/crates/instant) if not [WASM] build) | `"timestamp"` | _not supported_ |
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
| **Nothing/void/nil/null/Unit** (or whatever it is called) | `()` | `"()"` | `""` _(empty string)_ |
All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different -
they even cannot be added together. This is very similar to Rust.