diff --git a/README.md b/README.md
index b4f0853d..ac9a79a1 100644
--- a/README.md
+++ b/README.md
@@ -42,4 +42,4 @@ Features
Documentation
-------------
-See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai script engine and language.
+See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai scripting engine and language.
diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md
index da62423b..2ba5e9e7 100644
--- a/doc/src/SUMMARY.md
+++ b/doc/src/SUMMARY.md
@@ -9,12 +9,12 @@ The Rhai Scripting Language
2. [Getting Started](start.md)
1. [Install the Rhai Crate](start/install.md)
2. [Optional Features](start/features.md)
- 3. [Special Builds](start/builds.md)
- 1. [Performance Build](start/builds/performance.md)
- 2. [Minimal Build](start/builds/minimal.md)
- 3. [no-std Build](start/builds/no-std.md)
+ 3. [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.md)
+ 4. [Examples](start/examples/index.md)
1. [Rust](start/examples/rust.md)
2. [Scripts](start/examples/scripts.md)
3. [Using the `Engine`](engine.md)
@@ -30,7 +30,7 @@ The Rhai Scripting Language
1. [String Parameters in Rust Functions](rust/strings.md)
3. [Register a Generic Rust Function](rust/generic.md)
4. [Register a Fallible Rust Function](rust/fallible.md)
- 5. [Packages](rust/packages.md)
+ 5. [Packages](rust/packages/index.md)
1. [Built-in Packages](rust/packages/builtin.md)
2. [Create a Custom Package](rust/packages/create.md)
6. [Override a Built-in Function](rust/override.md)
@@ -40,7 +40,7 @@ The Rhai Scripting Language
2. [Indexers](rust/indexers.md)
3. [Disable Custom Types](rust/disable-custom.md)
4. [Printing Custom Types](rust/print-custom.md)
- 9. [Scope - Initializing and Maintaining State](rust/scope.md)
+ 9. [Scope - Initializing and Maintaining State](rust/scope.md)
10. [Engine Configuration Options](rust/options.md)
5. [Rhai Language Reference](language.md)
1. [Comments](language/comments.md)
@@ -72,14 +72,14 @@ The Rhai Scripting Language
1. [Function Overloading](language/overload.md)
2. [Call Method as Function](language/method.md)
15. [Print and Debug](language/print-debug.md)
- 16. [Modules](language/modules.md)
+ 16. [Modules](language/modules/index.md)
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
2. [Import Modules](language/modules/import.md)
3. [Create from Rust](language/modules/rust.md)
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.md)
+6. [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)
@@ -91,7 +91,7 @@ The Rhai Scripting Language
8. [Maximum Call Stack Depth](safety/max-call-stack.md)
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
7. [Advanced Topics](advanced.md)
- 1. [Script Optimization](engine/optimize.md)
+ 1. [Script Optimization](engine/optimize/index.md)
1. [Optimization Levels](engine/optimize/optimize-levels.md)
2. [Re-Optimize an AST](engine/optimize/reoptimize.md)
3. [Eager Function Evaluation](engine/optimize/eager.md)
@@ -99,3 +99,7 @@ The Rhai Scripting Language
5. [Volatility Considerations](engine/optimize/volatility.md)
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
2. [Eval Statement](language/eval.md)
+8. [Appendix](appendix/index.md)
+ 1. [Keywords](appendix/keywords.md)
+ 2. [Operators](appendix/operators.md)
+ 3. [Literals](appendix/literals.md)
diff --git a/doc/src/about/related.md b/doc/src/about/related.md
index b6d8d8df..f91fdb64 100644
--- a/doc/src/about/related.md
+++ b/doc/src/about/related.md
@@ -5,8 +5,11 @@ Related Resources
Other online documentation resources for Rhai:
-* [`DOCS.RS`](https://docs.rs/rhai)
+* [`crates.io`](https://crates.io/crates/rhai/) - Rhai crate
+* [`DOCS.RS`](https://docs.rs/rhai) - Rhai API documentation
+
+* [`LIB.RS`](https://lib.rs/crates/rhai) - Rhai library info
Other cool projects to check out:
diff --git a/doc/src/advanced.md b/doc/src/advanced.md
index d1982c2c..3cb28cb5 100644
--- a/doc/src/advanced.md
+++ b/doc/src/advanced.md
@@ -7,4 +7,4 @@ This section covers advanced features such as:
* [Script optimization]
-* The dreaded (or beloved depending on your taste) [`eval`] statement
+* The dreaded (or beloved for those with twisted tastes) [`eval`] statement
diff --git a/doc/src/appendix/index.md b/doc/src/appendix/index.md
new file mode 100644
index 00000000..bdb6cae9
--- /dev/null
+++ b/doc/src/appendix/index.md
@@ -0,0 +1,6 @@
+Appendix
+========
+
+{{#include ../links.md}}
+
+This section contains miscellaneous reference materials.
diff --git a/doc/src/appendix/keywords.md b/doc/src/appendix/keywords.md
new file mode 100644
index 00000000..f6cdd67e
--- /dev/null
+++ b/doc/src/appendix/keywords.md
@@ -0,0 +1,30 @@
+Keywords List
+=============
+
+{{#include ../links.md}}
+
+| Keyword | Description |
+| :--------: | ------------------------------------- |
+| `true` | Boolean true literal |
+| `false` | Boolean false literal |
+| `let` | Variable declaration |
+| `const` | Constant declaration |
+| `if` | If statement |
+| `else` | else block of if statement |
+| `while` | While loop |
+| `loop` | Infinite loop |
+| `for` | For loop |
+| `in` | Containment test, part of for loop |
+| `continue` | Continue a loop at the next iteration |
+| `break` | Loop breaking |
+| `return` | Return value |
+| `throw` | Throw exception |
+| `private` | Mark function private |
+| `import` | Import module |
+| `export` | Export variable |
+| `as` | Alias for variable export |
+| `fn` | Function definition |
+| `type_of` | Get type name of value |
+| `print` | Print value |
+| `debug` | Print value in debug format |
+| `eval` | Evaluate script |
diff --git a/doc/src/appendix/literals.md b/doc/src/appendix/literals.md
new file mode 100644
index 00000000..1f14b9fb
--- /dev/null
+++ b/doc/src/appendix/literals.md
@@ -0,0 +1,16 @@
+Literals Syntax
+===============
+
+{{#include ../links.md}}
+
+| Type | Literal syntax |
+| :--------------------------------: | :---------------------------------------: |
+| `INT` | `42`, `-123`, `0` |
+| `FLOAT` | `42.0`, `-123.456`, `0.0` |
+| [String] | `"... \x?? \u???? \U???????? ..."` |
+| Character | `"... \x?? \u???? \U???????? ..."` |
+| [`Array`] | `[ ???, ???, ??? ]` |
+| [Object map] | `#{ a: ???, b: ???, c: ???, "def": ??? }` |
+| Boolean true | `true` |
+| Boolean false | `false` |
+| `Nothing`/`null`/`nil`/`void`/Unit | `()` |
diff --git a/doc/src/appendix/operators.md b/doc/src/appendix/operators.md
new file mode 100644
index 00000000..4a1f4a2f
--- /dev/null
+++ b/doc/src/appendix/operators.md
@@ -0,0 +1,30 @@
+Operators
+=========
+
+{{#include ../links.md}}
+
+| Operator | Description | Binary? |
+| :---------------: | ---------------------------- | :-----: |
+| `+` | Add | Yes |
+| `-` | Subtract, Minus | Yes/No |
+| `*` | Multiply | Yes |
+| `/` | Divide | Yes |
+| `%` | Modulo | Yes |
+| `~` | Power | Yes |
+| `>>` | Right bit-shift | Yes |
+| `<<` | Left bit-shift | Yes |
+| `&` | Bit-wise AND, Boolean AND | Yes |
+| \|
| Bit-wise OR, Boolean OR | Yes |
+| `^` | Bit-wise XOR | Yes |
+| `==` | Equals to | Yes |
+| `~=` | Not equals to | Yes |
+| `>` | Greater than | Yes |
+| `>=` | Greater than or equals to | Yes |
+| `<` | Less than | Yes |
+| `<=` | Less than or equals to | Yes |
+| `>=` | Greater than or equals to | Yes |
+| `&&` | Boolean AND (short-circuits) | Yes |
+| \|\|
| Boolean OR (short-circuits) | Yes |
+| `~` | Boolean NOT | No |
+| `[` .. `]` | Indexing | Yes |
+| `.` | Property access, Method call | Yes |
diff --git a/doc/src/context.json b/doc/src/context.json
index f898a0c5..187955d4 100644
--- a/doc/src/context.json
+++ b/doc/src/context.json
@@ -1,4 +1,5 @@
{
+ "version": "0.15.1",
"rootUrl": "",
"rootUrlX": "/rhai"
}
\ No newline at end of file
diff --git a/doc/src/engine/optimize.md b/doc/src/engine/optimize/index.md
similarity index 99%
rename from doc/src/engine/optimize.md
rename to doc/src/engine/optimize/index.md
index 10ee4f21..e28aa748 100644
--- a/doc/src/engine/optimize.md
+++ b/doc/src/engine/optimize/index.md
@@ -1,7 +1,7 @@
Script Optimization
===================
-{{#include ../links.md}}
+{{#include ../../links.md}}
Rhai includes an _optimizer_ that tries to optimize a script after parsing.
This can reduce resource utilization and increase execution speed.
diff --git a/doc/src/engine/optimize/optimize-levels.md b/doc/src/engine/optimize/optimize-levels.md
index 11511099..97dc27ea 100644
--- a/doc/src/engine/optimize/optimize-levels.md
+++ b/doc/src/engine/optimize/optimize-levels.md
@@ -3,10 +3,7 @@ Optimization Levels
{{#include ../../links.md}}
-Set Optimization Level
----------------------
-
-There are actually three levels of optimizations: `None`, `Simple` and `Full`.
+There are three levels of optimization: `None`, `Simple` and `Full`.
* `None` is obvious - no optimization on the AST is performed.
@@ -16,6 +13,10 @@ There are actually three levels of optimizations: `None`, `Simple` and `Full`.
* `Full` is _much_ more aggressive, _including_ running functions on constant arguments to determine their result.
One benefit to this is that many more optimization opportunities arise, especially with regards to comparison operators.
+
+Set Optimization Level
+---------------------
+
An [`Engine`]'s optimization level is set via a call to `Engine::set_optimization_level`:
```rust
diff --git a/doc/src/engine/optimize/reoptimize.md b/doc/src/engine/optimize/reoptimize.md
index d3ac0f26..bd173be8 100644
--- a/doc/src/engine/optimize/reoptimize.md
+++ b/doc/src/engine/optimize/reoptimize.md
@@ -3,15 +3,38 @@ Re-Optimize an AST
{{#include ../../links.md}}
-If it is ever needed to _re_-optimize an `AST`, use the `optimize_ast` method:
+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`.
+
+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.
```rust
-// Compile script to AST
-let ast = engine.compile("40 + 2")?;
+// Compile master script to AST
+let master_ast = engine.compile(
+r"
+ if SCENARIO_1 {
+ do_work();
+ } else if SCENARIO_2 {
+ do_something();
+ } else if SCENARIO_3 {
+ do_something_else();
+ } else {
+ do_nothing();
+ }
+")?;
-// Create a new 'Scope' - put constants in it to aid optimization if using 'OptimizationLevel::Full'
-let scope = Scope::new();
+// Create a new 'Scope' - put constants in it to aid optimization
+let mut scope = Scope::new();
+scope.push_constant("SCENARIO_1", true);
+scope.push_constant("SCENARIO_2", false);
+scope.push_constant("SCENARIO_3", false);
// Re-optimize the AST
-let ast = engine.optimize_ast(&scope, &ast, OptimizationLevel::Full);
+let new_ast = engine.optimize_ast(&scope, master_ast.clone(), OptimizationLevel::Simple);
+
+// 'new_ast' is essentially: 'do_work()'
```
diff --git a/doc/src/language/constants.md b/doc/src/language/constants.md
index 82811166..b4ced7df 100644
--- a/doc/src/language/constants.md
+++ b/doc/src/language/constants.md
@@ -13,7 +13,8 @@ print(x * 2); // prints 84
x = 123; // <- syntax error: cannot assign to constant
```
-Constants must be assigned a _value_, not an expression.
+Unlike variables which need not have initial values (default to [`()`]),
+constants must be assigned one, and it must be a constant _value_, not an expression.
```rust
const x = 40 + 2; // <- syntax error: cannot assign expression to constant
diff --git a/doc/src/language/eval.md b/doc/src/language/eval.md
index f79810e5..f7f9965b 100644
--- a/doc/src/language/eval.md
+++ b/doc/src/language/eval.md
@@ -6,29 +6,29 @@
Or "How to Shoot Yourself in the Foot even Easier"
------------------------------------------------
-Saving the best for last: in addition to script optimizations, there is the ever-dreaded... `eval` function!
+Saving the best for last, there is the ever-dreaded... `eval` function!
```rust
let x = 10;
fn foo(x) { x += 12; x }
-let script = "let y = x;"; // build a script
+let script = "let y = x;"; // build a script
script += "y += foo(y);";
script += "x + y";
-let result = eval(script); // <- look, JS, we can also do this!
+let result = eval(script); // <- look, JS, we can also do this!
-print("Answer: " + result); // prints 42
+print("Answer: " + result); // prints 42
-print("x = " + x); // prints 10: functions call arguments are passed by value
-print("y = " + y); // prints 32: variables defined in 'eval' persist!
+print("x = " + x); // prints 10: functions call arguments are passed by value
+print("y = " + y); // prints 32: variables defined in 'eval' persist!
-eval("{ let z = y }"); // to keep a variable local, use a statement block
+eval("{ let z = y }"); // to keep a variable local, use a statement block
-print("z = " + z); // <- error: variable 'z' not found
+print("z = " + z); // <- error: variable 'z' not found
-"print(42)".eval(); // <- nope... method-call style doesn't work
+"print(42)".eval(); // <- nope... method-call style doesn't work
```
Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_,
@@ -45,8 +45,8 @@ not inside another function call!
```rust
let script = "x += 32";
let x = 10;
-eval(script); // variable 'x' in the current scope is visible!
-print(x); // prints 42
+eval(script); // variable 'x' in the current scope is visible!
+print(x); // prints 42
// The above is equivalent to:
let script = "x += 32";
@@ -65,7 +65,7 @@ disable `eval` by overloading it, probably with something that throws.
```rust
fn eval(script) { throw "eval is evil! I refuse to run " + script }
-let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2"
+let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2"
```
Or overload it from Rust:
@@ -86,11 +86,11 @@ There is even a package named [`EvalPackage`]({{rootUrl}}/rust/packages.md) whic
```rust
use rhai::Engine;
-use rhai::packages::Package // load the 'Package' trait to use packages
-use rhai::packages::EvalPackage; // the 'eval' package disables 'eval'
+use rhai::packages::Package // load the 'Package' trait to use packages
+use rhai::packages::EvalPackage; // the 'eval' package disables 'eval'
let mut engine = Engine::new();
-let package = EvalPackage::new(); // create the package
+let package = EvalPackage::new(); // create the package
-engine.load_package(package.get()); // load the package
+engine.load_package(package.get()); // load the package
```
diff --git a/doc/src/language/for.md b/doc/src/language/for.md
index eb2406fc..ff784d12 100644
--- a/doc/src/language/for.md
+++ b/doc/src/language/for.md
@@ -5,50 +5,53 @@
Iterating through a range or an [array] is provided by the `for` ... `in` loop.
+Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;
+`break` can be used to break out of the loop unconditionally.
+
```rust
// Iterate through string, yielding characters
let s = "hello, world!";
for ch in s {
- if ch > 'z' { continue; } // skip to the next iteration
+ if ch > 'z' { continue; } // skip to the next iteration
print(ch);
- if x == '@' { break; } // break out of for loop
+ if x == '@' { break; } // break out of for loop
}
// Iterate through array
let array = [1, 3, 5, 7, 9, 42];
for x in array {
- if x > 10 { continue; } // skip to the next iteration
+ if x > 10 { continue; } // skip to the next iteration
print(x);
- if x == 42 { break; } // break out of for loop
+ if x == 42 { break; } // break out of for loop
}
// The 'range' function allows iterating from first to last-1
for x in range(0, 50) {
- if x > 10 { continue; } // skip to the next iteration
+ if x > 10 { continue; } // skip to the next iteration
print(x);
- if x == 42 { break; } // break out of for loop
+ if x == 42 { break; } // break out of for loop
}
// The 'range' function also takes a step
-for x in range(0, 50, 3) { // step by 3
- if x > 10 { continue; } // skip to the next iteration
+for x in range(0, 50, 3) { // step by 3
+ if x > 10 { continue; } // skip to the next iteration
print(x);
- if x == 42 { break; } // break out of for loop
+ if x == 42 { break; } // break out of for loop
}
// Iterate through object map
let map = #{a:1, b:3, c:5, d:7, e:9};
-// Property names are returned in random order
+// Property names are returned in unsorted, random order
for x in keys(map) {
- if x > 10 { continue; } // skip to the next iteration
+ if x > 10 { continue; } // skip to the next iteration
print(x);
- if x == 42 { break; } // break out of for loop
+ if x == 42 { break; } // break out of for loop
}
-// Property values are returned in random order
+// Property values are returned in unsorted, random order
for val in values(map) {
print(val);
}
diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md
index d2cb48d5..30604a53 100644
--- a/doc/src/language/functions.md
+++ b/doc/src/language/functions.md
@@ -18,6 +18,7 @@ print(add(2, 3)); // prints 5
print(sub(2, 3,)); // prints -1 - trailing comma in arguments list is OK
```
+
Implicit Return
---------------
@@ -38,6 +39,7 @@ print(add(2, 3)); // prints 5
print(add2(42)); // prints 44
```
+
No Access to External Scope
--------------------------
@@ -50,13 +52,15 @@ let x = 42;
fn foo() { x } // <- syntax error: variable 'x' doesn't exist
```
-Passing Arguments by Value
--------------------------
+
+Arguments Passed by Value
+------------------------
Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
-It is important to remember that all arguments are passed by _value_, so all functions are _pure_
-(i.e. they never modify their arguments).
+Therefore, functions with the same name and same _number_ of parameters are equivalent.
+It is important to remember that all arguments are passed by _value_, so all Rhai script-defined functions
+are _pure_ (i.e. they never modify their arguments).
Any update to an argument will **not** be reflected back to the caller.
This can introduce subtle bugs, if not careful, especially when using the _method-call_ style.
@@ -71,6 +75,7 @@ x.change(); // de-sugars to 'change(x)'
x == 500; // 'x' is NOT changed!
```
+
Global Definitions Only
----------------------
@@ -92,6 +97,12 @@ fn do_addition(x) {
}
```
-Unlike C/C++, functions can be defined _anywhere_ within the global level. A function does not need to be defined
-prior to being used in a script; a statement in the script can freely call a function defined afterwards.
-This is similar to Rust and many other modern languages.
+
+Use Before Definition
+--------------------
+
+Unlike C/C++, functions in Rhai can be defined _anywhere_ at global level.
+A function does not need to be defined prior to being used in a script;
+a statement in the script can freely call a function defined afterwards.
+
+This is similar to Rust and many other modern languages, such as JS's `function` keyword.
diff --git a/doc/src/language/if.md b/doc/src/language/if.md
index a71cbe9a..3958a2e3 100644
--- a/doc/src/language/if.md
+++ b/doc/src/language/if.md
@@ -3,24 +3,30 @@
{{#include ../links.md}}
+`if` statements follow C syntax:
+
```rust
if foo(x) {
print("It's true!");
} else if bar == baz {
print("It's true again!");
-} else if ... {
- :
-} else if ... {
- :
+} else if baz.is_foo() {
+ print("Yet again true.");
+} else if foo(bar - baz) {
+ print("True again... this is getting boring.");
} else {
print("It's finally false!");
}
```
-All branches of an `if` statement must be enclosed within braces '`{`' .. '`}`', even when there is only one statement.
-Like Rust, there is no ambiguity regarding which `if` clause a statement belongs to.
+Unlike C, the condition expression does _not_ need to be enclosed in parentheses '`(`' .. '`)`', but
+all branches of the `if` statement must be enclosed within braces '`{`' .. '`}`',
+even when there is only one statement inside the branch.
+
+Like Rust, there is no ambiguity regarding which `if` clause a branch belongs to.
```rust
+// Rhai is not C!
if (decision) print("I've decided!");
// ^ syntax error, expecting '{' in statement block
```
diff --git a/doc/src/language/loop.md b/doc/src/language/loop.md
index 8b1cba8e..299ca3ff 100644
--- a/doc/src/language/loop.md
+++ b/doc/src/language/loop.md
@@ -3,6 +3,11 @@ Infinite `loop`
{{#include ../links.md}}
+Infinite loops follow C syntax.
+
+Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;
+`break` can be used to break out of the loop unconditionally.
+
```rust
let x = 10;
@@ -13,3 +18,6 @@ loop {
if x == 0 { break; } // break out of loop
}
```
+
+Beware: a `loop` statement without a `break` statement inside its loop block is infinite -
+there is no way for the loop to stop iterating.
diff --git a/doc/src/language/method.md b/doc/src/language/method.md
index 23822e05..a9cdc1c7 100644
--- a/doc/src/language/method.md
+++ b/doc/src/language/method.md
@@ -3,11 +3,16 @@ Call Method as Function
{{#include ../links.md}}
-Properties and methods in a Rust custom type registered with the [`Engine`] can be called just like a regular function in Rust.
+Property getters/setters and methods 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.
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).
+However, sometimes it is not as straight-forward, and methods called in function-call style may end up
+not muting the object - see the example below. Therefore, it is best to always use method-call style.
+
Custom types, properties and methods can be disabled via the [`no_object`] feature.
```rust
@@ -21,8 +26,8 @@ update(a); // <- this de-sugars to 'a.update()' thus if 'a' is a simple
let array = [ a ];
update(array[0]); // <- 'array[0]' is an expression returning a calculated value,
- // a transient (i.e. a copy) so this statement has no effect
+ // a transient (i.e. a copy), so this statement has no effect
// except waste a lot of time cloning
-array[0].update(); // <- call this method-call style will update 'a'
+array[0].update(); // <- call in method-call style will update 'a'
```
diff --git a/doc/src/language/modules.md b/doc/src/language/modules.md
index 6b616103..a55ecc05 100644
--- a/doc/src/language/modules.md
+++ b/doc/src/language/modules.md
@@ -1,18 +1 @@
-Modules
-=======
-
-{{#include ../links.md}}
-
-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.
+# Modules
diff --git a/doc/src/language/modules/imp-resolver.md b/doc/src/language/modules/imp-resolver.md
index 15de7253..a36f930b 100644
--- a/doc/src/language/modules/imp-resolver.md
+++ b/doc/src/language/modules/imp-resolver.md
@@ -7,8 +7,8 @@ For many applications in which Rhai is embedded, it is necessary to customize th
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`.
+A module resolver must implement the trait [`rhai::ModuleResolver`]({{rootUrl}}/rust/traits.md),
+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
diff --git a/doc/src/language/modules/import.md b/doc/src/language/modules/import.md
index 536e0745..4ede1f52 100644
--- a/doc/src/language/modules/import.md
+++ b/doc/src/language/modules/import.md
@@ -3,6 +3,9 @@ Import a Module
{{#include ../../links.md}}
+`import` Statement
+-----------------
+
A module can be _imported_ via the `import` statement, and its members are accessed via '`::`' similar to C++.
```rust
@@ -17,10 +20,14 @@ print(lock::status); // module variables are constants
lock::status = "off"; // <- runtime error - cannot modify a constant
```
+
+Scoped Imports
+--------------
+
`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
+group at the beginning of a script. It is 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
@@ -44,3 +51,32 @@ for x in range(0, 1000) {
c.encrypt(something);
}
```
+
+
+Recursive Imports
+----------------
+
+Beware of _import cycles_ - i.e. recursively loading the same module. This is a sure-fire way to
+cause a stack overflow in the [`Engine`], unless stopped by setting a limit for [maximum number of modules].
+
+For instance, importing itself always causes an infinite recursion:
+
+```rust
+// This file is 'hello.rhai'
+
+import "hello" as foo; // import itself - infinite recursion!
+
+foo::do_something();
+```
+
+Modules cross-referencing also cause infinite recursion:
+
+```rust
+// This file is 'hello.rhai' - references 'world.rhai'
+import "world" as foo;
+foo::do_something();
+
+// This file is 'world.rhai' - references 'hello.rhai'
+import "hello" as bar;
+bar::do_something_else();
+```
diff --git a/doc/src/language/modules/index.md b/doc/src/language/modules/index.md
new file mode 100644
index 00000000..cac54f3d
--- /dev/null
+++ b/doc/src/language/modules/index.md
@@ -0,0 +1,18 @@
+Modules
+=======
+
+{{#include ../../links.md}}
+
+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.
diff --git a/doc/src/language/return.md b/doc/src/language/return.md
index 5e696161..41c063f9 100644
--- a/doc/src/language/return.md
+++ b/doc/src/language/return.md
@@ -3,8 +3,14 @@ Return Values
{{#include ../links.md}}
+The `return` statement is used to immediately stop evaluation and exist the current context
+(typically a function call) yielding a _return value_.
+
```rust
return; // equivalent to return ();
return 123 + 456; // returns 579
```
+
+A `return` statement at _global_ level stop the entire script evaluation,
+the return value is taken as the result of the script evaluation.
diff --git a/doc/src/language/statements.md b/doc/src/language/statements.md
index c964d952..450011c2 100644
--- a/doc/src/language/statements.md
+++ b/doc/src/language/statements.md
@@ -3,13 +3,14 @@ Statements
{{#include ../links.md}}
+Terminated by '`;`'
+------------------
+
Statements are terminated by semicolons '`;`' and they are mandatory,
except for the _last_ statement in a _block_ (enclosed by '`{`' .. '`}`' pairs) where it can be omitted.
-A statement can be used anywhere where an expression is expected. These are called, for lack of a more
-creative name, "statement expressions." The _last_ statement of a statement block is _always_ the block's
-return value when used as a statement.
-If the last statement has no return value (e.g. variable definitions, assignments) then it is assumed to be [`()`].
+Semicolons can also be omitted if the statement contains a block itself
+(e.g. the `if`, `while`, `for` and `loop` statements).
```rust
let a = 42; // normal assignment statement
@@ -20,6 +21,20 @@ let a = { 40 + 2 }; // 'a' is set to the value of the statement block, which
// ^ the last statement does not require a terminating semicolon (although it also works with it)
// ^ semicolon required here to terminate the assignment statement; it is a syntax error without it
-4 * 10 + 2 // a statement which is just one expression; no ending semicolon is OK
+if foo { a = 42 }
+// ^ there is no need to terminate an if-statement with a semicolon
+
+4 * 10 + 2 // a statement which is just one expression - no ending semicolon is OK
// because it is the last statement of the whole block
```
+
+
+Statement Expression
+--------------------
+
+A statement can be used anywhere where an expression is expected. These are called, for lack of a more
+creative name, "statement expressions."
+
+The _last_ statement of a statement block is _always_ the block's return value when used as a statement.
+
+If the last statement has no return value (e.g. variable definitions, assignments) then it is assumed to be [`()`].
diff --git a/doc/src/language/variables.md b/doc/src/language/variables.md
index 94f0500b..581aa9c4 100644
--- a/doc/src/language/variables.md
+++ b/doc/src/language/variables.md
@@ -3,6 +3,9 @@ Variables
{{#include ../links.md}}
+Valid Names
+-----------
+
Variables in Rhai follow normal C naming rules (i.e. must contain only ASCII letters, digits and underscores '`_`').
Variable names must start with an ASCII letter or an underscore '`_`', must contain at least one ASCII letter,
@@ -11,9 +14,21 @@ and must start with an ASCII letter before a digit.
Therefore, names like '`_`', '`_42`', '`3a`' etc. are not legal variable names, but '`_c3po`' and '`r2d2`' are.
Variable names are also case _sensitive_.
-Variables are defined using the `let` keyword. A variable defined within a statement block is _local_ to that block.
+Variable names cannot be the same as a [keyword].
+
+
+Declare a Variable
+------------------
+
+Variables are declared using the `let` keyword.
+
+Variables do not have to be given an initial value.
+If none is provided, then it defaults to [`()`].
+
+A variable defined within a statement block is _local_ to that block.
```rust
+let x; // ok - value is '()'
let x = 3; // ok
let _x = 42; // ok
let x_ = 42; // also ok
diff --git a/doc/src/language/while.md b/doc/src/language/while.md
index f0f419fb..e912175e 100644
--- a/doc/src/language/while.md
+++ b/doc/src/language/while.md
@@ -3,6 +3,11 @@
{{#include ../links.md}}
+`while` loops follow C syntax.
+
+Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;
+`break` can be used to break out of the loop unconditionally.
+
```rust
let x = 10;
diff --git a/doc/src/links.md b/doc/src/links.md
index cc4dd9f0..ba4e57af 100644
--- a/doc/src/links.md
+++ b/doc/src/links.md
@@ -23,8 +23,8 @@
[`eval_expression_with_scope`]: {{rootUrl}}/engine/expressions.md
[raw `Engine`]: {{rootUrl}}/engine/raw.md
[built-in operators]: {{rootUrl}}/engine/raw.md#built-in-operators
-[package]: {{rootUrl}}/rust/packages.md
-[packages]: {{rootUrl}}/rust/packages.md
+[package]: {{rootUrl}}/rust/packages/index.md
+[packages]: {{rootUrl}}/rust/packages/index.md
[`Scope`]: {{rootUrl}}/rust/scope.md
[`type_of()`]: {{rootUrl}}/language/type-of.md
@@ -41,6 +41,9 @@
[`print`]: {{rootUrl}}/language/print-debug.md
[`debug`]: {{rootUrl}}/language/print-debug.md
+[keywords]: {{rootUrl}}/appendix/keywords.md
+[keyword]: {{rootUrl}}/appendix/keywords.md
+
[variable]: {{rootUrl}}/language/variables.md
[variables]: {{rootUrl}}/language/variables.md
@@ -63,9 +66,9 @@
[function]: {{rootUrl}}/language/functions.md
[functions]: {{rootUrl}}/language/functions.md
-[`Module`]: {{rootUrl}}/language/modules.md
-[module]: {{rootUrl}}/language/modules.md
-[modules]: {{rootUrl}}/language/modules.md
+[`Module`]: {{rootUrl}}/language/modules/index.md
+[module]: {{rootUrl}}/language/modules/index.md
+[modules]: {{rootUrl}}/language/modules/index.md
[`export`]: {{rootUrl}}/language/modules/export.md
[`import`]: {{rootUrl}}/language/modules/import.md
@@ -80,7 +83,7 @@
[maximum size of object maps]: {{rootUrl}}/safety/max-map-size.md
[progress]:/safety/progress.md
-[script optimization]: {{rootUrl}}/engine/optimize.md
+[script optimization]: {{rootUrl}}/engine/optimize/index.md
[`OptimizationLevel::Full`]: {{rootUrl}}/engine/optimize/optimize-levels.md
[`OptimizationLevel::Simple`]: {{rootUrl}}/engine/optimize/optimize-levels.md
[`OptimizationLevel::None`]: {{rootUrl}}/engine/optimize/optimize-levels.md
diff --git a/doc/src/rust/packages.md b/doc/src/rust/packages/index.md
similarity index 98%
rename from doc/src/rust/packages.md
rename to doc/src/rust/packages/index.md
index 6f8c5b6c..0b41656f 100644
--- a/doc/src/rust/packages.md
+++ b/doc/src/rust/packages/index.md
@@ -1,7 +1,7 @@
Packages
========
-{{#include ../links.md}}
+{{#include ../../links.md}}
Standard built-in Rhai features are provided in various _packages_ that can be loaded via a call to `Engine::load_package`.
diff --git a/doc/src/safety.md b/doc/src/safety.md
index d207fe9d..1140c921 100644
--- a/doc/src/safety.md
+++ b/doc/src/safety.md
@@ -1,31 +1 @@
-Safety and Protection Against DoS Attacks
-========================================
-
-{{#include links.md}}
-
-For scripting systems open to untrusted user-land scripts, it is always best to limit the amount of
-resources used by a script so that it does not consume more resources that it is allowed to.
-
-The most important resources to watch out for are:
-
-* **Memory**: A malicous script may continuously grow a [string], an [array] or [object map] until all memory is consumed.
- It may also create a large [array] or [object map] literal that exhausts all memory during parsing.
-
-* **CPU**: A malicous script may run an infinite tight loop that consumes all CPU cycles.
-
-* **Time**: A malicous script may run indefinitely, thereby blocking the calling system which is waiting for a result.
-
-* **Stack**: A malicous script may attempt an infinite recursive call that exhausts the call stack.
- Alternatively, it may create a degenerated deep expression with so many levels that the parser exhausts the call stack
- when parsing the expression; or even deeply-nested statement blocks, if nested deep enough.
-
-* **Overflows**: A malicous script may deliberately cause numeric over-flows and/or under-flows, divide by zero, and/or
- create bad floating-point representations, in order to crash the system.
-
-* **Files**: A malicous script may continuously [`import`] an external module within an infinite loop,
- thereby putting heavy load on the file-system (or even the network if the file is not local).
- Furthermore, the module script may simply [`import`] itself in an infinite recursion.
- Even when modules are not created from files, they still typically consume a lot of resources to load.
-
-* **Data**: A malicous script may attempt to read from and/or write to data that it does not own. If this happens,
- it is a severe security breach and may put the entire system at risk.
+# Safety and Protection
diff --git a/doc/src/safety/index.md b/doc/src/safety/index.md
new file mode 100644
index 00000000..d49768e4
--- /dev/null
+++ b/doc/src/safety/index.md
@@ -0,0 +1,35 @@
+Safety and Protection Against DoS Attacks
+========================================
+
+{{#include ../links.md}}
+
+For scripting systems open to untrusted user-land scripts, it is always best to limit the amount of
+resources used by a script so that it does not consume more resources that it is allowed to.
+
+The most important resources to watch out for are:
+
+* **Memory**: A malicous script may continuously grow a [string], an [array] or [object map] until all memory is consumed.
+
+ It may also create a large [array] or [object map] literal that exhausts all memory during parsing.
+
+* **CPU**: A malicous script may run an infinite tight loop that consumes all CPU cycles.
+
+* **Time**: A malicous script may run indefinitely, thereby blocking the calling system which is waiting for a result.
+
+* **Stack**: A malicous script may attempt an infinite recursive call that exhausts the call stack.
+
+ Alternatively, it may create a degenerated deep expression with so many levels that the parser exhausts the call stack
+ when parsing the expression; or even deeply-nested statement blocks, if nested deep enough.
+
+ Another way to cause a stack overflow is to load a [self-referencing module]({{rootUrl}}/language/modules/import.md).
+
+* **Overflows**: A malicous script may deliberately cause numeric over-flows and/or under-flows, divide by zero, and/or
+ create bad floating-point representations, in order to crash the system.
+
+* **Files**: A malicous script may continuously [`import`] an external module within an infinite loop,
+ thereby putting heavy load on the file-system (or even the network if the file is not local).
+
+ Even when modules are not created from files, they still typically consume a lot of resources to load.
+
+* **Data**: A malicous script may attempt to read from and/or write to data that it does not own. If this happens,
+ it is a severe security breach and may put the entire system at risk.
diff --git a/doc/src/safety/max-modules.md b/doc/src/safety/max-modules.md
index d6f2f8da..d707ee64 100644
--- a/doc/src/safety/max-modules.md
+++ b/doc/src/safety/max-modules.md
@@ -10,6 +10,9 @@ of modules to zero does _not_ indicate unlimited modules, but disallows loading
A script attempting to load more than the maximum number of modules will terminate with an error result.
+This limit can also be used to stop [`import`-loops]({{rootUrl}}/language/modules/import.md)
+(i.e. cycles of modules referring to each other).
+
This check can be disabled via the [`unchecked`] feature for higher performance
(but higher risks as well).
diff --git a/doc/src/start/builds.md b/doc/src/start/builds/index.md
similarity index 86%
rename from doc/src/start/builds.md
rename to doc/src/start/builds/index.md
index ac2023d5..a3cdfbf3 100644
--- a/doc/src/start/builds.md
+++ b/doc/src/start/builds/index.md
@@ -1,7 +1,7 @@
Special Builds
==============
-{{#include ../links.md}}
+{{#include ../../links.md}}
It is possible to mix-and-match various [features] of the Rhai crate to make
specialized builds with specific characteristics and behaviors.
diff --git a/doc/src/start/examples.md b/doc/src/start/examples/index.md
similarity index 88%
rename from doc/src/start/examples.md
rename to doc/src/start/examples/index.md
index b7f9ae1d..6af1495b 100644
--- a/doc/src/start/examples.md
+++ b/doc/src/start/examples/index.md
@@ -1,7 +1,7 @@
Examples
========
-{{#include ../links.md}}
+{{#include ../../links.md}}
Rhai comes with a number of examples showing how to integrate the scripting [`Engine`] within
a Rust application, as well as a number of sample scripts that showcase different Rhai language features.
diff --git a/doc/src/start/features.md b/doc/src/start/features.md
index fca919a5..1f67fb28 100644
--- a/doc/src/start/features.md
+++ b/doc/src/start/features.md
@@ -4,10 +4,12 @@ Optional Features
{{#include ../links.md}}
By default, Rhai includes all the standard functionalities in a small, tight package.
-Most features are here to opt-**out** of certain functionalities that are not needed.
-Excluding unneeded functionalities can result in smaller, faster builds
-as well as more control over what a script can (or cannot) do.
+Most features are here to opt-**out** of certain functionalities that are not needed.
+Notice that this deviates from Rust norm where features are _additive_.
+
+Excluding unneeded functionalities can result in smaller, faster builds as well as
+more control over what a script can (or cannot) do.
| Feature | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -38,7 +40,7 @@ The `Cargo.toml` configuration below turns on these six features:
```toml
[dependencies]
-rhai = { version = "0.15.2", features = [ "sync", "unchecked", "only_i32", "no_float", "no_module", "no_function" ] }
+rhai = { version = "{{version}}", features = [ "sync", "unchecked", "only_i32", "no_float", "no_module", "no_function" ] }
```
The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32` or `i16`),
diff --git a/doc/src/start/install.md b/doc/src/start/install.md
index c3553298..b8ee54dc 100644
--- a/doc/src/start/install.md
+++ b/doc/src/start/install.md
@@ -3,15 +3,15 @@ Install the Rhai Crate
{{#include ../links.md}}
-Install the Rhai crate from [`crates.io`](https:/crates.io/crates/rhai/) by adding this line
-under `dependencies` in `Cargo.toml`:
+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`:
```toml
[dependencies]
-rhai = "0.15.2"
+rhai = "{{version}}" # assuming {{version}} is the latest version
```
-Use the latest released crate version on [`crates.io`](https:/crates.io/crates/rhai/):
+Or to automatically use the latest released crate version on [`crates.io`](https:/crates.io/crates/rhai/):
```toml
[dependencies]
diff --git a/tests/side_effects.rs b/tests/side_effects.rs
index fb364747..a5725b08 100644
--- a/tests/side_effects.rs
+++ b/tests/side_effects.rs
@@ -44,13 +44,13 @@ fn test_side_effects_command() -> Result<(), Box> {
let mut engine = Engine::new();
let mut scope = Scope::new();
- // Create the command object with initial state, handled by an `Rc`.
+ // Create the command object with initial state, handled by an `Arc`.
let command = Arc::new(Mutex::new(Command { state: 12 }));
assert_eq!(command.lock().unwrap().get(), 12);
// Create the wrapper.
let wrapper = CommandWrapper {
- command: command.clone(), // Notice this clones the `Rc` only
+ command: command.clone(), // Notice this clones the `Arc` only
};
// Make the wrapper a singleton in the script environment.