diff --git a/README.md b/README.md
index 7d55bdbe..952d9bf3 100644
--- a/README.md
+++ b/README.md
@@ -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).
@@ -43,3 +45,9 @@ Documentation
-------------
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/doc/src/SUMMARY.md b/doc/src/SUMMARY.md
index 6ea2fc76..f9491dd3 100644
--- a/doc/src/SUMMARY.md
+++ b/doc/src/SUMMARY.md
@@ -1,30 +1,31 @@
The Rhai Scripting Language
==========================
-1. [What is Rhai](about.md)
+1. [What is Rhai](about/index.md)
1. [Features](about/features.md)
2. [Supported Targets and Builds](about/targets.md)
3. [What Rhai Isn't](about/non-design.md)
4. [Related Resources](about/related.md)
-2. [Getting Started](start.md)
- 1. [Install the Rhai Crate](start/install.md)
- 2. [Optional Features](start/features.md)
- 3. [Special Builds](start/builds/index.md)
+3. [Getting Started](start/index.md)
+ 1. [Online Playground](start/playground.md)
+ 2. [Install the Rhai Crate](start/install.md)
+ 3. [Optional Features](start/features.md)
+ 4. [Special Builds](start/builds/index.md)
1. [Performance](start/builds/performance.md)
2. [Minimal](start/builds/minimal.md)
3. [no-std](start/builds/no-std.md)
4. [WebAssembly (WASM)](start/builds/wasm.md)
- 4. [Examples](start/examples/index.md)
+ 5. [Examples](start/examples/index.md)
1. [Rust](start/examples/rust.md)
2. [Scripts](start/examples/scripts.md)
-3. [Using the `Engine`](engine.md)
+4. [Using the `Engine`](engine/index.md)
1. [Hello World in Rhai - Evaluate a Script](engine/hello-world.md)
2. [Compile a Script to AST for Repeated Evaluations](engine/compile.md)
3. [Call a Rhai Function from Rust](engine/call-fn.md)
4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md)
5. [Evaluate Expressions Only](engine/expressions.md)
6. [Raw Engine](engine/raw.md)
-4. [Extend Rhai with Rust](rust.md)
+5. [Extend Rhai with Rust](rust/index.md)
1. [Traits](rust/traits.md)
2. [Register a Rust Function](rust/functions.md)
1. [String Parameters in Rust Functions](rust/strings.md)
@@ -42,7 +43,7 @@ The Rhai Scripting Language
4. [Printing Custom Types](rust/print-custom.md)
9. [Scope - Initializing and Maintaining State](rust/scope.md)
10. [Engine Configuration Options](rust/options.md)
-5. [Rhai Language Reference](language.md)
+6. [Rhai Language Reference](language/index.md)
1. [Comments](language/comments.md)
2. [Values and Types](language/values-and-types.md)
1. [Dynamic Values](language/dynamic.md)
@@ -70,9 +71,10 @@ The Rhai Scripting Language
12. [Return Values](language/return.md)
13. [Throw Exception on Error](language/throw.md)
14. [Functions](language/functions.md)
- 1. [Function Overloading](language/overload.md)
- 2. [Call Method as Function](language/method.md)
- 3. [Function Pointers](language/fn-ptr.md)
+ 1. [Call Method as Function](language/method.md)
+ 2. [Overloading](language/overload.md)
+ 3. [Namespaces](language/fn-namespaces.md)
+ 4. [Function Pointers](language/fn-ptr.md)
15. [Print and Debug](language/print-debug.md)
16. [Modules](language/modules/index.md)
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
@@ -81,7 +83,7 @@ The Rhai Scripting Language
4. [Create from AST](language/modules/ast.md)
5. [Module Resolvers](language/modules/resolvers.md)
1. [Implement a Custom Module Resolver](language/modules/imp-resolver.md)
-6. [Safety and Protection](safety/index.md)
+7. [Safety and Protection](safety/index.md)
1. [Checked Arithmetic](safety/checked.md)
2. [Sand-Boxing](safety/sandbox.md)
3. [Maximum Length of Strings](safety/max-string-size.md)
@@ -92,7 +94,7 @@ The Rhai Scripting Language
7. [Maximum Number of Modules](safety/max-modules.md)
8. [Maximum Call Stack Depth](safety/max-call-stack.md)
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
-7. [Advanced Topics](advanced.md)
+8. [Advanced Topics](advanced.md)
1. [Object-Oriented Programming (OOP)](language/oop.md)
2. [Script Optimization](engine/optimize/index.md)
1. [Optimization Levels](engine/optimize/optimize-levels.md)
@@ -102,7 +104,7 @@ The Rhai Scripting Language
5. [Volatility Considerations](engine/optimize/volatility.md)
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
3. [Eval Statement](language/eval.md)
-8. [Appendix](appendix/index.md)
+9. [Appendix](appendix/index.md)
1. [Keywords](appendix/keywords.md)
2. [Operators](appendix/operators.md)
3. [Literals](appendix/literals.md)
diff --git a/doc/src/about/features.md b/doc/src/about/features.md
index 75df3def..c012353a 100644
--- a/doc/src/about/features.md
+++ b/doc/src/about/features.md
@@ -8,7 +8,7 @@ Easy
* Easy-to-use language similar to JavaScript+Rust with dynamic typing.
-* Tight integration with native Rust [functions] and [types][custom types], including [getters/setters]({{rootUrl}}/rust/getters-setters.md), [methods][custom type] and [indexers]({{rootUrl}}/rust/indexers.md).
+* Tight integration with native Rust [functions] and [types][custom types], including [getters/setters], [methods][custom type] and [indexers].
* Freely pass Rust variables/constants into a script via an external [`Scope`].
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/related.md b/doc/src/about/related.md
index f91fdb64..defc29e4 100644
--- a/doc/src/about/related.md
+++ b/doc/src/about/related.md
@@ -11,6 +11,8 @@ Other online documentation resources for Rhai:
* [`LIB.RS`](https://lib.rs/crates/rhai) - Rhai library info
+* [Online Playground][playground] - Run scripts directly from editor
+
Other cool projects to check out:
* [ChaiScript](http://chaiscript.com/) - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being led by my cousin.
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/reoptimize.md b/doc/src/engine/optimize/reoptimize.md
index bd173be8..e8292e4c 100644
--- a/doc/src/engine/optimize/reoptimize.md
+++ b/doc/src/engine/optimize/reoptimize.md
@@ -4,13 +4,13 @@ Re-Optimize an AST
{{#include ../../links.md}}
Sometimes it is more efficient to store one single, large script with delimited code blocks guarded by
-constant variables. This script is compiled once to an `AST`.
+constant variables. This script is compiled once to an [`AST`].
-Then, depending on the execution environment, constants are passed into the [`Engine`] and the `AST`
+Then, depending on the execution environment, constants are passed into the [`Engine`] and the [`AST`]
is _re_-optimized based on those constants via the `Engine::optimize_ast` method,
effectively pruning out unused code sections.
-The final, optimized `AST` is then used for evaluations.
+The final, optimized [`AST`] is then used for evaluations.
```rust
// Compile master script to AST
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
index a9f2e64a..e4d9bc5d 100644
--- a/doc/src/language/fn-ptr.md
+++ b/doc/src/language/fn-ptr.md
@@ -54,7 +54,34 @@ let fn_name = "hello"; // the function name does not have to exist yet
let hello = Fn(fn_name + "_world");
-hello.call(0); // error: function not found - "hello_world (i64)"
+hello.call(0); // error: function not found - 'hello_world (i64)'
+```
+
+
+Global Namespace Only
+--------------------
+
+Because of their dynamic nature, function pointers cannot refer to functions in a _module_ [namespace][function namespace]
+(i.e. functions in [`import`]-ed modules). They can only refer to functions within the global [namespace][function namespace].
+See [function namespaces] for more details.
+
+```rust
+import "foo" as f; // assume there is 'f::do_something()'
+
+f::do_something(); // works!
+
+let p = Fn("f::do_something");
+
+p.call(); // error: function not found - 'f::do_something'
+
+fn do_something_now() { // call it from a local function
+ import "foo" as f;
+ f::do_something();
+}
+
+let p = Fn("do_something_now");
+
+p.call(); // works!
```
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/method.md b/doc/src/language/method.md
index a9cdc1c7..bdce0ed6 100644
--- a/doc/src/language/method.md
+++ b/doc/src/language/method.md
@@ -3,9 +3,9 @@ Call Method as Function
{{#include ../links.md}}
-Property getters/setters and methods in a Rust custom type registered with the [`Engine`] can be called
+Property [getters/setters] and [methods][custom types] in a Rust custom type registered with the [`Engine`] can be called
just like a regular function. In fact, like Rust, property getters/setters and object methods
-are registered as regular functions in Rhai that take a first `&mut` parameter.
+are registered as regular [functions] in Rhai that take a first `&mut` parameter.
Unlike functions defined in script (for which all arguments are passed by _value_),
native Rust functions may mutate the object (or the first argument if called in normal function call style).
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/overload.md b/doc/src/language/overload.md
index b841d00a..bb291ca3 100644
--- a/doc/src/language/overload.md
+++ b/doc/src/language/overload.md
@@ -3,7 +3,7 @@ Function Overloading
{{#include ../links.md}}
-Functions defined in script can be _overloaded_ by _arity_ (i.e. they are resolved purely upon the function's _name_
+[Functions] defined in script can be _overloaded_ by _arity_ (i.e. they are resolved purely upon the function's _name_
and _number_ of parameters, but not parameter _types_ since all parameters are the same type - [`Dynamic`]).
New definitions _overwrite_ previous definitions of the same name and number of parameters.
diff --git a/doc/src/links.md b/doc/src/links.md
index ae6ef01d..b7147a50 100644
--- a/doc/src/links.md
+++ b/doc/src/links.md
@@ -15,11 +15,13 @@
[minimal builds]: {{rootUrl}}/start/builds/minimal.md
[WASM]: {{rootUrl}}/start/builds/wasm.md
+[playground]: https://alvinhochun.github.io/rhai-demo
[`Engine`]: {{rootUrl}}/engine/hello-world.md
[traits]: {{rootUrl}}/rust/traits.md
[`private`]: {{rootUrl}}/engine/call-fn.md
[`Func`]: {{rootUrl}}/engine/func.md
+[`AST`]: {{rootUrl}}/engine/compile.md
[`eval_expression`]: {{rootUrl}}/engine/expressions.md
[`eval_expression_with_scope`]: {{rootUrl}}/engine/expressions.md
[raw `Engine`]: {{rootUrl}}/engine/raw.md
@@ -38,6 +40,8 @@
[custom type]: {{rootUrl}}/rust/custom.md
[custom types]: {{rootUrl}}/rust/custom.md
+[getters/setters]: {{rootUrl}}/rust/getters-setters.md
+[indexers]: {{rootUrl}}/rust/indexers.md
[`instant::Instant`]: https://crates.io/crates/instant
@@ -70,6 +74,8 @@
[functions]: {{rootUrl}}/language/functions.md
[function pointer]: {{rootUrl}}/language/fn-ptr.md
[function pointers]: {{rootUrl}}/language/fn-ptr.md
+[function namespace]: {{rootUrl}}/language/fn-namespaces.md
+[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
[`Module`]: {{rootUrl}}/language/modules/index.md
[module]: {{rootUrl}}/language/modules/index.md
@@ -89,7 +95,7 @@
[maximum length of strings]: {{rootUrl}}/safety/max-string-size.md
[maximum size of arrays]: {{rootUrl}}/safety/max-array-size.md
[maximum size of object maps]: {{rootUrl}}/safety/max-map-size.md
-[progress]:/safety/progress.md
+[progress]: {{rootUrl}}/safety/progress.md
[script optimization]: {{rootUrl}}/engine/optimize/index.md
[`OptimizationLevel::Full`]: {{rootUrl}}/engine/optimize/optimize-levels.md
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/safety.md b/doc/src/safety.md
deleted file mode 100644
index 1140c921..00000000
--- a/doc/src/safety.md
+++ /dev/null
@@ -1 +0,0 @@
-# Safety and Protection
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/features.md b/doc/src/start/features.md
index c7cbd095..b4a415a9 100644
--- a/doc/src/start/features.md
+++ b/doc/src/start/features.md
@@ -14,7 +14,7 @@ more control over what a script can (or cannot) do.
| Feature | Description |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unchecked` | Disable arithmetic checking (such as over-flows and division by zero), call stack depth limit, operations count limit and modules loading limit.
Beware that a bad script may panic the entire system! |
-| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and `AST`, are all `Send + Sync`. |
+| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and [`AST`], are all `Send + Sync`. |
| `no_optimize` | Disable [script optimization]. |
| `no_float` | Disable floating-point numbers and math. |
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
@@ -24,7 +24,7 @@ more control over what a script can (or cannot) do.
| `no_function` | Disable script-defined [functions]. |
| `no_module` | Disable loading external [modules]. |
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
-| `internals` | Expose internal data structures (e.g. `AST` nodes). Beware that Rhai internals are volatile and may change from version to version. |
+| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
Example
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 b8ee54dc..a7dc944f 100644
--- a/doc/src/start/install.md
+++ b/doc/src/start/install.md
@@ -3,8 +3,10 @@ Install the Rhai Crate
{{#include ../links.md}}
-Install the Rhai crate from [`crates.io`](https:/crates.io/crates/rhai/), start by looking up the
-latest version and adding this line under `dependencies` in `Cargo.toml`:
+In order to use Rhai in a project, the Rhai crate must first be made a dependency.
+
+The easiest way is to install the Rhai crate from [`crates.io`](https:/crates.io/crates/rhai/),
+starting by looking up the latest version and adding this line under `dependencies` in the project's `Cargo.toml`:
```toml
[dependencies]
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/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/src/any.rs b/src/any.rs
index d99712e1..bc148956 100644
--- a/src/any.rs
+++ b/src/any.rs
@@ -545,6 +545,7 @@ impl Dynamic {
pub fn as_str(&self) -> Result<&str, &'static str> {
match &self.0 {
Union::Str(s) => Ok(s),
+ Union::FnPtr(f) => Ok(f.fn_name()),
_ => Err(self.type_name()),
}
}
@@ -561,15 +562,7 @@ impl Dynamic {
pub(crate) fn take_immutable_string(self) -> Result {
match self.0 {
Union::Str(s) => Ok(s),
- _ => Err(self.type_name()),
- }
- }
-
- /// Cast the `Dynamic` as a `FnPtr` and return the function name.
- /// Returns the name of the actual type if the cast fails.
- pub(crate) fn as_fn_name(&self) -> Result<&str, &'static str> {
- match &self.0 {
- Union::FnPtr(f) => Ok(f.fn_name()),
+ Union::FnPtr(f) => Ok(f.take_fn_name()),
_ => Err(self.type_name()),
}
}
diff --git a/src/api.rs b/src/api.rs
index 66050214..2f0e3e19 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -422,7 +422,7 @@ impl Engine {
self.register_indexer_set(setter);
}
- /// Compile a string into an `AST`, which can be used later for evaluation.
+ /// Compile a string into an [`AST`], which can be used later for evaluation.
///
/// # Example
///
@@ -445,7 +445,7 @@ impl Engine {
self.compile_with_scope(&Scope::new(), script)
}
- /// Compile a string into an `AST` using own scope, which can be used later for evaluation.
+ /// Compile a string into an [`AST`] using own scope, which can be used later for evaluation.
///
/// The scope is useful for passing constants into the script for optimization
/// when using `OptimizationLevel::Full`.
@@ -488,7 +488,7 @@ impl Engine {
}
/// When passed a list of strings, first join the strings into one large script,
- /// and then compile them into an `AST` using own scope, which can be used later for evaluation.
+ /// and then compile them into an [`AST`] using own scope, which can be used later for evaluation.
///
/// The scope is useful for passing constants into the script for optimization
/// when using `OptimizationLevel::Full`.
@@ -541,7 +541,7 @@ impl Engine {
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
}
- /// Join a list of strings and compile into an `AST` using own scope at a specific optimization level.
+ /// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
pub(crate) fn compile_with_scope_and_optimization_level(
&self,
scope: &Scope,
@@ -577,7 +577,7 @@ impl Engine {
Ok(contents)
}
- /// Compile a script file into an `AST`, which can be used later for evaluation.
+ /// Compile a script file into an [`AST`], which can be used later for evaluation.
///
/// # Example
///
@@ -603,7 +603,7 @@ impl Engine {
self.compile_file_with_scope(&Scope::new(), path)
}
- /// Compile a script file into an `AST` using own scope, which can be used later for evaluation.
+ /// Compile a script file into an [`AST`] using own scope, which can be used later for evaluation.
///
/// The scope is useful for passing constants into the script for optimization
/// when using `OptimizationLevel::Full`.
@@ -685,7 +685,7 @@ impl Engine {
self.eval_ast_with_scope(&mut scope, &ast)
}
- /// Compile a string containing an expression into an `AST`,
+ /// Compile a string containing an expression into an [`AST`],
/// which can be used later for evaluation.
///
/// # Example
@@ -709,7 +709,7 @@ impl Engine {
self.compile_expression_with_scope(&Scope::new(), script)
}
- /// Compile a string containing an expression into an `AST` using own scope,
+ /// Compile a string containing an expression into an [`AST`] using own scope,
/// which can be used later for evaluation.
///
/// The scope is useful for passing constants into the script for optimization
@@ -917,7 +917,7 @@ impl Engine {
self.eval_ast_with_scope(scope, &ast)
}
- /// Evaluate an `AST`.
+ /// Evaluate an [`AST`].
///
/// # Example
///
@@ -939,7 +939,7 @@ impl Engine {
self.eval_ast_with_scope(&mut Scope::new(), ast)
}
- /// Evaluate an `AST` with own scope.
+ /// Evaluate an [`AST`] with own scope.
///
/// # Example
///
@@ -986,7 +986,7 @@ impl Engine {
});
}
- /// Evaluate an `AST` with own scope.
+ /// Evaluate an [`AST`] with own scope.
pub(crate) fn eval_ast_with_scope_raw<'a>(
&self,
scope: &mut Scope,
@@ -1052,7 +1052,7 @@ impl Engine {
self.consume_ast_with_scope(&mut Scope::new(), ast)
}
- /// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
+ /// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
pub fn consume_ast_with_scope(
&self,
@@ -1076,7 +1076,7 @@ impl Engine {
)
}
- /// Call a script function defined in an `AST` with multiple arguments.
+ /// Call a script function defined in an [`AST`] with multiple arguments.
/// Arguments are passed as a tuple.
///
/// # Example
@@ -1133,7 +1133,7 @@ impl Engine {
});
}
- /// Call a script function defined in an `AST` with multiple `Dynamic` arguments.
+ /// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments.
///
/// # Example
///
@@ -1179,7 +1179,7 @@ impl Engine {
self.call_fn_dynamic_raw(scope, ast, name, arg_values.as_mut())
}
- /// Call a script function defined in an `AST` with multiple `Dynamic` arguments.
+ /// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments.
///
/// ## WARNING
///
@@ -1220,15 +1220,15 @@ impl Engine {
)
}
- /// Optimize the `AST` with constants defined in an external Scope.
- /// An optimized copy of the `AST` is returned while the original `AST` is consumed.
+ /// Optimize the [`AST`] with constants defined in an external Scope.
+ /// An optimized copy of the [`AST`] is returned while the original [`AST`] is consumed.
///
/// Although optimization is performed by default during compilation, sometimes it is necessary to
/// _re_-optimize an AST. For example, when working with constants that are passed in via an
- /// external scope, it will be more efficient to optimize the `AST` once again to take advantage
+ /// external scope, it will be more efficient to optimize the [`AST`] once again to take advantage
/// of the new constants.
///
- /// With this method, it is no longer necessary to recompile a large script. The script `AST` can be
+ /// With this method, it is no longer necessary to recompile a large script. The script [`AST`] can be
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
#[cfg(not(feature = "no_optimize"))]
diff --git a/src/engine.rs b/src/engine.rs
index 516f34fb..65c91735 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -8,10 +8,10 @@ use crate::module::{resolvers, Module, ModuleRef, ModuleResolver};
use crate::optimize::OptimizationLevel;
use crate::packages::{Package, PackageLibrary, PackagesCollection, StandardPackage};
use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, Stmt, AST, INT};
-use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
+use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope};
-use crate::token::Position;
+use crate::token::{is_valid_identifier, Position};
use crate::utils::StaticVec;
#[cfg(not(feature = "no_float"))]
@@ -75,6 +75,7 @@ pub const KEYWORD_PRINT: &str = "print";
pub const KEYWORD_DEBUG: &str = "debug";
pub const KEYWORD_TYPE_OF: &str = "type_of";
pub const KEYWORD_EVAL: &str = "eval";
+pub const KEYWORD_FN_PTR: &str = "Fn";
pub const KEYWORD_FN_PTR_CALL: &str = "call";
pub const KEYWORD_THIS: &str = "this";
pub const FN_TO_STRING: &str = "to_string";
@@ -82,7 +83,6 @@ pub const FN_GET: &str = "get$";
pub const FN_SET: &str = "set$";
pub const FN_IDX_GET: &str = "$index$get$";
pub const FN_IDX_SET: &str = "$index$set$";
-pub const FN_FN_PTR: &str = "Fn";
/// A type specifying the method of chaining.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@@ -729,10 +729,17 @@ impl Engine {
// Replace the first reference with a reference to the clone, force-casting the lifetime.
// Keep the original reference. Must remember to restore it later with `restore_first_arg_of_method_call`.
- let this_pointer = mem::replace(
- args.get_mut(0).unwrap(),
- unsafe_mut_cast_to_lifetime(this_copy),
- );
+ //
+ // # Safety
+ //
+ // Blindly casting a a reference to another lifetime saves on allocations and string cloning,
+ // but must be used with the utmost care.
+ //
+ // We can do this here because, at the end of this scope, we'd restore the original reference
+ // with `restore_first_arg_of_method_call`. Therefore this shorter lifetime does not get "out".
+ let this_pointer = mem::replace(args.get_mut(0).unwrap(), unsafe {
+ mem::transmute(this_copy)
+ });
*old_this_ptr = Some(this_pointer);
}
@@ -1020,7 +1027,7 @@ impl Engine {
)),
// Fn
- FN_FN_PTR if args.len() == 1 && !self.has_override(lib, hashes) => {
+ KEYWORD_FN_PTR if args.len() == 1 && !self.has_override(lib, hashes) => {
Err(Box::new(EvalAltResult::ErrorRuntime(
"'Fn' should not be called in method style. Try Fn(...);".into(),
Position::none(),
@@ -1133,7 +1140,7 @@ impl Engine {
state, lib, this_ptr, obj_ptr, expr, idx_values, next_chain, level,
new_val,
)
- .map_err(|err| EvalAltResult::new_position(err, *pos))
+ .map_err(|err| err.new_position(*pos))
}
// xxx[rhs] = new_val
_ if new_val.is_some() => {
@@ -1162,9 +1169,9 @@ impl Engine {
}
// Indexed value is a reference - update directly
Ok(ref mut obj_ptr) => {
- obj_ptr.set_value(new_val.unwrap()).map_err(|err| {
- EvalAltResult::new_position(err, rhs.position())
- })?;
+ obj_ptr
+ .set_value(new_val.unwrap())
+ .map_err(|err| err.new_position(rhs.position()))?;
}
Err(err) => match *err {
// No index getter - try to call an index setter
@@ -1205,11 +1212,12 @@ impl Engine {
let (result, updated) = {
let obj = target.as_mut();
let idx = idx_val.downcast_mut::>().unwrap();
+ let mut fn_name = name.as_ref();
// Check if it is a FnPtr call
- if name == KEYWORD_FN_PTR_CALL && obj.is::() {
+ if fn_name == KEYWORD_FN_PTR_CALL && obj.is::() {
// Redirect function name
- let fn_name = obj.as_fn_name().unwrap();
+ fn_name = obj.as_str().unwrap();
// Recalculate hash
let hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
// Arguments are passed as-is
@@ -1222,25 +1230,20 @@ impl Engine {
def_val, level,
)
} else {
- let mut fn_name = name.clone();
- let mut redirected = None;
+ let redirected: Option;
let mut hash = *hash;
// Check if it is a map method call in OOP style
if let Some(map) = obj.downcast_ref::