diff --git a/Cargo.toml b/Cargo.toml
index 97512851..2ffbd617 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rhai"
-version = "0.15.2"
+version = "0.16.0"
edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
description = "Embedded scripting for Rust"
@@ -33,6 +33,7 @@ no_index = [] # no arrays and indexing
no_object = [] # no custom objects
no_function = [] # no script-defined functions
no_module = [] # no modules
+internals = [] # expose internal data structures
# compiling for no-std
no_std = [ "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ]
diff --git a/README.md b/README.md
index b4f0853d..952d9bf3 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Supported targets and builds
Features
--------
-* Easy-to-use language similar to JS+Rust with dynamic typing.
+* Easy-to-use language similar to JavaScript+Rust with dynamic typing.
* Tight integration with native Rust [functions](https://schungx.github.io/rhai/rust/functions.html) and [types]([#custom-types-and-methods](https://schungx.github.io/rhai/rust/custom.html)), including [getters/setters](https://schungx.github.io/rhai/rust/getters-setters.html), [methods](https://schungx.github.io/rhai/rust/custom.html) and [indexers](https://schungx.github.io/rhai/rust/indexers.html).
* Freely pass Rust variables/constants into a script via an external [`Scope`](https://schungx.github.io/rhai/rust/scope.html).
* Easily [call a script-defined function](https://schungx.github.io/rhai/engine/call-fn.html) from Rust.
@@ -31,10 +31,12 @@ Features
one single source file, all with names starting with `"unsafe_"`).
* Re-entrant scripting engine can be made `Send + Sync` (via the [`sync`] feature).
* Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless explicitly permitted (e.g. via a `RefCell`).
-* Rugged - protection 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.
+* 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.
* [Function overloading](https://schungx.github.io/rhai/language/overload.html).
* [Operator overloading](https://schungx.github.io/rhai/rust/operators.html).
+* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html).
+* Some support for [object-oriented programming (OOP)](https://schungx.github.io/rhai/language/oop.html).
* Organize code base with dynamically-loadable [modules](https://schungx.github.io/rhai/language/modules.html).
* Scripts are [optimized](https://schungx.github.io/rhai/engine/optimize.html) (useful for template-based machine-generated scripts) for repeated evaluations.
* Support for [minimal builds](https://schungx.github.io/rhai/start/builds/minimal.html) by excluding unneeded language [features](https://schungx.github.io/rhai/start/features.html).
@@ -42,4 +44,10 @@ 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.
+
+Playground
+----------
+
+An [Online Playground](https://alvinhochun.github.io/rhai-demo/) is available with syntax-highlighting editor.
+Scripts can be evaluated directly from the editor.
diff --git a/RELEASES.md b/RELEASES.md
index 33c4418a..6f38bb72 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,18 +1,34 @@
Rhai Release Notes
==================
-Version 0.15.2
+Version 0.16.0
==============
+The major new feature in this version is OOP - well, poor man's OOP, that is.
+
Breaking changes
----------------
* The trait function `ModuleResolver::resolve` no longer takes a `Scope` as argument.
+* Functions defined in script now differentiates between using method-call style and normal function-call style.
+ The method-call style will bind the object to the `this` parameter instead of consuming the first parameter.
+* Imported modules are no longer stored in the `Scope`. `Scope::push_module` is removed.
+ Therefore, cannot rely on module imports to persist across invocations using a `Scope`.
+* `AST::retain_functions` is used for another purpose. The old `AST::retain_functions` is renamed to `AST::clear_statements`.
+
+New features
+------------
+
+* Support for _function pointers_ via `Fn(name)` and `Fn.call(...)` syntax - a poor man's first-class function.
+* Support for calling script-defined functions in method-call style with `this` binding to the object.
+* Special support in object maps for OOP.
+* Expanded the `AST` API for fine-tuned manipulation of functions.
Enhancements
------------
* [The Rhai Book](https://schungx.github.io/rhai) is online. Most content in the original `README` was transferred to the Book.
+* New feature `internals` to expose internal data structures (e.g. the AST nodes).
Version 0.15.1
diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md
index da62423b..f9491dd3 100644
--- a/doc/src/SUMMARY.md
+++ b/doc/src/SUMMARY.md
@@ -1,36 +1,37 @@
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.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. [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.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)
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,9 +41,9 @@ 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)
+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)
@@ -56,6 +57,7 @@ The Rhai Scripting Language
5. [Arrays](language/arrays.md)
6. [Object Maps](language/object-maps.md)
1. [Parse from JSON](language/json.md)
+ 2. [Special Support for OOP](language/object-maps-oop.md)
7. [Time-Stamps](language/timestamps.md)
3. [Keywords](language/keywords.md)
4. [Statements](language/statements.md)
@@ -69,17 +71,19 @@ 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)
+ 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.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)
+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)
@@ -90,12 +94,17 @@ 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)
- 1. [Script Optimization](engine/optimize.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)
2. [Re-Optimize an AST](engine/optimize/reoptimize.md)
3. [Eager Function Evaluation](engine/optimize/eager.md)
4. [Side-Effect Considerations](engine/optimize/side-effects.md)
5. [Volatility Considerations](engine/optimize/volatility.md)
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
- 2. [Eval Statement](language/eval.md)
+ 3. [Eval Statement](language/eval.md)
+9. [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/features.md b/doc/src/about/features.md
index 16910c63..c012353a 100644
--- a/doc/src/about/features.md
+++ b/doc/src/about/features.md
@@ -6,9 +6,9 @@ Features
Easy
----
-* Easy-to-use language similar to JS+Rust with dynamic typing.
+* Easy-to-use language similar to JavaScript+Rust with dynamic typing.
-* Tight integration with native Rust [functions]({{rootUrl}}/rust/functions.md) and [types]({{rootUrl}}/rust/custom.md), including [getters/setters]({{rootUrl}}/rust/getters-setters.md), [methods]({{rootUrl}}/rust/custom.md) 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`].
@@ -24,7 +24,7 @@ Fast
* Fairly efficient evaluation (1 million iterations in 0.25 sec on a single core, 2.3 GHz Linux VM).
-* Scripts are [optimized]({{rootUrl}}/engine/optimize.md) (useful for template-based machine-generated scripts) for repeated evaluations.
+* Scripts are [optimized][script optimization] (useful for template-based machine-generated scripts) for repeated evaluations.
Dynamic
-------
@@ -35,6 +35,10 @@ Dynamic
* Organize code base with dynamically-loadable [modules].
+* Dynamic dispatch via [function pointers].
+
+* Some support for [object-oriented programming (OOP)][OOP].
+
Safe
----
@@ -46,7 +50,7 @@ Rugged
* Sand-boxed - the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless explicitly permitted (e.g. via a `RefCell`).
-* Protected against malicious attacks (such as [stack-overflow]({{rootUrl}}/safety/max-call-stack.md), [over-sized data]({{rootUrl}}/safety/max-string-size.md), and [runaway scripts]({{rootUrl}}/safety/max-operations.md) etc.) that may come from untrusted third-party user-land scripts.
+* Protected against malicious attacks (such as [stack-overflow][maximum call stack depth], [over-sized data][maximum length of strings], and [runaway scripts][maximum number of operations] etc.) that may come from untrusted third-party user-land scripts.
* Track script evaluation [progress] and manually terminate a script run.
diff --git a/doc/src/about.md b/doc/src/about/index.md
similarity index 86%
rename from doc/src/about.md
rename to doc/src/about/index.md
index 8a15d31f..85c55824 100644
--- a/doc/src/about.md
+++ b/doc/src/about/index.md
@@ -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.
diff --git a/doc/src/about/non-design.md b/doc/src/about/non-design.md
index 2b07946e..da3747df 100644
--- a/doc/src/about/non-design.md
+++ b/doc/src/about/non-design.md
@@ -11,10 +11,15 @@ It doesn't attempt to be a new language. For example:
* No traits... so it is also not Rust. Do your Rusty stuff in Rust.
* No structures/records - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_.
+
There is, however, a built-in [object map] type which is adequate for most uses.
+ It is possible to simulate [object-oriented programming (OOP)][OOP] by storing [function pointers]
+ in [object map] properties, turning them into _methods_.
* No first-class functions - Code your functions in Rust instead, and register them with Rhai.
+ There is, however, support for simple [function pointers] allowing runtime dispatch by function name.
+
* No garbage collection - this should be expected, so...
* No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
@@ -26,7 +31,7 @@ Due to this intended usage, Rhai deliberately keeps the language simple and smal
such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc.
Avoid the temptation to write full-fledge program logic entirely in Rhai - that use case is best fulfilled by
-more complete languages such as JS or Lua.
+more complete languages such as JavaScript or Lua.
Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call.
All your core functionalities should be in Rust.
diff --git a/doc/src/about/related.md b/doc/src/about/related.md
index b6d8d8df..defc29e4 100644
--- a/doc/src/about/related.md
+++ b/doc/src/about/related.md
@@ -5,8 +5,13 @@ 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
+
+* [Online Playground][playground] - Run scripts directly from editor
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..660a605c
--- /dev/null
+++ b/doc/src/appendix/keywords.md
@@ -0,0 +1,33 @@
+Keywords List
+=============
+
+{{#include ../links.md}}
+
+| Keyword | Description | Not available under |
+| :-------------------: | ---------------------------------------- | :-----------------: |
+| `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 | |
+| `import` | Import module | [`no_module`] |
+| `export` | Export variable | [`no_module`] |
+| `as` | Alias for variable export | [`no_module`] |
+| `private` | Mark function private | [`no_function`] |
+| `fn` (lower-case `f`) | Function definition | [`no_function`] |
+| `Fn` (capital `F`) | Function to create a [function pointer] | |
+| `call` | Call a [function pointer] | |
+| `this` | Reference to base object for method call | [`no_function`] |
+| `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..a308e951
--- /dev/null
+++ b/doc/src/appendix/literals.md
@@ -0,0 +1,16 @@
+Literals Syntax
+===============
+
+{{#include ../links.md}}
+
+| Type | Literal syntax |
+| :--------------------------------: | :------------------------------------------------------------------------------: |
+| `INT` | `42`, `-123`, `0`,
`0x????..` (hex), `0b????..` (binary), `0o????..` (octal) |
+| `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..56782b95
--- /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..6da8d6bb 100644
--- a/doc/src/context.json
+++ b/doc/src/context.json
@@ -1,4 +1,5 @@
{
+ "version": "0.16.0",
"rootUrl": "",
"rootUrlX": "/rhai"
}
\ No newline at end of file
diff --git a/doc/src/engine/compile.md b/doc/src/engine/compile.md
index 32e16bce..f4ee0e65 100644
--- a/doc/src/engine/compile.md
+++ b/doc/src/engine/compile.md
@@ -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
diff --git a/doc/src/engine.md b/doc/src/engine/index.md
similarity index 88%
rename from doc/src/engine.md
rename to doc/src/engine/index.md
index 5ed3389a..8bf58504 100644
--- a/doc/src/engine.md
+++ b/doc/src/engine/index.md
@@ -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.
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..e8292e4c 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/arrays.md b/doc/src/language/arrays.md
index 5730d862..3afc09b6 100644
--- a/doc/src/language/arrays.md
+++ b/doc/src/language/arrays.md
@@ -22,7 +22,7 @@ The maximum allowed size of an array can be controlled via `Engine::set_max_arra
Built-in Functions
-----------------
-The following methods (mostly defined in the [`BasicArrayPackage`]({{rootUrl}}/rust/packages.md) but excluded if using a [raw `Engine`]) operate on arrays:
+The following methods (mostly defined in the [`BasicArrayPackage`][packages] but excluded if using a [raw `Engine`]) operate on arrays:
| Function | Parameter(s) | Description |
| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
@@ -38,6 +38,7 @@ The following methods (mostly defined in the [`BasicArrayPackage`]({{rootUrl}}/r
| `clear` | _none_ | empties the array |
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
+
Examples
--------
diff --git a/doc/src/language/constants.md b/doc/src/language/constants.md
index 82811166..d88ecd4f 100644
--- a/doc/src/language/constants.md
+++ b/doc/src/language/constants.md
@@ -9,11 +9,14 @@ Constants follow the same naming rules as [variables].
```rust
const x = 42;
+
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/convert.md b/doc/src/language/convert.md
index a0cd7a83..0c2226eb 100644
--- a/doc/src/language/convert.md
+++ b/doc/src/language/convert.md
@@ -11,10 +11,14 @@ That's it; for other conversions, register custom conversion functions.
```rust
let x = 42;
+
let y = x * 100.0; // <- error: cannot multiply i64 with f64
+
let y = x.to_float() * 100.0; // works
+
let z = y.to_int() + x; // works
let c = 'X'; // character
+
print("c is '" + c + "' and its code is " + c.to_int()); // prints "c is 'X' and its code is 88"
```
diff --git a/doc/src/language/dynamic.md b/doc/src/language/dynamic.md
index e8c8a21b..9ebe7d6f 100644
--- a/doc/src/language/dynamic.md
+++ b/doc/src/language/dynamic.md
@@ -27,6 +27,8 @@ if type_of(mystery) == "i64" {
print("Hey, I got an array here!");
} else if type_of(mystery) == "map" {
print("Hey, I got an object map here!");
+} else if type_of(mystery) == "Fn" {
+ print("Hey, I got a function pointer here!");
} else if type_of(mystery) == "TestStruct" {
print("Hey, I got the TestStruct custom type here!");
} else {
diff --git a/doc/src/language/eval.md b/doc/src/language/eval.md
index f79810e5..a7e5b979 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, JavaScript, 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 with 'eval'
```
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:
@@ -82,15 +82,15 @@ engine.register_result_fn("eval", alt_eval);
`EvalPackage`
-------------
-There is even a package named [`EvalPackage`]({{rootUrl}}/rust/packages.md) which implements the disabling override:
+There is even a package named [`EvalPackage`][packages] which implements the disabling override:
```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/fn-namespaces.md b/doc/src/language/fn-namespaces.md
new file mode 100644
index 00000000..5bd8e16f
--- /dev/null
+++ b/doc/src/language/fn-namespaces.md
@@ -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!'
+```
diff --git a/doc/src/language/fn-ptr.md b/doc/src/language/fn-ptr.md
new file mode 100644
index 00000000..e4d9bc5d
--- /dev/null
+++ b/doc/src/language/fn-ptr.md
@@ -0,0 +1,136 @@
+Function Pointers
+=================
+
+{{#include ../links.md}}
+
+It is possible to store a _function pointer_ in a variable just like a normal value.
+In fact, internally a function pointer simply stores the _name_ of the function as a string.
+
+Call a function pointer using the `call` method, which needs to be called in method-call style.
+
+
+Built-in methods
+----------------
+
+The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if
+using a [raw `Engine`]) operate on [strings]:
+
+| Function | Parameter(s) | Description |
+| -------------------------- | ------------ | --------------------------------------------------------------------- |
+| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer |
+
+
+Examples
+--------
+
+```rust
+fn foo(x) { 41 + x }
+
+let func = Fn("foo"); // use the 'Fn' function to create a function pointer
+
+print(func); // prints 'Fn(foo)'
+
+let func = fn_name.Fn(); // <- error: 'Fn' cannot be called in method-call style
+
+func.type_of() == "Fn"; // type_of() as function pointer is 'Fn'
+
+func.name == "foo";
+
+func.call(1) == 42; // call a function pointer with the 'call' method
+
+foo(1) == 42; // <- the above de-sugars to this
+
+call(func, 1); //<- error: 'call (Fn, i64)' is not a registered function
+
+let len = Fn("len"); // 'Fn' also works with registered native Rust functions
+
+len.call("hello") == 5;
+
+let add = Fn("+"); // 'Fn' works with built-in operators also
+
+add.call(40, 2) == 42;
+
+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)'
+```
+
+
+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!
+```
+
+
+Dynamic Dispatch
+----------------
+
+The purpose of function pointers is to enable rudimentary _dynamic dispatch_, meaning to determine,
+at runtime, which function to call among a group.
+
+Although it is possible to simulate dynamic dispatch via a number and a large `if-then-else-if` statement,
+using function pointers significantly simplifies the code.
+
+```rust
+let x = some_calculation();
+
+// These are the functions to call depending on the value of 'x'
+fn method1(x) { ... }
+fn method2(x) { ... }
+fn method3(x) { ... }
+
+// Traditional - using decision variable
+let func = sign(x);
+
+// Dispatch with if-statement
+if func == -1 {
+ method1(42);
+} else if func == 0 {
+ method2(42);
+} else if func == 1 {
+ method3(42);
+}
+
+// Using pure function pointer
+let func = if x < 0 {
+ Fn("method1")
+} else if x == 0 {
+ Fn("method2")
+} else if x > 0 {
+ Fn("method3")
+}
+
+// Dynamic dispatch
+func.call(42);
+
+// Using functions map
+let map = [ Fn("method1"), Fn("method2"), Fn("method3") ];
+
+let func = sign(x) + 1;
+
+// Dynamic dispatch
+map[func].call(42);
+```
diff --git a/doc/src/language/for.md b/doc/src/language/for.md
index eb2406fc..6780a1cd 100644
--- a/doc/src/language/for.md
+++ b/doc/src/language/for.md
@@ -5,50 +5,63 @@
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..72dac0b4 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,26 +52,6 @@ let x = 42;
fn foo() { x } // <- syntax error: variable 'x' doesn't exist
```
-Passing Arguments 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).
-
-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.
-
-```rust
-fn change(s) { // 's' is passed by value
- s = 42; // only a COPY of 's' is changed
-}
-
-let x = 500;
-x.change(); // de-sugars to 'change(x)'
-x == 500; // 'x' is NOT changed!
-```
Global Definitions Only
----------------------
@@ -92,6 +74,56 @@ 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 JavaScript's `function` keyword.
+
+
+Arguments Passed by Value
+------------------------
+
+Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
+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.
+
+```rust
+fn change(s) { // 's' is passed by value
+ s = 42; // only a COPY of 's' is changed
+}
+
+let x = 500;
+
+change(x);
+
+x == 500; // 'x' is NOT changed!
+```
+
+
+`this` - Simulating an Object Method
+-----------------------------------
+
+Functions can also be called in method-call style. When this is the case, the keyword '`this`'
+binds to the object in the method call and can be changed.
+
+```rust
+fn change() { // not that the object does not need a parameter
+ this = 42; // 'this' binds to the object in method-call
+}
+
+let x = 500;
+
+x.change(); // call 'change' in method-call style, 'this' binds to 'x'
+
+x == 42; // 'x' is changed!
+
+change(); // <- error: `this` is unbounded
+```
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.md b/doc/src/language/index.md
similarity index 78%
rename from doc/src/language.md
rename to doc/src/language/index.md
index b3526ebd..ac4e4fb6 100644
--- a/doc/src/language.md
+++ b/doc/src/language/index.md
@@ -1,7 +1,7 @@
Rhai Language Reference
======================
-{{#include links.md}}
+{{#include ../links.md}}
This section outlines the Rhai language.
diff --git a/doc/src/language/keywords.md b/doc/src/language/keywords.md
index 48c32015..836e9b0c 100644
--- a/doc/src/language/keywords.md
+++ b/doc/src/language/keywords.md
@@ -13,8 +13,10 @@ The following are reserved keywords in Rhai:
| `while`, `loop`, `for`, `in`, `continue`, `break` | Looping | |
| `fn`, `private` | Functions | [`no_function`] |
| `return` | Return values | |
-| `throw` | Return errors | |
+| `throw` | throw exceptions | |
| `import`, `export`, `as` | Modules | [`no_module`] |
+| `Fn`, `call` | Function pointers | |
+| `type_of`, `print`, `debug`, `eval` | Special functions | |
-Keywords cannot be the name of a [function] or [variable], unless the relevant exclusive feature is enabled.
+Keywords cannot be the name of a [function] or [variable], unless the relevant feature is enabled.
For example, `fn` is a valid variable name under [`no_function`].
diff --git a/doc/src/language/logic.md b/doc/src/language/logic.md
index 7867a3d8..113641f6 100644
--- a/doc/src/language/logic.md
+++ b/doc/src/language/logic.md
@@ -13,8 +13,11 @@ set of types (see [built-in operators]).
```rust
42 == 42; // true
+
42 > 42; // false
+
"hello" > "foo"; // true
+
"42" == 42; // false
```
@@ -23,13 +26,17 @@ except for '`!=`' (not equals) which results in `true`. This is in line with int
```rust
42 == 42.0; // false - i64 cannot be compared with f64
+
42 != 42.0; // true - i64 cannot be compared with f64
42 > "42"; // false - i64 cannot be compared with string
+
42 <= "42"; // false - i64 cannot be compared with string
let ts = new_ts(); // custom type
+
ts == 42; // false - types cannot be compared
+
ts != 42; // true - types cannot be compared
```
@@ -50,25 +57,42 @@ if the first one already proves the condition wrong.
Single boolean operators `&` and `|` always evaluate both operands.
```rust
-this() || that(); // that() is not evaluated if this() is true
-this() && that(); // that() is not evaluated if this() is false
+a() || b(); // b() is not evaluated if a() is true
-this() | that(); // both this() and that() are evaluated
-this() & that(); // both this() and that() are evaluated
+a() && b(); // b() is not evaluated if a() is false
+
+a() | b(); // both a() and b() are evaluated
+
+a() & b(); // both a() and b() are evaluated
```
Compound Assignment Operators
----------------------------
```rust
-let number = 5;
-number += 4; // number = number + 4
-number -= 3; // number = number - 3
-number *= 2; // number = number * 2
-number /= 1; // number = number / 1
-number %= 3; // number = number % 3
+let number = 9;
+
+number += 8; // number = number + 8
+
+number -= 7; // number = number - 7
+
+number *= 6; // number = number * 6
+
+number /= 5; // number = number / 5
+
+number %= 4; // number = number % 4
+
+number ~= 3; // number = number ~ 3
+
number <<= 2; // number = number << 2
+
number >>= 1; // number = number >> 1
+
+number &= 0x00ff; // number = number & 0x00ff;
+
+number |= 0x00ff; // number = number | 0x00ff;
+
+number ^= 0x00ff; // number = number ^ 0x00ff;
```
The `+=` operator can also be used to build [strings]:
diff --git a/doc/src/language/loop.md b/doc/src/language/loop.md
index 8b1cba8e..b92c3f7e 100644
--- a/doc/src/language/loop.md
+++ b/doc/src/language/loop.md
@@ -3,13 +3,24 @@ 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;
loop {
x = x - 1;
+
if x > 5 { continue; } // skip to the next iteration
+
print(x);
+
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..bdce0ed6 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][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.
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/ast.md b/doc/src/language/modules/ast.md
index 96607881..4bf39ace 100644
--- a/doc/src/language/modules/ast.md
+++ b/doc/src/language/modules/ast.md
@@ -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).
diff --git a/doc/src/language/modules/export.md b/doc/src/language/modules/export.md
index 9ab2b9e3..dbb4ddca 100644
--- a/doc/src/language/modules/export.md
+++ b/doc/src/language/modules/export.md
@@ -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.
diff --git a/doc/src/language/modules/imp-resolver.md b/doc/src/language/modules/imp-resolver.md
index 15de7253..415abf72 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`][traits],
+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
@@ -43,18 +43,14 @@ impl ModuleResolver for MyModuleResolver {
}
}
-fn main() -> Result<(), Box> {
- let mut engine = Engine::new();
+let mut engine = Engine::new();
- // Set the custom module resolver into the 'Engine'.
- engine.set_module_resolver(Some(MyModuleResolver {}));
+// 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(())
-}
+engine.consume(r#"
+ import "hello" as foo; // this 'import' statement will call
+ // 'MyModuleResolver::resolve' with "hello" as path
+ foo:bar();
+"#)?;
```
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.md b/doc/src/language/modules/index.md
similarity index 96%
rename from doc/src/language/modules.md
rename to doc/src/language/modules/index.md
index 6b616103..cac54f3d 100644
--- a/doc/src/language/modules.md
+++ b/doc/src/language/modules/index.md
@@ -1,7 +1,7 @@
Modules
=======
-{{#include ../links.md}}
+{{#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.
diff --git a/doc/src/language/modules/resolvers.md b/doc/src/language/modules/resolvers.md
index 3377cb56..ed2bf54c 100644
--- a/doc/src/language/modules/resolvers.md
+++ b/doc/src/language/modules/resolvers.md
@@ -5,7 +5,7 @@ Module Resolvers
When encountering an [`import`] statement, Rhai attempts to _resolve_ the module based on the path string.
-_Module Resolvers_ are service types that implement the [`ModuleResolver`]({{rootUrl}}/rust/traits.md) trait.
+_Module Resolvers_ are service types that implement the [`ModuleResolver`][traits] trait.
There are a number of standard resolvers built into Rhai, the default being the `FileModuleResolver`
which simply loads a script file based on the path (with `.rhai` extension attached) and execute it to form a module.
diff --git a/doc/src/language/num-fn.md b/doc/src/language/num-fn.md
index c6df7900..a963c7e2 100644
--- a/doc/src/language/num-fn.md
+++ b/doc/src/language/num-fn.md
@@ -6,18 +6,19 @@ Numeric Functions
Integer Functions
----------------
-The following standard functions (defined in the [`BasicMathPackage`]({{rootUrl}}/rust/packages.md) but excluded if using a [raw `Engine`])
+The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`])
operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
-| Function | Description |
-| ------------ | --------------------------------- |
-| `abs` | absolute value |
-| [`to_float`] | converts an integer type to `f64` |
+| Function | Description |
+| ------------ | --------------------------------------------------------------- |
+| `abs` | absolute value |
+| `sign` | returns -1 if the number is negative, +1 if positive, 0 if zero |
+| [`to_float`] | converts an integer type to `f64` |
Floating-Point Functions
-----------------------
-The following standard functions (defined in the [`BasicMathPackage`]({{rootUrl}}/rust/packages.md) but excluded if using a [raw `Engine`])
+The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`])
operate on `f64` only:
| Category | Functions |
diff --git a/doc/src/language/num-op.md b/doc/src/language/num-op.md
index a7bd524d..fa96f1e9 100644
--- a/doc/src/language/num-op.md
+++ b/doc/src/language/num-op.md
@@ -22,19 +22,19 @@ number = -5 - +5;
Binary Operators
----------------
-| Operator | Description | Integers only |
-| -------- | ---------------------------------------------------- | :-----------: |
-| `+` | Plus | |
-| `-` | Minus | |
-| `*` | Multiply | |
-| `/` | Divide (integer division if acting on integer types) | |
-| `%` | Modulo (remainder) | |
-| `~` | Power | |
-| `&` | Binary _And_ bit-mask | Yes |
-| `|` | Binary _Or_ bit-mask | Yes |
-| `^` | Binary _Xor_ bit-mask | Yes |
-| `<<` | Left bit-shift | Yes |
-| `>>` | Right bit-shift | Yes |
+| Operator | Description | Integers only |
+| --------------- | ---------------------------------------------------- | :-----------: |
+| `+` | Plus | |
+| `-` | Minus | |
+| `*` | Multiply | |
+| `/` | Divide (integer division if acting on integer types) | |
+| `%` | Modulo (remainder) | |
+| `~` | Power | |
+| `&` | Bit-wise _And_ | Yes |
+| \|
| Bit-wise _Or_ | Yes |
+| `^` | Bit-wise _Xor_ | Yes |
+| `<<` | Left bit-shift | Yes |
+| `>>` | Right bit-shift | Yes |
```rust
let x = (1 + 2) * (6 - 4) / 2; // arithmetic, with parentheses
diff --git a/doc/src/language/object-maps-oop.md b/doc/src/language/object-maps-oop.md
new file mode 100644
index 00000000..4727871b
--- /dev/null
+++ b/doc/src/language/object-maps-oop.md
@@ -0,0 +1,36 @@
+Special Support for OOP via Object Maps
+======================================
+
+{{#include ../links.md}}
+
+[Object maps] can be used to simulate [object-oriented programming (OOP)][OOP] by storing data
+as properties and methods as properties holding [function pointers].
+
+If an [object map]'s property holds a [function pointer], the property can simply be called like
+a normal method in method-call syntax. This is a _short-hand_ to avoid the more verbose syntax
+of using the `call` function keyword.
+
+When a property holding a [function pointer] is called like a method, what happens next depends
+on whether the target function is a native Rust function or a script-defined function.
+
+If it is a registered native Rust method function, then it is called directly.
+
+If it is a script-defined function, the `this` variable within the function body is bound
+to the [object map] before the function is called. There is no way to simulate this behavior
+via a normal function-call syntax because all scripted function arguments are passed by value.
+
+```rust
+fn do_action(x) { print(this.data + x); } // 'this' binds to the object when called
+
+let obj = #{
+ data: 40,
+ action: Fn("do_action") // 'action' holds a function pointer to 'do_action'
+ };
+
+obj.action(2); // Short-hand syntax: prints 42
+
+// To achieve the above with normal function pointer calls:
+fn do_action(map, x) { print(map.data + x); }
+
+obj.action.call(obj, 2); // this call cannot mutate 'obj'
+```
diff --git a/doc/src/language/object-maps.md b/doc/src/language/object-maps.md
index 16d6b6f0..27220134 100644
--- a/doc/src/language/object-maps.md
+++ b/doc/src/language/object-maps.md
@@ -39,7 +39,7 @@ The index notation allows setting/getting properties of arbitrary names (even th
Built-in Functions
-----------------
-The following methods (defined in the [`BasicMapPackage`]({{rootUrl}}/rust/packages.md) but excluded if using a [raw `Engine`])
+The following methods (defined in the [`BasicMapPackage`][packages] but excluded if using a [raw `Engine`])
operate on object maps:
| Function | Parameter(s) | Description |
@@ -50,6 +50,7 @@ operate on object maps:
| `remove` | property name | removes a certain property and returns it ([`()`] if the property does not exist) |
| `+=` operator, `mixin` | second object map | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values) |
| `+` operator | first object map, second object map | merges the first object map with the second |
+| `fill_with` | second object map | adds in all properties of the second object map that do not exist in the object map |
| `keys` | _none_ | returns an [array] of all the property names (in random order), not available under [`no_index`] |
| `values` | _none_ | returns an [array] of all the property values (in random order), not available under [`no_index`] |
@@ -61,7 +62,7 @@ Examples
let y = #{ // object map literal with 3 properties
a: 1,
bar: "hello",
- "baz!$@": 123.456, // like JS, you can use any string as property names...
+ "baz!$@": 123.456, // like JavaScript, you can use any string as property names...
"": false, // even the empty string!
a: 42 // <- syntax error: duplicated property name
diff --git a/doc/src/language/oop.md b/doc/src/language/oop.md
new file mode 100644
index 00000000..120b42d0
--- /dev/null
+++ b/doc/src/language/oop.md
@@ -0,0 +1,44 @@
+Object-Oriented Programming (OOP)
+================================
+
+{{#include ../links.md}}
+
+Rhai does not have _objects_ per se, but it is possible to _simulate_ object-oriented programming.
+
+
+Use [Object Maps] to Simulate OOP
+--------------------------------
+
+Rhai's [object maps] has [special support for OOP]({{rootUrl}}/language/object-maps-oop.md).
+
+| Rhai concept | Maps to OOP |
+| ----------------------------------------------------- | :---------: |
+| [Object maps] | objects |
+| [Object map] properties holding values | properties |
+| [Object map] properties that hold [function pointers] | methods |
+
+
+Examples
+--------
+
+```rust
+// Define the object
+let obj = #{
+ data: 0,
+ increment: Fn("add"), // when called, 'this' binds to 'obj'
+ update: Fn("update"), // when called, 'this' binds to 'obj'
+ action: Fn("action") // when called, 'this' binds to 'obj'
+ };
+
+// Define functions
+fn add(x) { this.data += x; } // update using 'this'
+fn update(x) { this.data = x; } // update using 'this'
+fn action() { print(this.data); } // access properties of 'this'
+
+// Use the object
+obj.increment(1);
+obj.action(); // prints 1
+
+obj.update(42);
+obj.action(); // prints 42
+```
diff --git a/doc/src/language/overload.md b/doc/src/language/overload.md
index b83a8028..bb291ca3 100644
--- a/doc/src/language/overload.md
+++ b/doc/src/language/overload.md
@@ -3,20 +3,27 @@ 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.
```rust
fn foo(x,y,z) { print("Three!!! " + x + "," + y + "," + z) }
+
fn foo(x) { print("One! " + x) }
+
fn foo(x,y) { print("Two! " + x + "," + y) }
+
fn foo() { print("None.") }
+
fn foo(x) { print("HA! NEW ONE! " + x) } // overwrites previous definition
foo(1,2,3); // prints "Three!!! 1,2,3"
+
foo(42); // prints "HA! NEW ONE! 42"
+
foo(1,2); // prints "Two!! 1,2"
+
foo(); // prints "None."
```
diff --git a/doc/src/language/print-debug.md b/doc/src/language/print-debug.md
index bd031b4a..515b098f 100644
--- a/doc/src/language/print-debug.md
+++ b/doc/src/language/print-debug.md
@@ -7,8 +7,11 @@ The `print` and `debug` functions default to printing to `stdout`, with `debug`
```rust
print("hello"); // prints hello to stdout
+
print(1 + 2 + 3); // prints 6 to stdout
+
print("hello" + 42); // prints hello42 to stdout
+
debug("world!"); // prints "world!" to stdout using debug formatting
```
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/string-fn.md b/doc/src/language/string-fn.md
index 8504e2e3..504ea0a6 100644
--- a/doc/src/language/string-fn.md
+++ b/doc/src/language/string-fn.md
@@ -3,7 +3,7 @@ Built-in String Functions
{{#include ../links.md}}
-The following standard methods (mostly defined in the [`MoreStringPackage`]({{rootUrl}}/rust/packages.md) but excluded if
+The following standard methods (mostly defined in the [`MoreStringPackage`][packages] but excluded if
using a [raw `Engine`]) operate on [strings]:
| Function | Parameter(s) | Description |
diff --git a/doc/src/language/strings-chars.md b/doc/src/language/strings-chars.md
index f7019048..b6372240 100644
--- a/doc/src/language/strings-chars.md
+++ b/doc/src/language/strings-chars.md
@@ -6,7 +6,7 @@ Strings and Characters
String in Rhai contain any text sequence of valid Unicode characters.
Internally strings are stored in UTF-8 encoding.
-Strings can be built up from other strings and types via the `+` operator (provided by the [`MoreStringPackage`]({{rootUrl}}/rust/packages.md)
+Strings can be built up from other strings and types via the `+` operator (provided by the [`MoreStringPackage`][packages]
but excluded if using a [raw `Engine`]). This is particularly useful when printing output.
[`type_of()`] a string returns `"string"`.
diff --git a/doc/src/language/timestamps.md b/doc/src/language/timestamps.md
index 3d02797e..47ba302b 100644
--- a/doc/src/language/timestamps.md
+++ b/doc/src/language/timestamps.md
@@ -3,12 +3,12 @@
{{#include ../links.md}}
-Timestamps are provided by the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md) (excluded if using a [raw `Engine`])
+Timestamps are provided by the [`BasicTimePackage`][packages] (excluded if using a [raw `Engine`])
via the `timestamp` function.
Timestamps are not available under [`no_std`].
-The Rust type of a timestamp is `std::time::Instant` ([`instant::Instant`](https://crates.io/crates/instant) in [WASM] builds).
+The Rust type of a timestamp is `std::time::Instant` ([`instant::Instant`] in [WASM] builds).
[`type_of()`] a timestamp returns `"timestamp"`.
@@ -16,7 +16,7 @@ The Rust type of a timestamp is `std::time::Instant` ([`instant::Instant`](https
Built-in Functions
-----------------
-The following methods (defined in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md) but excluded if using a [raw `Engine`]) operate on timestamps:
+The following methods (defined in the [`BasicTimePackage`][packages] but excluded if using a [raw `Engine`]) operate on timestamps:
| Function | Parameter(s) | Description |
| ----------------------------- | ---------------------------------- | -------------------------------------------------------- |
diff --git a/doc/src/language/values-and-types.md b/doc/src/language/values-and-types.md
index 2aef02c3..70c276a9 100644
--- a/doc/src/language/values-and-types.md
+++ b/doc/src/language/values-and-types.md
@@ -5,20 +5,21 @@ Values and Types
The following primitive types are supported natively:
-| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
-| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | --------------------- |
-| **Integer number** | `u8`, `i8`, `u16`, `i16`,
`u32`, `i32` (default for [`only_i32`]),
`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` or `Arc`) | `"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)_ |
+| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
+| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | ----------------------- |
+| **Integer number** | `u8`, `i8`, `u16`, `i16`,
`u32`, `i32` (default for [`only_i32`]),
`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` or `Arc`) | `"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`][packages], disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`] if not [WASM] build) | `"timestamp"` | _not supported_ |
+| **[Function pointer]** | _None_ | `Fn` | `"Fn(foo)"` |
+| **[`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.
diff --git a/doc/src/language/variables.md b/doc/src/language/variables.md
index 94f0500b..be4abbce 100644
--- a/doc/src/language/variables.md
+++ b/doc/src/language/variables.md
@@ -3,17 +3,37 @@ Variables
{{#include ../links.md}}
-Variables in Rhai follow normal C naming rules (i.e. must contain only ASCII letters, digits and underscores '`_`').
+Valid Names
+-----------
-Variable names must start with an ASCII letter or an underscore '`_`', must contain at least one ASCII letter,
-and must start with an ASCII letter before a digit.
+Variables in Rhai follow normal C naming rules - must contain only ASCII letters, digits and underscores '`_`',
+and cannot start with a digit.
-Therefore, names like '`_`', '`_42`', '`3a`' etc. are not legal variable names, but '`_c3po`' and '`r2d2`' are.
-Variable names are also case _sensitive_.
+For example: '`_c3po`' and '`r2d2`' are valid variable names, but '`3abc`' is not.
-Variables are defined using the `let` keyword. A variable defined within a statement block is _local_ to that block.
+However, unlike Rust, a variable name must also contain at least one ASCII letter, and an ASCII letter must come before any digit.
+In other words, the first character that is not an underscore '`_`' must be an ASCII letter and not a digit.
+
+Therefore, some names acceptable to Rust, like '`_`', '`_42foo`', '`_1`' etc., are not valid in Rhai.
+This restriction is to reduce confusion because, for instance, '`_1`' can easily be misread (or mis-typed) as `-1`.
+
+Variable names are case _sensitive_.
+
+Variable names also 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, 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..b7147a50 100644
--- a/doc/src/links.md
+++ b/doc/src/links.md
@@ -10,21 +10,24 @@
[`no_function`]: {{rootUrl}}/start/features.md
[`no_module`]: {{rootUrl}}/start/features.md
[`no_std`]: {{rootUrl}}/start/features.md
-
[`no-std`]: {{rootUrl}}/start/features.md
+[`internals`]: {{rootUrl}}/start/features.md
[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
[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
@@ -37,10 +40,17 @@
[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
[`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
@@ -62,15 +72,22 @@
[function]: {{rootUrl}}/language/functions.md
[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.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
+[module resolver]: {{rootUrl}}/language/modules/imp-resolver.md
[`export`]: {{rootUrl}}/language/modules/export.md
[`import`]: {{rootUrl}}/language/modules/import.md
[`eval`]: {{rootUrl}}/language/eval.md
+[OOP]: {{rootUrl}}/language/oop.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
@@ -78,9 +95,9 @@
[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.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/builtin-packages.md b/doc/src/rust/builtin-packages.md
deleted file mode 100644
index f77cc900..00000000
--- a/doc/src/rust/builtin-packages.md
+++ /dev/null
@@ -1 +0,0 @@
-# Built-in Packages
diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md
index cd3998b6..b87f6409 100644
--- a/doc/src/rust/custom.md
+++ b/doc/src/rust/custom.md
@@ -26,21 +26,16 @@ impl TestStruct {
}
}
-fn main() -> Result<(), Box>
-{
- let engine = Engine::new();
+let mut engine = Engine::new();
- engine.register_type::();
+engine.register_type::();
- engine.register_fn("update", TestStruct::update);
- engine.register_fn("new_ts", TestStruct::new);
+engine.register_fn("update", TestStruct::update);
+engine.register_fn("new_ts", TestStruct::new);
- let result = engine.eval::("let x = new_ts(); x.update(); x")?;
+let result = engine.eval::("let x = new_ts(); x.update(); x")?;
- println!("result: {}", result.field); // prints 42
-
- Ok(())
-}
+println!("result: {}", result.field); // prints 42
```
Register a Custom Type
@@ -66,7 +61,7 @@ impl TestStruct {
}
}
-let engine = Engine::new();
+let mut engine = Engine::new();
engine.register_type::();
```
@@ -102,15 +97,17 @@ println!("result: {}", result.field); // prints 42
Method-Call Style vs. Function-Call Style
----------------------------------------
-In fact, any function with a first argument that is a `&mut` reference can be used as method calls because
-internally they are the same thing: methods on a type is implemented as a functions taking a `&mut` first argument.
+Any function with a first argument that is a `&mut` reference can be used
+as method calls because internally they are the same thing: methods on a type is
+implemented as a functions taking a `&mut` first argument.
+This design is similar to Rust.
```rust
fn foo(ts: &mut TestStruct) -> i64 {
ts.field
}
-engine.register_fn("foo", foo); // register ad hoc function with correct signature
+engine.register_fn("foo", foo); // register a Rust native function
let result = engine.eval::(
"let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x'
@@ -119,8 +116,8 @@ let result = engine.eval::(
println!("result: {}", result); // prints 1
```
-Under [`no_object`], however, the _method_ style of function calls (i.e. calling a function as an object-method)
-is no longer supported.
+Under [`no_object`], however, the _method_ style of function calls
+(i.e. calling a function as an object-method) is no longer supported.
```rust
// Below is a syntax error under 'no_object' because 'clear' cannot be called in method style.
diff --git a/doc/src/rust/fallible.md b/doc/src/rust/fallible.md
index 6c20bf94..16c16826 100644
--- a/doc/src/rust/fallible.md
+++ b/doc/src/rust/fallible.md
@@ -8,11 +8,6 @@ If a function is _fallible_ (i.e. it returns a `Result<_, Error>`), it can be re
The function must return `Result>`.
-`Box` implements `From<&str>` and `From` etc.
-and the error text gets converted into `Box`.
-
-The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely.
-
```rust
use rhai::{Engine, EvalAltResult, Position};
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
@@ -27,15 +22,20 @@ fn safe_divide(x: i64, y: i64) -> Result> {
}
}
-fn main()
-{
- let engine = Engine::new();
+let mut engine = Engine::new();
- // Fallible functions that return Result values must use register_result_fn()
- engine.register_result_fn("divide", safe_divide);
+// Fallible functions that return Result values must use register_result_fn()
+engine.register_result_fn("divide", safe_divide);
- if let Err(error) = engine.eval::("divide(40, 0)") {
- println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
- }
+if let Err(error) = engine.eval::("divide(40, 0)") {
+ println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
}
```
+
+Create a `Box`
+----------------------------
+
+`Box` implements `From<&str>` and `From` etc.
+and the error text gets converted into `Box`.
+
+The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely.
diff --git a/doc/src/rust/functions.md b/doc/src/rust/functions.md
index de60646f..aca6fe8e 100644
--- a/doc/src/rust/functions.md
+++ b/doc/src/rust/functions.md
@@ -29,30 +29,25 @@ fn get_any_value() -> Result> {
Ok((42_i64).into()) // standard types can use 'into()'
}
-fn main() -> Result<(), Box>
-{
- let engine = Engine::new();
+let mut engine = Engine::new();
- engine.register_fn("add", add_len);
- engine.register_fn("add_str", add_len_str);
+engine.register_fn("add", add_len);
+engine.register_fn("add_str", add_len_str);
- let result = engine.eval::(r#"add(40, "xx")"#)?;
+let result = engine.eval::(r#"add(40, "xx")"#)?;
- println!("Answer: {}", result); // prints 42
+println!("Answer: {}", result); // prints 42
- let result = engine.eval::(r#"add_str(40, "xx")"#)?;
+let result = engine.eval::(r#"add_str(40, "xx")"#)?;
- println!("Answer: {}", result); // prints 42
+println!("Answer: {}", result); // prints 42
- // Functions that return Dynamic values must use register_result_fn()
- engine.register_result_fn("get_any_value", get_any_value);
+// Functions that return Dynamic values must use register_result_fn()
+engine.register_result_fn("get_any_value", get_any_value);
- let result = engine.eval::("get_any_value()")?;
+let result = engine.eval::("get_any_value()")?;
- println!("Answer: {}", result); // prints 42
-
- Ok(())
-}
+println!("Answer: {}", result); // prints 42
```
To create a [`Dynamic`] value, use the `Dynamic::from` method.
diff --git a/doc/src/rust/generic.md b/doc/src/rust/generic.md
index 84527951..c68bf562 100644
--- a/doc/src/rust/generic.md
+++ b/doc/src/rust/generic.md
@@ -17,14 +17,11 @@ fn show_it(x: &mut T) {
println!("put up a good show: {}!", x)
}
-fn main()
-{
- let engine = Engine::new();
+let mut engine = Engine::new();
- engine.register_fn("print", show_it::);
- engine.register_fn("print", show_it::);
- engine.register_fn("print", show_it::);
-}
+engine.register_fn("print", show_it::);
+engine.register_fn("print", show_it::);
+engine.register_fn("print", show_it::);
```
The above example shows how to register multiple functions
diff --git a/doc/src/rust/getters-setters.md b/doc/src/rust/getters-setters.md
index 18cf868e..92c659b9 100644
--- a/doc/src/rust/getters-setters.md
+++ b/doc/src/rust/getters-setters.md
@@ -28,7 +28,7 @@ impl TestStruct {
}
}
-let engine = Engine::new();
+let mut engine = Engine::new();
engine.register_type::();
diff --git a/doc/src/rust.md b/doc/src/rust/index.md
similarity index 91%
rename from doc/src/rust.md
rename to doc/src/rust/index.md
index bad92288..6a0ca08d 100644
--- a/doc/src/rust.md
+++ b/doc/src/rust/index.md
@@ -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.
diff --git a/doc/src/rust/indexers.md b/doc/src/rust/indexers.md
index 9c85e9a5..800c43b8 100644
--- a/doc/src/rust/indexers.md
+++ b/doc/src/rust/indexers.md
@@ -9,6 +9,9 @@ A custom type with an indexer function defined can use the bracket '`[]`' notati
Indexers are disabled when the [`no_index`] feature is used.
+For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for
+[arrays] and [object maps].
+
```rust
#[derive(Clone)]
struct TestStruct {
@@ -28,7 +31,7 @@ impl TestStruct {
}
}
-let engine = Engine::new();
+let mut engine = Engine::new();
engine.register_type::();
@@ -42,6 +45,3 @@ let result = engine.eval::("let a = new_ts(); a[2] = 42; a[2]")?;
println!("Answer: {}", result); // prints 42
```
-
-For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for
-[arrays] and [object maps].
diff --git a/doc/src/rust/operators.md b/doc/src/rust/operators.md
index b91b1cdc..8374bfb4 100644
--- a/doc/src/rust/operators.md
+++ b/doc/src/rust/operators.md
@@ -17,6 +17,10 @@ Similarly, comparison operators including `==`, `!=` etc. are all implemented as
with the stark exception of `&&` and `||`. Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators),
`&&` and `||` are handled specially and _not_ via a function; as a result, overriding them has no effect at all.
+
+Overload Operator via Rust Function
+----------------------------------
+
Operator functions cannot be defined as a script function (because operators syntax are not valid function names).
However, operator functions _can_ be registered to the [`Engine`] via the methods
@@ -48,6 +52,10 @@ engine.register_fn("+", mixed_add); // register '+' operator for
let result: i64 = engine.eval("1 + 1.0"); // prints 2.0 (normally an error)
```
+
+Considerations
+--------------
+
Normally, use operator overloading for [custom types] only.
Be very careful when overriding built-in operators because script authors expect standard operators to behave in a
diff --git a/doc/src/rust/packages/builtin.md b/doc/src/rust/packages/builtin.md
index da33bfe4..4b4fba81 100644
--- a/doc/src/rust/packages/builtin.md
+++ b/doc/src/rust/packages/builtin.md
@@ -18,6 +18,7 @@ Built-In Packages
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
| `BasicArrayPackage` | Basic [array] functions (not available under `no_index`) | No | Yes |
| `BasicMapPackage` | Basic [object map] functions (not available under `no_object`) | No | Yes |
+| `BasicFnPackage` | Basic methods for [function pointers]. | Yes | Yes |
| `EvalPackage` | Disable [`eval`] | No | No |
| `CorePackage` | Basic essentials | Yes | Yes |
| `StandardPackage` | Standard library (default for `Engine::new`) | No | Yes |
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/rust/print-custom.md b/doc/src/rust/print-custom.md
index 53996fe0..c729c49d 100644
--- a/doc/src/rust/print-custom.md
+++ b/doc/src/rust/print-custom.md
@@ -3,14 +3,15 @@ Printing for Custom Types
{{#include ../links.md}}
-To use custom types for [`print`] and [`debug`], or convert its value into a [string], it is necessary that the following
-functions be registered (assuming the custom type is `T : Display + Debug`):
+To use custom types for [`print`] and [`debug`], or convert its value into a [string],
+it is necessary that the following functions be registered (assuming the custom type
+is `T : Display + Debug`):
| Function | Signature | Typical implementation | Usage |
| ----------- | ------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
| `to_string` | \|s: &mut T\| -> ImmutableString
| `s.to_string().into()` | Converts the custom type into a [string] |
-| `print` | \|s: &mut T\| -> ImmutableString
| `s.to_string().into()` | Converts the custom type into a [string] for the [`print`](#print-and-debug) statement |
-| `debug` | \|s: &mut T\| -> ImmutableString
| `format!("{:?}", s).into()` | Converts the custom type into a [string] for the [`debug`](#print-and-debug) statement |
+| `print` | \|s: &mut T\| -> ImmutableString
| `s.to_string().into()` | Converts the custom type into a [string] for the [`print`] statement |
+| `debug` | \|s: &mut T\| -> ImmutableString
| `format!("{:?}", s).into()` | Converts the custom type into a [string] for the [`debug`] statement |
| `+` | \|s1: ImmutableString, s: T\| -> ImmutableString
| `s1 + s` | Append the custom type to another [string], for `print("Answer: " + type);` usage |
| `+` | \|s: T, s2: ImmutableString\| -> ImmutableString
| `s.to_string().push_str(&s2).into();` | Append another [string] to the custom type, for `print(type + " is the answer");` usage |
| `+=` | \|s1: &mut ImmutableString, s: T\|
| `s1 += s.to_string()` | Append the custom type to an existing [string], for `s += type;` usage |
diff --git a/doc/src/rust/scope.md b/doc/src/rust/scope.md
index d5ad8fa5..a33e4b4c 100644
--- a/doc/src/rust/scope.md
+++ b/doc/src/rust/scope.md
@@ -20,40 +20,35 @@ then the same state is threaded through multiple invocations:
```rust
use rhai::{Engine, Scope, EvalAltResult};
-fn main() -> Result<(), Box>
-{
- let engine = Engine::new();
+let engine = Engine::new();
- // First create the state
- let mut scope = Scope::new();
+// First create the state
+let mut scope = Scope::new();
- // Then push (i.e. add) some initialized variables into the state.
- // Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64.
- // Better stick to them or it gets hard working with the script.
- scope.push("y", 42_i64);
- scope.push("z", 999_i64);
+// Then push (i.e. add) some initialized variables into the state.
+// Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64.
+// Better stick to them or it gets hard working with the script.
+scope.push("y", 42_i64);
+scope.push("z", 999_i64);
- // 'set_value' adds a variable when one doesn't exist
- scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str'
+// 'set_value' adds a variable when one doesn't exist
+scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str'
- // First invocation
- engine.eval_with_scope::<()>(&mut scope, r"
- let x = 4 + 5 - y + z + s.len;
- y = 1;
- ")?;
+// First invocation
+engine.eval_with_scope::<()>(&mut scope, r"
+ let x = 4 + 5 - y + z + s.len;
+ y = 1;
+")?;
- // Second invocation using the same state
- let result = engine.eval_with_scope::(&mut scope, "x")?;
+// Second invocation using the same state
+let result = engine.eval_with_scope::(&mut scope, "x")?;
- println!("result: {}", result); // prints 979
+println!("result: {}", result); // prints 979
- // Variable y is changed in the script - read it with 'get_value'
- assert_eq!(scope.get_value::("y").expect("variable y should exist"), 1);
+// Variable y is changed in the script - read it with 'get_value'
+assert_eq!(scope.get_value::("y").expect("variable y should exist"), 1);
- // We can modify scope variables directly with 'set_value'
- scope.set_value("y", 42_i64);
- assert_eq!(scope.get_value::("y").expect("variable y should exist"), 42);
-
- Ok(())
-}
+// We can modify scope variables directly with 'set_value'
+scope.set_value("y", 42_i64);
+assert_eq!(scope.get_value::("y").expect("variable y should exist"), 42);
```
diff --git a/doc/src/safety.md b/doc/src/safety/index.md
similarity index 52%
rename from doc/src/safety.md
rename to doc/src/safety/index.md
index d207fe9d..22ae237d 100644
--- a/doc/src/safety.md
+++ b/doc/src/safety/index.md
@@ -1,31 +1,35 @@
Safety and Protection Against DoS Attacks
========================================
-{{#include links.md}}
+{{#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.
+* **Memory**: A malicious 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.
+* **CPU**: A malicious 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.
+* **Time**: A malicious script may run indefinitely, thereby blocking the calling system which is waiting for a result.
+
+* **Stack**: A malicious script may attempt an infinite recursive call that exhausts the call stack.
-* **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
+ Another way to cause a stack overflow is to load a [self-referencing module][`import`].
+
+* **Overflows**: A malicious 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,
+* **Files**: A malicious 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,
+* **Data**: A malicious 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..adb1c133 100644
--- a/doc/src/safety/max-modules.md
+++ b/doc/src/safety/max-modules.md
@@ -10,6 +10,8 @@ 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][`import`] (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/safety/max-stmt-depth.md b/doc/src/safety/max-stmt-depth.md
index 007e7c82..e8b5ee84 100644
--- a/doc/src/safety/max-stmt-depth.md
+++ b/doc/src/safety/max-stmt-depth.md
@@ -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).
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/builds/minimal.md b/doc/src/start/builds/minimal.md
index 57ad59c7..d1da0f01 100644
--- a/doc/src/start/builds/minimal.md
+++ b/doc/src/start/builds/minimal.md
@@ -17,6 +17,16 @@ opt-level = "z" # optimize for size
```
+Use `i32` Only
+--------------
+
+For embedded systems that must optimize for code size, the architecture is commonly 32-bit.
+Use [`only_i32`] to prune away large sections of code implementing functions for other numeric types
+(including `i64`).
+
+If, for some reason, 64-bit long integers must be supported, use [`only_i64`] instead of [`only_i32`].
+
+
Opt-Out of Features
------------------
@@ -28,13 +38,17 @@ Omitting arrays ([`no_index`]) yields the most code-size savings, followed by fl
([`no_float`]), checked arithmetic/script resource limits ([`unchecked`]) and finally object maps and custom types ([`no_object`]).
Where the usage scenario does not call for loading externally-defined modules, use [`no_module`] to save some bytes.
-Disable script-defined functions ([`no_function`]) only when the feature is not needed because code size savings is minimal.
+Disable script-defined functions ([`no_function`]) when the feature is not needed.
+Both of these have little code size savings.
Use a Raw [`Engine`]
-------------------
-[`Engine::new_raw`](#raw-engine) creates a _raw_ engine.
-A _raw_ engine supports, out of the box, only a very [restricted set](#built-in-operators) of basic arithmetic and logical operators.
+[`Engine::new_raw`][raw `Engine`] creates a _raw_ engine.
+A _raw_ engine supports, out of the box, only a very [restricted set]({{rootUrl}}/engine/raw.md#built-in-operators)
+of basic arithmetic and logical operators.
+
Selectively include other necessary functionalities by loading specific [packages] to minimize the footprint.
+
Packages are sharable (even across threads via the [`sync`] feature), so they only have to be created once.
diff --git a/doc/src/start/builds/performance.md b/doc/src/start/builds/performance.md
index 65099707..5db1edc1 100644
--- a/doc/src/start/builds/performance.md
+++ b/doc/src/start/builds/performance.md
@@ -7,14 +7,17 @@ Use Only One Integer Type
------------------------
Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`).
+
If only a single integer type is needed in scripts - most of the time this is the case - it is best to avoid registering
-lots of functions related to other integer types that will never be used. As a result, performance should improve.
+lots of functions related to other integer types that will never be used. As a result, [`Engine`] creation will be faster
+because fewer functions need to be loaded.
Use Only 32-Bit Numbers
----------------------
If only 32-bit integers are needed - again, most of the time this is the case - using [`only_i32`] disables also `i64`.
+
On 64-bit targets this may not gain much, but on some 32-bit targets this improves performance due to 64-bit arithmetic
requiring more CPU cycles to complete.
@@ -24,4 +27,5 @@ Minimize Size of [`Dynamic`]
Turning on [`no_float`], and [`only_i32`] makes the key [`Dynamic`] data type only 8 bytes small on 32-bit targets
while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`.
+
Making [`Dynamic`] small helps performance due to better cache efficiency.
diff --git a/doc/src/start/builds/wasm.md b/doc/src/start/builds/wasm.md
index ca2b0dbb..1f813ae8 100644
--- a/doc/src/start/builds/wasm.md
+++ b/doc/src/start/builds/wasm.md
@@ -14,8 +14,41 @@ But anyhow, do it because you _can_!
When building for WASM, certain features will not be available, such as the script file API's and loading modules
from external script files.
-Also look into [minimal builds] to reduce generated WASM size. As of this version, a typical, full-featured
-Rhai scripting engine compiles to a single WASM file less than 200KB gzipped. When excluding features that are
-marginal in WASM environment, the gzipped payload can be further shrunk to 160KB.
+
+Size
+----
+
+Also look into [minimal builds] to reduce generated WASM size.
+
+As of this version, a typical, full-featured Rhai scripting engine compiles to a single WASM file
+less than 200KB gzipped.
+
+When excluding features that are marginal in WASM environment, the gzipped payload can be
+further shrunk to 160KB.
+
+
+Speed
+-----
In benchmark tests, a WASM build runs scripts roughly 1.7-2.2x slower than a native optimized release build.
+
+
+Common Features
+---------------
+
+Some Rhai functionalities are not necessary in a WASM environment, so the following features
+are typically used for a WASM build:
+
+| Feature | Description |
+| :-----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [`unchecked`] | When a WASM module panics, it doesn't crash the entire web app; however this also disables [maximum number of operations] and [progress] tracking so a script can still run indefinitely - the web app must terminate it itself. |
+| [`only_i32`] | JavaScript has only one `number` type and we're only supporting `wasm32` here (so far). |
+| [`no_module`] | A WASM module cannot load modules from the file system, so usually this is not needed, but the savings are minimal; alternatively, a custom [module resolver] can be provided that loads other Rhai scripts. |
+
+The following features are typically _not_ used because they don't make sense in a WASM build:
+
+| Feature | Why unnecessary |
+| :-----------: | ------------------------------------------------------------------ |
+| [`sync`] | WASM is single-threaded. |
+| [`no_std`] | `std` lib works fine with WASM. |
+| [`internals`] | WASM usually doesn't need to access Rhai internal data structures. |
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/examples/rust.md b/doc/src/start/examples/rust.md
index d7a28271..1803c49b 100644
--- a/doc/src/start/examples/rust.md
+++ b/doc/src/start/examples/rust.md
@@ -5,17 +5,17 @@ Rust Examples
A number of examples can be found in the `examples` folder:
-| Example | Description |
-| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
-| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | shows how to register a custom Rust type and using [arrays] on it |
-| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | shows how to register a custom Rust type and methods for it |
-| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | simple example that evaluates an expression and prints the result |
-| [`no_std`](https://github.com/jonathandturner/rhai/tree/master/examples/no_std.rs) | example to test out `no-std` builds |
-| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | evaluates two pieces of code in separate runs, but using a common [`Scope`] |
-| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | runs each filename passed to it as a Rhai script |
-| [`simple_fn`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | shows how to register a simple function |
-| [`strings`](https://github.com/jonathandturner/rhai/tree/master/examples/strings.rs) | shows different ways to register functions taking string arguments |
-| [`repl`](https://github.com/jonathandturner/rhai/tree/master/examples/repl.rs) | a simple REPL, interactively evaluate statements from stdin |
+| Example | Description |
+| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
+| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | Shows how to register a custom Rust type and using [arrays] on it. |
+| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | Shows how to register a custom Rust type and methods for it. |
+| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | Simple example that evaluates an expression and prints the result. |
+| [`no_std`](https://github.com/jonathandturner/rhai/tree/master/examples/no_std.rs) | Example to test out `no-std` builds. |
+| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | Evaluates two pieces of code in separate runs, but using a common [`Scope`]. |
+| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | Runs each filename passed to it as a Rhai script. |
+| [`simple_fn`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | Shows how to register a simple function. |
+| [`strings`](https://github.com/jonathandturner/rhai/tree/master/examples/strings.rs) | Shows different ways to register functions taking string arguments. |
+| [`repl`](https://github.com/jonathandturner/rhai/tree/master/examples/repl.rs) | A simple REPL, interactively evaluate statements from stdin. |
The `repl` example is a particularly good one as it allows one to interactively try out Rhai's
language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop).
diff --git a/doc/src/start/examples/scripts.md b/doc/src/start/examples/scripts.md
index 50cfba4e..20836aef 100644
--- a/doc/src/start/examples/scripts.md
+++ b/doc/src/start/examples/scripts.md
@@ -10,21 +10,22 @@ There are also a number of examples scripts that showcase Rhai's features, all i
| Script | Description |
| -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
-| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [arrays] in Rhai |
-| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | variable declarations |
-| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | just comments |
+| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [Arrays] |
+| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | Variable declarations |
+| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | Just comments |
| [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`](#for-loop) loops |
| [`for2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for2.rhai) | [`for`](#for-loop) loops on [arrays] |
-| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | a [function] without parameters |
-| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | a [function] with two parameters |
-| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | a [function] with many parameters |
+| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | A [function] without parameters |
+| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | A [function] with two parameters |
+| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | A [function] with many parameters |
| [`if1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/if1.rhai) | [`if`](#if-statement) example |
-| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop |
-| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | just simple addition |
-| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | simple addition and multiplication |
-| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | change evaluation order with parenthesis |
-| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [string] operations |
-| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [string] and [object map] operations |
+| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | Count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop |
+| [`oop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] |
+| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | Just simple addition |
+| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | Simple addition and multiplication |
+| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | Change evaluation order with parenthesis |
+| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [String] operations |
+| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [String] and [object map] operations |
| [`while.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/while.rhai) | [`while`](#while-loop) loop |
@@ -33,12 +34,12 @@ Benchmark Scripts
The following scripts are for benchmarking the speed of Rhai:
-| Scripts | Description |
-| ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
-| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | a simple program to measure the speed of Rhai's interpreter (1 million iterations) |
-| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | use Sieve of Eratosthenes to find all primes smaller than a limit |
-| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | calculate the n-th Fibonacci number using a really dumb algorithm |
-| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | matrix multiplication test to measure the speed of multi-dimensional array access |
+| Scripts | Description |
+| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
+| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | A simple program to measure the speed of Rhai's interpreter (1 million iterations). |
+| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | Use Sieve of Eratosthenes to find all primes smaller than a limit. |
+| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | Calculate the n-th Fibonacci number using a really dumb algorithm. |
+| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. |
Running Example Scripts
diff --git a/doc/src/start/features.md b/doc/src/start/features.md
index fca919a5..b4a415a9 100644
--- a/doc/src/start/features.md
+++ b/doc/src/start/features.md
@@ -4,24 +4,27 @@ 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.
+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.
+Excluding unneeded functionalities can result in smaller, faster builds as well as
+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. 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`. |
-| `no_optimize` | Disable the script optimizer. |
-| `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`. |
-| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
-| `no_index` | Disable [arrays] and indexing features. |
-| `no_object` | Disable support for custom types and [object maps]. |
-| `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. |
+| 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.
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`. |
+| `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`. |
+| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
+| `no_index` | Disable [arrays] and indexing features. |
+| `no_object` | Disable support for [custom types] and [object maps]. |
+| `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. |
Example
@@ -30,19 +33,19 @@ Example
The `Cargo.toml` configuration below turns on these six features:
* `sync` (everything `Send + Sync`)
-* `unchecked` (no checked arithmetic - should not be used with untrusted user scripts)
+* `unchecked` (disable all checking - should not be used with untrusted user scripts)
* `only_i32` (only 32-bit signed integers)
* `no_float` (no floating point numbers)
-* `no_module` (no loading external modules)
-* `no_function` (no defining functions)
+* `no_module` (no loading external [modules])
+* `no_function` (no defining [functions])
```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`),
-no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining functions
-nor loading external modules.
+The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32`, `i16` or `i64`),
+no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining [functions]
+nor loading external [modules].
This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware.
diff --git a/doc/src/start.md b/doc/src/start/index.md
similarity index 81%
rename from doc/src/start.md
rename to doc/src/start/index.md
index 33de6526..f89dfbd0 100644
--- a/doc/src/start.md
+++ b/doc/src/start/index.md
@@ -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.
diff --git a/doc/src/start/install.md b/doc/src/start/install.md
index c3553298..a7dc944f 100644
--- a/doc/src/start/install.md
+++ b/doc/src/start/install.md
@@ -3,15 +3,17 @@ 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`:
+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]
-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/doc/src/start/playground.md b/doc/src/start/playground.md
new file mode 100644
index 00000000..08809805
--- /dev/null
+++ b/doc/src/start/playground.md
@@ -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.
diff --git a/examples/repl.rs b/examples/repl.rs
index f18a56e5..cf255986 100644
--- a/examples/repl.rs
+++ b/examples/repl.rs
@@ -48,6 +48,7 @@ fn print_error(input: &str, err: EvalAltResult) {
fn print_help() {
println!("help => print this help");
println!("quit, exit => quit");
+ println!("scope => print all variables in the scope");
println!("ast => print the last AST");
println!("astu => print the last raw, un-optimized AST");
println!(r"end a line with '\' to continue to the next line.");
@@ -110,6 +111,13 @@ fn main() {
continue;
}
"exit" | "quit" => break, // quit
+ "scope" => {
+ scope
+ .iter()
+ .enumerate()
+ .for_each(|(i, (name, value))| println!("[{}] {} = {:?}", i + 1, name, value));
+ continue;
+ }
"astu" => {
// print the last un-optimized AST
println!("{:#?}", &ast_u);
@@ -158,7 +166,6 @@ fn main() {
}
// Throw away all the statements, leaving only the functions
- #[cfg(not(feature = "no_function"))]
- main_ast.retain_functions();
+ main_ast.clear_statements();
}
}
diff --git a/rhai_logo.png b/rhai_logo.png
new file mode 100644
index 00000000..af45aa7f
Binary files /dev/null and b/rhai_logo.png differ
diff --git a/scripts/for1.rhai b/scripts/for1.rhai
index 8fc6c95e..345d17a7 100644
--- a/scripts/for1.rhai
+++ b/scripts/for1.rhai
@@ -12,6 +12,6 @@ for a in arr {
//print(a); // <- if you uncomment this line, the script will fail to run
// because 'a' is not defined here
-for i in range(0, 5) { // runs through a range from 1 to 5 exclusive
+for i in range(0, 5) { // runs through a range from 0 to 4
print(i);
}
diff --git a/scripts/oop.rhai b/scripts/oop.rhai
new file mode 100644
index 00000000..fe03b636
--- /dev/null
+++ b/scripts/oop.rhai
@@ -0,0 +1,45 @@
+// This script simulates object-oriented programming (OOP) techniques
+// using function pointers (Fn) and object maps.
+
+// Define object
+let obj1 = #{
+ _data: 42, // data field
+ get_data: Fn("getData"), // property getter
+ action: Fn("action"), // method
+ update: Fn("update1") // property setter
+};
+
+fn getData() {
+ this._data
+}
+fn action() {
+ print("Data=" + this._data);
+}
+fn update1(x) {
+ this._data = x;
+ this.action();
+}
+
+if obj1.get_data() > 0 { // property access
+ obj1.update(123); // call method
+} else {
+ print("we have a problem here");
+}
+
+// Define another object based on the first object
+let obj2 = #{
+ _data: 0, // data field - new value
+ update: Fn("update2") // property setter - another function
+};
+obj2.fill_with(obj1); // add all other fields from obj1
+
+fn update2(x) {
+ this._data = x * 2;
+ this.action();
+}
+
+if obj2.get_data() > 0 { // property access
+ obj2.update(0); // call method
+} else {
+ obj2.update(42); // call method
+}
diff --git a/scripts/string.rhai b/scripts/string.rhai
index 835b2556..38dc52e9 100644
--- a/scripts/string.rhai
+++ b/scripts/string.rhai
@@ -10,8 +10,8 @@ print("foo" < "bar"); // string comparison
print("foo" >= "bar"); // string comparison
print("the answer is " + 42); // string building using non-string types
-let s = "hello, world!"; // string variable
-print("length=" + s.len); // should be 13
+let s = "\u2764" hello, world! \U0001F603"; // string variable
+print("length=" + s.len); // should be 17
-s[s.len-1] = '?'; // change the string
+s[s.len-3] = '?'; // change the string
print("Question: " + s); // should print 'Question: hello, world?'
diff --git a/src/any.rs b/src/any.rs
index 6a5d9050..7e8b8072 100644
--- a/src/any.rs
+++ b/src/any.rs
@@ -1,7 +1,6 @@
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
-use crate::fn_native::SendSync;
-use crate::module::Module;
+use crate::fn_native::{FnPtr, SendSync};
use crate::parser::{ImmutableString, INT};
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
@@ -91,13 +90,13 @@ pub trait Variant: Any + Send + Sync {
impl Variant for T {
fn as_any(&self) -> &dyn Any {
- self as &dyn Any
+ self
}
fn as_mut_any(&mut self) -> &mut dyn Any {
- self as &mut dyn Any
+ self
}
fn as_box_any(self: Box) -> Box {
- self as Box
+ self
}
fn type_name(&self) -> &'static str {
type_name::()
@@ -138,7 +137,7 @@ pub enum Union {
Array(Box),
#[cfg(not(feature = "no_object"))]
Map(Box