Remove book.
@ -1,41 +0,0 @@
|
|||||||
The Rhai Book
|
|
||||||
=============
|
|
||||||
|
|
||||||
[_The Rhai Book_](https://rhaiscript.github.io/book) serves as Rhai's primary
|
|
||||||
documentation and tutorial resource.
|
|
||||||
|
|
||||||
|
|
||||||
How to Build from Source
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
* Install [`mdbook`](https://github.com/rust-lang/mdBook):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo install mdbook
|
|
||||||
```
|
|
||||||
|
|
||||||
* Install [`mdbook-tera`](https://github.com/avitex/mdbook-tera) (for templating):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo install mdbook-tera
|
|
||||||
```
|
|
||||||
|
|
||||||
* Run build in source directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd doc
|
|
||||||
mdbook build
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Configuration Settings
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Settings stored in `context.json`:
|
|
||||||
|
|
||||||
| Setting | Description |
|
|
||||||
| ---------- | -------------------------------------------------------------------------------------------- |
|
|
||||||
| `version` | version of Rhai |
|
|
||||||
| `repoHome` | points to the [root of the GitHub repo](https://github.com/rhaiscript/rhai/blob/master) |
|
|
||||||
| `repoTree` | points to the [root of the GitHub repo tree](https://github.com/rhaiscript/rhai/tree/master) |
|
|
||||||
| `rootUrl` | sub-directory for the root domain, e.g. `/rhai` |
|
|
@ -1,22 +0,0 @@
|
|||||||
[book]
|
|
||||||
title = "Rhai - Embedded Scripting for Rust"
|
|
||||||
authors = ["Jonathan Turner", "Stephen Chung"]
|
|
||||||
description = "Tutorial and reference on the Rhai scripting engine and language."
|
|
||||||
language = "en"
|
|
||||||
|
|
||||||
[output.html]
|
|
||||||
no-section-label = true
|
|
||||||
git-repository-url = "https://github.com/rhaiscript/rhai"
|
|
||||||
curly-quotes = true
|
|
||||||
|
|
||||||
[output.html.fold]
|
|
||||||
enable = true
|
|
||||||
level = 4
|
|
||||||
|
|
||||||
[outputX.linkcheck]
|
|
||||||
follow-web-links = false
|
|
||||||
traverse-parent-directories = false
|
|
||||||
warning-policy = "ignore"
|
|
||||||
|
|
||||||
[preprocessor.tera]
|
|
||||||
command = "mdbook-tera --json ./src/context.json"
|
|
@ -1,149 +0,0 @@
|
|||||||
The Rhai Scripting Language
|
|
||||||
==========================
|
|
||||||
|
|
||||||
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. [Licensing](about/license.md)
|
|
||||||
5. [Related Resources](about/related.md)
|
|
||||||
2. [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)
|
|
||||||
5. [Packaged Utilities](start/bin.md)
|
|
||||||
6. [Examples](start/examples/index.md)
|
|
||||||
1. [Rust](start/examples/rust.md)
|
|
||||||
2. [Scripts](start/examples/scripts.md)
|
|
||||||
3. [Using the `Engine`](engine/index.md)
|
|
||||||
1. [Hello World in Rhai – Evaluate a Script](engine/hello-world.md)
|
|
||||||
2. [Compile to AST for Repeated Evaluations](engine/compile.md)
|
|
||||||
3. [Call a Rhai Function from Rust](engine/call-fn.md)
|
|
||||||
4. [Create a Rust Closure from a Rhai Function](engine/func.md)
|
|
||||||
5. [Evaluate Expressions Only](engine/expressions.md)
|
|
||||||
6. [Raw Engine](engine/raw.md)
|
|
||||||
7. [Scope – Initializing and Maintaining State](engine/scope.md)
|
|
||||||
8. [Engine Configuration Options](engine/options.md)
|
|
||||||
4. [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. [Override a Built-in Function](rust/override.md)
|
|
||||||
6. [Operator Overloading](rust/operators.md)
|
|
||||||
7. [Register any Rust Type and its Methods](rust/custom.md)
|
|
||||||
1. [Property Getters and Setters](rust/getters-setters.md)
|
|
||||||
2. [Indexers](rust/indexers.md)
|
|
||||||
3. [Disable Custom Types](rust/disable-custom.md)
|
|
||||||
4. [Printing Custom Types](rust/print-custom.md)
|
|
||||||
8. [Modules](rust/modules/index.md)
|
|
||||||
1. [Create from Rust](rust/modules/create.md)
|
|
||||||
2. [Create from AST](rust/modules/ast.md)
|
|
||||||
3. [Module Resolvers](rust/modules/resolvers.md)
|
|
||||||
1. [Custom Module Resolvers](rust/modules/imp-resolver.md)
|
|
||||||
9. [Plugins](plugins/index.md)
|
|
||||||
1. [Export a Rust Module](plugins/module.md)
|
|
||||||
2. [Export a Rust Function](plugins/function.md)
|
|
||||||
10. [Packages](rust/packages/index.md)
|
|
||||||
1. [Built-in Packages](rust/packages/builtin.md)
|
|
||||||
2. [Custom Packages](rust/packages/create.md)
|
|
||||||
5. [Rhai Language Reference](language/index.md)
|
|
||||||
1. [Comments](language/comments.md)
|
|
||||||
1. [Doc-Comments](language/doc-comments.md)
|
|
||||||
2. [Values and Types](language/values-and-types.md)
|
|
||||||
1. [Dynamic Values](language/dynamic.md)
|
|
||||||
2. [Serialization/Deserialization with `serde`](rust/serde.md)
|
|
||||||
3. [type_of()](language/type-of.md)
|
|
||||||
4. [Numbers](language/numbers.md)
|
|
||||||
1. [Operators](language/num-op.md)
|
|
||||||
2. [Functions](language/num-fn.md)
|
|
||||||
3. [Value Conversions](language/convert.md)
|
|
||||||
5. [Strings and Characters](language/strings-chars.md)
|
|
||||||
1. [Built-in Functions](language/string-fn.md)
|
|
||||||
6. [Arrays](language/arrays.md)
|
|
||||||
7. [Object Maps](language/object-maps.md)
|
|
||||||
1. [Parse from JSON](language/json.md)
|
|
||||||
2. [Special Support for OOP](language/object-maps-oop.md)
|
|
||||||
8. [Time-Stamps](language/timestamps.md)
|
|
||||||
3. [Keywords](language/keywords.md)
|
|
||||||
4. [Statements](language/statements.md)
|
|
||||||
5. [Variables](language/variables.md)
|
|
||||||
6. [Constants](language/constants.md)
|
|
||||||
7. [Logic Operators](language/logic.md)
|
|
||||||
8. [Assignment Operators](language/assignment-op.md)
|
|
||||||
9. [If Statement](language/if.md)
|
|
||||||
10. [Switch Expression](language/switch.md)
|
|
||||||
11. [While Loop](language/while.md)
|
|
||||||
12. [Do Loop](language/do.md)
|
|
||||||
13. [Loop Statement](language/loop.md)
|
|
||||||
14. [For Loop](language/for.md)
|
|
||||||
1. [Iterators for Custom Types](language/iterator.md)
|
|
||||||
15. [Return Values](language/return.md)
|
|
||||||
16. [Throw Exception on Error](language/throw.md)
|
|
||||||
17. [Catch Exceptions](language/try-catch.md)
|
|
||||||
18. [Functions](language/functions.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)
|
|
||||||
5. [Currying](language/fn-curry.md)
|
|
||||||
6. [Anonymous Functions](language/fn-anon.md)
|
|
||||||
7. [Closures](language/fn-closure.md)
|
|
||||||
19. [Print and Debug](language/print-debug.md)
|
|
||||||
20. [Modules](language/modules/index.md)
|
|
||||||
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
|
||||||
2. [Import Modules](language/modules/import.md)
|
|
||||||
21. [Eval Function](language/eval.md)
|
|
||||||
6. [Safety and Protection](safety/index.md)
|
|
||||||
1. [Checked Arithmetic](safety/checked.md)
|
|
||||||
2. [Sand-Boxing](safety/sandbox.md)
|
|
||||||
3. [Maximum Length of Strings](safety/max-string-size.md)
|
|
||||||
4. [Maximum Size of Arrays](safety/max-array-size.md)
|
|
||||||
5. [Maximum Size of Object Maps](safety/max-map-size.md)
|
|
||||||
6. [Maximum Number of Operations](safety/max-operations.md)
|
|
||||||
1. [Tracking Progress and Force-Termination](safety/progress.md)
|
|
||||||
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. [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)
|
|
||||||
8. [Usage Patterns](patterns/index.md)
|
|
||||||
1. [Object-Oriented Programming (OOP)](patterns/oop.md)
|
|
||||||
2. [Working With Rust Enums](patterns/enums.md)
|
|
||||||
3. [Loadable Configuration](patterns/config.md)
|
|
||||||
4. [Control Layer](patterns/control.md)
|
|
||||||
5. [Singleton Command](patterns/singleton.md)
|
|
||||||
6. [Multi-Layer Functions](patterns/multi-layer.md)
|
|
||||||
7. [One Engine Instance Per Call](patterns/parallel.md)
|
|
||||||
8. [Scriptable Event Handler with State](patterns/events.md)
|
|
||||||
9. [Dynamic Constants Provider](patterns/dynamic-const.md)
|
|
||||||
9. [Advanced Topics](advanced.md)
|
|
||||||
1. [Capture Scope for Function Call](language/fn-capture.md)
|
|
||||||
2. [Low-Level API](rust/register-raw.md)
|
|
||||||
3. [Variable Resolver](engine/var.md)
|
|
||||||
4. [Use as DSL](engine/dsl.md)
|
|
||||||
1. [Disable Keywords and/or Operators](engine/disable.md)
|
|
||||||
2. [Custom Operators](engine/custom-op.md)
|
|
||||||
3. [Extending with Custom Syntax](engine/custom-syntax.md)
|
|
||||||
5. [Multiple Instantiation](patterns/multiple.md)
|
|
||||||
6. [Functions Metadata](engine/metadata/index.md)
|
|
||||||
1. [Generate Function Signatures](engine/metadata/gen_fn_sig.md)
|
|
||||||
2. [Export Metadata to JSON](engine/metadata/export_to_json.md)
|
|
||||||
10. [External Tools](tools/index.md)
|
|
||||||
1. [Online Playground](tools/playground.md)
|
|
||||||
2. [`rhai-doc`](tools/rhai-doc.md)
|
|
||||||
11. [Appendix](appendix/index.md)
|
|
||||||
1. [Keywords](appendix/keywords.md)
|
|
||||||
2. [Operators and Symbols](appendix/operators.md)
|
|
||||||
3. [Literals](appendix/literals.md)
|
|
@ -1,79 +0,0 @@
|
|||||||
Features
|
|
||||||
========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
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],
|
|
||||||
[methods][custom type] and [indexers].
|
|
||||||
|
|
||||||
* Freely pass Rust variables/constants into a script via an external [`Scope`] – all clonable Rust types are supported seamlessly
|
|
||||||
without the need to implement any special trait.
|
|
||||||
|
|
||||||
* Easily [call a script-defined function]({{rootUrl}}/engine/call-fn.md) from Rust.
|
|
||||||
|
|
||||||
* Very few additional dependencies – right now only [`smallvec`](https://crates.io/crates/smallvec/) plus crates for procedural macros;
|
|
||||||
for [`no-std`] and `WASM` builds, a number of additional dependencies are pulled in to provide for missing functionalities.
|
|
||||||
|
|
||||||
* [Plugins] system powered by procedural macros simplifies custom API development.
|
|
||||||
|
|
||||||
Fast
|
|
||||||
----
|
|
||||||
|
|
||||||
* Fairly low compile-time overhead.
|
|
||||||
|
|
||||||
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single core, 2.3 GHz Linux VM).
|
|
||||||
|
|
||||||
* Scripts are [optimized][script optimization] (useful for template-based machine-generated scripts) for repeated evaluations.
|
|
||||||
|
|
||||||
Dynamic
|
|
||||||
-------
|
|
||||||
|
|
||||||
* [Function overloading]({{rootUrl}}/language/overload.md).
|
|
||||||
|
|
||||||
* [Operator overloading]({{rootUrl}}/rust/operators.md).
|
|
||||||
|
|
||||||
* Organize code base with dynamically-loadable [modules].
|
|
||||||
|
|
||||||
* Dynamic dispatch via [function pointers] with additional support for [currying].
|
|
||||||
|
|
||||||
* [Closures] that can capture shared variables.
|
|
||||||
|
|
||||||
* Some support for [object-oriented programming (OOP)][OOP].
|
|
||||||
|
|
||||||
* Hook into variables access via [variable resolver].
|
|
||||||
|
|
||||||
Safe
|
|
||||||
----
|
|
||||||
|
|
||||||
* Relatively little `unsafe` code (yes there are some for performance reasons).
|
|
||||||
|
|
||||||
* Sand-boxed – the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless
|
|
||||||
[explicitly permitted]({{rootUrl}}/patterns/control.md).
|
|
||||||
|
|
||||||
Rugged
|
|
||||||
------
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
Flexible
|
|
||||||
--------
|
|
||||||
|
|
||||||
* Re-entrant scripting [`Engine`] can be made `Send + Sync` (via the [`sync`] feature).
|
|
||||||
|
|
||||||
* Serialization/deserialization support via [`serde`](https://crates.io/crates/serde).
|
|
||||||
|
|
||||||
* Support for [minimal builds] by excluding unneeded language [features].
|
|
||||||
|
|
||||||
* Supports [most build targets](targets.md) including `no-std` and [WASM].
|
|
||||||
|
|
||||||
* Surgically [disable keywords and operators] to restrict the language.
|
|
||||||
|
|
||||||
* Use as a [DSL] by defining [custom operators] and/or extending the language with [custom syntax].
|
|
@ -1,46 +0,0 @@
|
|||||||
What is Rhai
|
|
||||||
============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
![Rhai Logo]({{rootUrl}}/images/logo/rhai-banner-transparent-colour.svg)
|
|
||||||
|
|
||||||
Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way
|
|
||||||
to add scripting to any application.
|
|
||||||
|
|
||||||
|
|
||||||
Versions
|
|
||||||
--------
|
|
||||||
|
|
||||||
This Book is for version **{{version}}** of Rhai.
|
|
||||||
|
|
||||||
{% if rootUrl != "" and not rootUrl is ending_with("vnext") %}
|
|
||||||
For the latest development version, see [here]({{rootUrl}}/vnext/).
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
Etymology of the name "Rhai"
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
### As per Rhai's author Johnathan Turner
|
|
||||||
|
|
||||||
In the beginning there was [ChaiScript](http://chaiscript.com),
|
|
||||||
which is an embedded scripting language for C++.
|
|
||||||
Originally it was intended to be a scripting language similar to **JavaScript**.
|
|
||||||
|
|
||||||
With java being a kind of hot beverage, the new language was named after
|
|
||||||
another hot beverage – **Chai**, which is the word for "tea" in many world languages
|
|
||||||
and, in particular, a popular kind of milk tea consumed in India.
|
|
||||||
|
|
||||||
Later, when the novel implementation technique behind ChaiScript was ported from C++ to Rust,
|
|
||||||
logically the `C` was changed to an `R` to make it "RhaiScript", or just "Rhai".
|
|
||||||
|
|
||||||
### On the origin of the semi-official Rhai logo
|
|
||||||
|
|
||||||
One of Rhai's maintainers, [Stephen Chung](https://github.com/schungx), was thinking about a logo when he accidentally
|
|
||||||
came across a copy of _Catcher in the Rye_ in a restaurant, and drew the first version
|
|
||||||
of the logo.
|
|
||||||
|
|
||||||
Then [`@semirix`](https://github.com/semirix) refined it to the current version.
|
|
||||||
|
|
||||||
The plan is to make the logo official together with a `1.0` release.
|
|
@ -1,14 +0,0 @@
|
|||||||
Licensing
|
|
||||||
=========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Rhai is licensed under either of the following, at your choice:
|
|
||||||
|
|
||||||
* [Apache License, Version 2.0]({{repoHome}}/LICENSE-APACHE.txt), or
|
|
||||||
|
|
||||||
* [MIT license]({{repoHome}}/LICENSE-MIT.txt).
|
|
||||||
|
|
||||||
Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate,
|
|
||||||
as defined in the Apache-2.0 license, shall be dual-licensed as above,
|
|
||||||
without any additional terms or conditions.
|
|
@ -1,77 +0,0 @@
|
|||||||
What Rhai Isn't
|
|
||||||
===============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Rhai's purpose is to provide a dynamic layer over Rust code, in the same spirit of _zero cost abstractions_.
|
|
||||||
It doesn't attempt to be a new language. For example:
|
|
||||||
|
|
||||||
* **No classes**. Well, Rust doesn't either. On the other hand...
|
|
||||||
|
|
||||||
* **No traits**... so it is also not Rust. Do your Rusty stuff in Rust.
|
|
||||||
|
|
||||||
* **No structures/records/tuples** – 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]
|
|
||||||
or [closures] 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] to allow runtime dispatch by function name.
|
|
||||||
|
|
||||||
* **No garbage collection** – this should be expected, so...
|
|
||||||
|
|
||||||
* **No first-class closures** – do your closure magic in Rust instead: [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
|
|
||||||
|
|
||||||
There is, however, support for simulated [closures] via [currying] a [function pointer] with
|
|
||||||
captured shared variables.
|
|
||||||
|
|
||||||
* **No byte-codes/JIT** – Rhai has an optimized AST-walking interpreter which is fast enough for most casual
|
|
||||||
usage scenarios. Essential AST data structures are packed and kept together to maximize cache friendliness.
|
|
||||||
|
|
||||||
Functions are dispatched based on pre-calculated hashes and accessing variables are mostly through pre-calculated
|
|
||||||
offsets to the variables file (a [`Scope`]), so it is seldom necessary to look something up by text name.
|
|
||||||
|
|
||||||
In addition, Rhai's design deliberately avoids maintaining a _scope chain_ so function scopes do not
|
|
||||||
pay any speed penalty. This particular design also allows variables data to be kept together in a contiguous
|
|
||||||
block, avoiding allocations and fragmentation while being cache-friendly. In a typical script evaluation run,
|
|
||||||
no data is shared and nothing is locked.
|
|
||||||
|
|
||||||
Still, the purpose of Rhai is not to be super _fast_, but to make it as easy and versatile as possible to
|
|
||||||
integrate with native Rust applications.
|
|
||||||
|
|
||||||
* **No formal language grammar** – Rhai uses a hand-coded lexer, a hand-coded top-down recursive-descent parser
|
|
||||||
for statements, and a hand-coded Pratt parser for expressions.
|
|
||||||
|
|
||||||
This lack of formalism allows the _tokenizer_ and _parser_ themselves to be exposed as services in order
|
|
||||||
to support [disabling keywords/operators][disable keywords and operators], adding [custom operators],
|
|
||||||
and defining [custom syntax].
|
|
||||||
|
|
||||||
|
|
||||||
Do Not Write The Next 4D VR Game in Rhai
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting
|
|
||||||
advanced language features such as classes, inheritance, interfaces, generics,
|
|
||||||
first-class functions/closures, pattern matching, concurrency, byte-codes VM, JIT etc.
|
|
||||||
Focus is on _flexibility_ and _ease of use_ instead of raw speed.
|
|
||||||
|
|
||||||
Avoid the temptation to write full-fledge application logic entirely in Rhai -
|
|
||||||
that use case is best fulfilled by more complete languages such as JavaScript or Lua.
|
|
||||||
|
|
||||||
|
|
||||||
Thin Dynamic Wrapper Layer Over Rust Code
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
In actual practice, it is usually best to expose a Rust API into Rhai for scripts to call.
|
|
||||||
|
|
||||||
All the core functionalities should be written in Rust, with Rhai being the dynamic _control_ layer.
|
|
||||||
|
|
||||||
This is similar to some dynamic languages where most of the core functionalities reside in a C/C++
|
|
||||||
standard library.
|
|
||||||
|
|
||||||
Another similar scenario is a web front-end driving back-end services written in a systems language.
|
|
||||||
In this case, JavaScript takes the role of Rhai while the back-end language, well... it can actually also be Rust.
|
|
||||||
Except that Rhai integrates with Rust _much_ more tightly, removing the need for interfaces such
|
|
||||||
as XHR calls and payload encoding such as JSON.
|
|
@ -1,36 +0,0 @@
|
|||||||
Related Resources
|
|
||||||
=================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
Online Resources for Rhai
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
* [GitHub](https://github.com/rhaiscript/rhai) – Home repository
|
|
||||||
|
|
||||||
* [`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
|
|
||||||
|
|
||||||
* [Discord Chat](https://discord.gg/HquqbYFcZ9) – Rhai channel
|
|
||||||
|
|
||||||
* [Reddit](https://www.reddit.com/r/Rhai) – Rhai community
|
|
||||||
|
|
||||||
|
|
||||||
External Tools
|
|
||||||
--------------
|
|
||||||
|
|
||||||
* [Online Playground][playground] – Run Rhai scripts directly from an editor in the browser
|
|
||||||
|
|
||||||
* [`rhai-doc`] – Rhai script documentation tool
|
|
||||||
|
|
||||||
|
|
||||||
Other Cool Projects
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
* [ChaiScript](http://chaiscript.com) – A strong inspiration for Rhai. An embedded scripting language for C++.
|
|
||||||
|
|
||||||
* Check out the list of [scripting languages for Rust](https://github.com/rust-unofficial/awesome-rust#scripting) on [awesome-rust](https://github.com/rust-unofficial/awesome-rust)
|
|
@ -1,18 +0,0 @@
|
|||||||
Supported Targets and Builds
|
|
||||||
===========================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
The following targets and builds are support by Rhai:
|
|
||||||
|
|
||||||
* All common CPU targets for Windows, Linux and MacOS.
|
|
||||||
|
|
||||||
* WebAssembly ([WASM])
|
|
||||||
|
|
||||||
* [`no-std`]
|
|
||||||
|
|
||||||
|
|
||||||
Minimum Rust Version
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
The minimum version of Rust required to compile Rhai is `1.45.0`.
|
|
@ -1,6 +0,0 @@
|
|||||||
Advanced Topics
|
|
||||||
===============
|
|
||||||
|
|
||||||
{{#include links.md}}
|
|
||||||
|
|
||||||
This section covers advanced features of the Rhai [`Engine`].
|
|
@ -1,6 +0,0 @@
|
|||||||
Appendix
|
|
||||||
========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
This section contains miscellaneous reference materials.
|
|
@ -1,75 +0,0 @@
|
|||||||
Keywords List
|
|
||||||
=============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
| Keyword | Description | Inactive under | Is function? | Overloadable |
|
|
||||||
| :-------------------: | ------------------------------------------- | :-------------: | :----------: | :----------: |
|
|
||||||
| `true` | boolean true literal | | no | |
|
|
||||||
| `false` | boolean false literal | | no | |
|
|
||||||
| `let` | variable declaration | | no | |
|
|
||||||
| `const` | constant declaration | | no | |
|
|
||||||
| `if` | if statement | | no | |
|
|
||||||
| `else` | else block of if statement | | no | |
|
|
||||||
| `switch` | matching | | no | |
|
|
||||||
| `do` | looping | | no | |
|
|
||||||
| `while` | 1) while loop<br/>2) condition for do loop | | no | |
|
|
||||||
| `until` | do loop | | no | |
|
|
||||||
| `loop` | infinite loop | | no | |
|
|
||||||
| `for` | for loop | | no | |
|
|
||||||
| `in` | 1) containment test<br/>2) part of for loop | | no | |
|
|
||||||
| `continue` | continue a loop at the next iteration | | no | |
|
|
||||||
| `break` | break out of loop iteration | | no | |
|
|
||||||
| `return` | return value | | no | |
|
|
||||||
| `throw` | throw exception | | no | |
|
|
||||||
| `try` | trap exception | | no | |
|
|
||||||
| `catch` | catch exception | | no | |
|
|
||||||
| `import` | import module | [`no_module`] | no | |
|
|
||||||
| `export` | export variable | [`no_module`] | no | |
|
|
||||||
| `as` | alias for variable export | [`no_module`] | no | |
|
|
||||||
| `private` | mark function private | [`no_function`] | no | |
|
|
||||||
| `fn` (lower-case `f`) | function definition | [`no_function`] | no | |
|
|
||||||
| `Fn` (capital `F`) | create a [function pointer] | | yes | yes |
|
|
||||||
| `call` | call a [function pointer] | | yes | no |
|
|
||||||
| `curry` | curry a [function pointer] | | yes | no |
|
|
||||||
| `this` | reference to base object for method call | [`no_function`] | no | |
|
|
||||||
| `type_of` | get type name of value | | yes | yes |
|
|
||||||
| `print` | print value | | yes | yes |
|
|
||||||
| `debug` | print value in debug format | | yes | yes |
|
|
||||||
| `eval` | evaluate script | | yes | yes |
|
|
||||||
|
|
||||||
|
|
||||||
Reserved Keywords
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
| Keyword | Potential usage |
|
|
||||||
| --------- | --------------------- |
|
|
||||||
| `var` | variable declaration |
|
|
||||||
| `static` | variable declaration |
|
|
||||||
| `begin` | block scope |
|
|
||||||
| `end` | block scope |
|
|
||||||
| `shared` | share value |
|
|
||||||
| `each` | looping |
|
|
||||||
| `then` | control flow |
|
|
||||||
| `goto` | control flow |
|
|
||||||
| `exit` | control flow |
|
|
||||||
| `unless` | control flow |
|
|
||||||
| `match` | matching |
|
|
||||||
| `case` | matching |
|
|
||||||
| `public` | function/field access |
|
|
||||||
| `new` | constructor |
|
|
||||||
| `use` | import namespace |
|
|
||||||
| `with` | scope |
|
|
||||||
| `module` | module |
|
|
||||||
| `package` | package |
|
|
||||||
| `thread` | threading |
|
|
||||||
| `spawn` | threading |
|
|
||||||
| `go` | threading |
|
|
||||||
| `await` | async |
|
|
||||||
| `async` | async |
|
|
||||||
| `sync` | async |
|
|
||||||
| `yield` | async |
|
|
||||||
| `default` | special value |
|
|
||||||
| `void` | special value |
|
|
||||||
| `null` | special value |
|
|
||||||
| `nil` | special value |
|
|
@ -1,16 +0,0 @@
|
|||||||
Literals Syntax
|
|
||||||
===============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
| Type | Literal syntax |
|
|
||||||
| :--------------------------------: | :-----------------------------------------------------------------------------------------: |
|
|
||||||
| `INT` | decimal: `42`, `-123`, `0`<br/>hex: `0x????..`<br/>binary: `0b????..`<br/>octal: `0o????..` |
|
|
||||||
| `FLOAT` | `42.0`, `-123.456`, `0.0` |
|
|
||||||
| [String] | `"... \x?? \u???? \U???????? ..."` |
|
|
||||||
| [Character][string] | single: `'?'`<br/>ASCII hex: `'\x??'`<br/>Unicode: `'\u????'`, `'\U????????'` |
|
|
||||||
| [`Array`] | `[ ???, ???, ??? ]` |
|
|
||||||
| [Object map] | `#{ a: ???, b: ???, c: ???, "def": ??? }` |
|
|
||||||
| Boolean true | `true` |
|
|
||||||
| Boolean false | `false` |
|
|
||||||
| `Nothing`/`null`/`nil`/`void`/Unit | `()` |
|
|
@ -1,74 +0,0 @@
|
|||||||
Operators and Symbols
|
|
||||||
====================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
Operators
|
|
||||||
---------
|
|
||||||
|
|
||||||
| Operator | Description | Binary? | Binding direction |
|
|
||||||
| :-----------------------------------------------------------------------------------------: | -------------------------------------- | :--------: | :---------------: |
|
|
||||||
| `+` | add | yes | left |
|
|
||||||
| `-` | 1) subtract<br/>2) negative (prefix) | yes<br/>no | left<br/>right |
|
|
||||||
| `*` | multiply | yes | left |
|
|
||||||
| `/` | divide | yes | left |
|
|
||||||
| `%` | modulo | yes | left |
|
|
||||||
| `~` | power | yes | left |
|
|
||||||
| `>>` | right bit-shift | yes | left |
|
|
||||||
| `<<` | left bit-shift | yes | left |
|
|
||||||
| `&` | 1) bit-wise _AND_<br/>2) boolean _AND_ | yes | left |
|
|
||||||
| <code>\|</code> | 1) bit-wise _OR_<br/>2) boolean _OR_ | yes | left |
|
|
||||||
| `^` | 1) bit-wise _XOR_<br/>2) boolean _XOR_ | yes | left |
|
|
||||||
| `=`, `+=`, `-=`, `*=`, `/=`,<br/>`~=`, `%=`, `<<=`, `>>=`, `&=`,<br/><code>\|=</code>, `^=` | assignments | yes | right |
|
|
||||||
| `==` | equals to | yes | left |
|
|
||||||
| `~=` | not equals to | yes | left |
|
|
||||||
| `>` | greater than | yes | left |
|
|
||||||
| `>=` | greater than or equals to | yes | left |
|
|
||||||
| `<` | less than | yes | left |
|
|
||||||
| `<=` | less than or equals to | yes | left |
|
|
||||||
| `&&` | boolean _AND_ (short-circuits) | yes | left |
|
|
||||||
| <code>\|\|</code> | boolean _OR_ (short-circuits) | yes | left |
|
|
||||||
| `!` | boolean _NOT_ | no | left |
|
|
||||||
| `[` .. `]` | indexing | yes | right |
|
|
||||||
| `.` | 1) property access<br/>2) method call | yes | right |
|
|
||||||
|
|
||||||
|
|
||||||
Symbols and Patterns
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
| Symbol | Name | Description |
|
|
||||||
| ---------------------------------- | :------------------: | ------------------------------------- |
|
|
||||||
| `_` | underscore | default `switch` case |
|
|
||||||
| `;` | semicolon | statement separator |
|
|
||||||
| `,` | comma | list separator |
|
|
||||||
| `:` | colon | [object map] property value separator |
|
|
||||||
| `::` | path | module path separator |
|
|
||||||
| `#{` .. `}` | hash map | [object map] literal |
|
|
||||||
| `"` .. `"` | double quote | [string] |
|
|
||||||
| `'` .. `'` | single quote | [character][string] |
|
|
||||||
| `\` | escape | escape character literal |
|
|
||||||
| `(` .. `)` | parentheses | expression grouping |
|
|
||||||
| `{` .. `}` | braces | block statement |
|
|
||||||
| <code>\|</code> .. <code>\|</code> | pipes | closure |
|
|
||||||
| `[` .. `]` | brackets | [array] literal |
|
|
||||||
| `!` | bang | function call in calling scope |
|
|
||||||
| `=>` | double arrow | `switch` expression case separator |
|
|
||||||
| `//` | comment | line comment |
|
|
||||||
| `/*` .. `*/` | comment | block comment |
|
|
||||||
| `(*` .. `*)` | comment | _reserved_ |
|
|
||||||
| `<` .. `>` | angular brackets | _reserved_ |
|
|
||||||
| `++` | increment | _reserved_ |
|
|
||||||
| `--` | decrement | _reserved_ |
|
|
||||||
| `..` | range | _reserved_ |
|
|
||||||
| `...` | range | _reserved_ |
|
|
||||||
| `**` | exponentiation | _reserved_ |
|
|
||||||
| `#` | hash | _reserved_ |
|
|
||||||
| `@` | at | _reserved_ |
|
|
||||||
| `$` | dollar | _reserved_ |
|
|
||||||
| `->` | arrow | _reserved_ |
|
|
||||||
| `<-` | left arrow | _reserved_ |
|
|
||||||
| `===` | strict equals to | _reserved_ |
|
|
||||||
| `!==` | strict not equals to | _reserved_ |
|
|
||||||
| `:=` | assignment | _reserved_ |
|
|
||||||
| `::<` .. `>` | turbofish | _reserved_ |
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.19.10",
|
|
||||||
"repoHome": "https://github.com/rhaiscript/rhai/blob/master",
|
|
||||||
"repoTree": "https://github.com/rhaiscript/rhai/tree/master",
|
|
||||||
"rootUrl": "",
|
|
||||||
"rootUrlX": "/rhai",
|
|
||||||
"rootUrlXX": "/rhai/vnext"
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
Calling Rhai Functions from Rust
|
|
||||||
===============================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Rhai also allows working _backwards_ from the other direction – i.e. calling a Rhai-scripted function
|
|
||||||
from Rust via `Engine::call_fn`.
|
|
||||||
|
|
||||||
Functions declared with `private` are hidden and cannot be called from Rust (see also [modules]).
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Define functions in a script.
|
|
||||||
let ast = engine.compile(true,
|
|
||||||
r#"
|
|
||||||
// a function with two parameters: string and i64
|
|
||||||
fn hello(x, y) {
|
|
||||||
x.len + y
|
|
||||||
}
|
|
||||||
|
|
||||||
// functions can be overloaded: this one takes only one parameter
|
|
||||||
fn hello(x) {
|
|
||||||
x * 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// this one takes no parameters
|
|
||||||
fn hello() {
|
|
||||||
42
|
|
||||||
}
|
|
||||||
|
|
||||||
// this one is private and cannot be called by 'call_fn'
|
|
||||||
private hidden() {
|
|
||||||
throw "you shouldn't see me!";
|
|
||||||
}
|
|
||||||
"#)?;
|
|
||||||
|
|
||||||
// A custom scope can also contain any variables/constants available to the functions
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
|
|
||||||
// Evaluate a function defined in the script, passing arguments into the script as a tuple.
|
|
||||||
// Beware, arguments must be of the correct types because Rhai does not have built-in type conversions.
|
|
||||||
// If arguments of the wrong types are passed, the Engine will not find the function.
|
|
||||||
|
|
||||||
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", ( String::from("abc"), 123_i64 ) )?;
|
|
||||||
// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
// return type must be specified put arguments in a tuple
|
|
||||||
|
|
||||||
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", (123_i64,) )?;
|
|
||||||
// ^^^^^^^^^^ tuple of one
|
|
||||||
|
|
||||||
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?;
|
|
||||||
// ^^ unit = tuple of zero
|
|
||||||
|
|
||||||
// The following call will return a function-not-found error because
|
|
||||||
// 'hidden' is declared with 'private'.
|
|
||||||
let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Low-Level API – `Engine::call_fn_dynamic`
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
For more control, construct all arguments as `Dynamic` values and use `Engine::call_fn_dynamic`, passing it
|
|
||||||
anything that implements `AsMut<Dynamic>` (such as a simple array or a `Vec<Dynamic>`):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let result = engine.call_fn_dynamic(
|
|
||||||
&mut scope, // scope to use
|
|
||||||
&ast, // AST containing the functions
|
|
||||||
"hello", // function entry-point
|
|
||||||
None, // 'this' pointer, if any
|
|
||||||
[ String::from("abc").into(), 123_i64.into() ] // arguments
|
|
||||||
)?;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Binding the `this` Pointer
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
`Engine::call_fn_dynamic` can also bind a value to the `this` pointer of a script-defined function.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let ast = engine.compile("fn action(x) { this += x; }")?;
|
|
||||||
|
|
||||||
let mut value: Dynamic = 1_i64.into();
|
|
||||||
|
|
||||||
let result = engine.call_fn_dynamic(
|
|
||||||
&mut scope,
|
|
||||||
&ast,
|
|
||||||
"action",
|
|
||||||
Some(&mut value), // binding the 'this' pointer
|
|
||||||
[ 41_i64.into() ]
|
|
||||||
)?;
|
|
||||||
|
|
||||||
assert_eq!(value.as_int()?, 42);
|
|
||||||
```
|
|
@ -1,27 +0,0 @@
|
|||||||
Compile a Script (to AST)
|
|
||||||
========================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
To repeatedly evaluate a script, _compile_ it first with `Engine::compile` into an `AST`
|
|
||||||
(abstract syntax tree) form.
|
|
||||||
|
|
||||||
`Engine::eval_ast` evaluates a pre-compiled `AST`.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Compile to an AST and store it for later evaluations
|
|
||||||
let ast = engine.compile("40 + 2")?;
|
|
||||||
|
|
||||||
for _ in 0..42 {
|
|
||||||
let result: i64 = engine.eval_ast(&ast)?;
|
|
||||||
|
|
||||||
println!("Answer #{}: {}", i, result); // prints 42
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Compiling a script file is also supported with `Engine::compile_file`
|
|
||||||
(not available under [`no_std`] or in [WASM] builds):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let ast = engine.compile_file("hello_world.rhai".into())?;
|
|
||||||
```
|
|
@ -1,115 +0,0 @@
|
|||||||
Custom Operators
|
|
||||||
================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
For use as a DSL (Domain-Specific Languages), it is sometimes more convenient to augment Rhai with
|
|
||||||
customized operators performing specific logic.
|
|
||||||
|
|
||||||
`Engine::register_custom_operator` registers a keyword as a custom operator, giving it a particular
|
|
||||||
_precedence_ (which cannot be zero).
|
|
||||||
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::{Engine, RegisterFn};
|
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
|
|
||||||
// Register a custom operator named 'foo' and give it a precedence of 160
|
|
||||||
// (i.e. between +|- and *|/)
|
|
||||||
// Also register the implementation of the customer operator as a function
|
|
||||||
engine
|
|
||||||
.register_custom_operator("foo", 160)?
|
|
||||||
.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y));
|
|
||||||
|
|
||||||
// The custom operator can be used in expressions
|
|
||||||
let result = engine.eval_expression::<i64>("1 + 2 * 3 foo 4 - 5 / 6")?;
|
|
||||||
// ^ custom operator
|
|
||||||
|
|
||||||
// The above is equivalent to: 1 + ((2 * 3) foo 4) - (5 / 6)
|
|
||||||
result == 15;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Alternatives to a Custom Operator
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Custom operators are merely _syntactic sugar_. They map directly to registered functions.
|
|
||||||
|
|
||||||
Therefore, the following are equivalent (assuming `foo` has been registered as a custom operator):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
1 + 2 * 3 foo 4 - 5 / 6 // use custom operator
|
|
||||||
|
|
||||||
1 + foo(2 * 3, 4) - 5 / 6 // use function call
|
|
||||||
```
|
|
||||||
|
|
||||||
A script using custom operators can always be pre-processed, via a pre-processor application,
|
|
||||||
into a syntax that uses the corresponding function calls.
|
|
||||||
|
|
||||||
Using `Engine::register_custom_operator` merely enables a convenient shortcut.
|
|
||||||
|
|
||||||
|
|
||||||
Must be a Valid Identifier or Reserved Symbol
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
All custom operators must be _identifiers_ that follow the same naming rules as [variables].
|
|
||||||
|
|
||||||
Alternatively, they can also be [reserved symbols]({{rootUrl}}/appendix/operators.md#symbols),
|
|
||||||
[disabled operators or keywords][disable keywords and operators].
|
|
||||||
|
|
||||||
```rust
|
|
||||||
engine.register_custom_operator("foo", 20); // 'foo' is a valid custom operator
|
|
||||||
|
|
||||||
engine.register_custom_operator("#", 20); // the reserved symbol '#' is also
|
|
||||||
// a valid custom operator
|
|
||||||
|
|
||||||
engine.register_custom_operator("+", 30); // <- error: '+' is an active operator
|
|
||||||
|
|
||||||
engine.register_custom_operator("=>", 30); // <- error: '=>' is an active symbol
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Binary Operators Only
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
All custom operators must be _binary_ (i.e. they take two operands).
|
|
||||||
_Unary_ custom operators are not supported.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
engine
|
|
||||||
.register_custom_operator("foo", 160)?
|
|
||||||
.register_fn("foo", |x: i64| x * x);
|
|
||||||
|
|
||||||
engine.eval::<i64>("1 + 2 * 3 foo 4 - 5 / 6")?; // error: function 'foo (i64, i64)' not found
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Operator Precedence
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
All operators in Rhai has a _precedence_ indicating how tightly they bind.
|
|
||||||
|
|
||||||
A higher precedence binds more tightly than a lower precedence, so `*` and `/` binds before `+` and `-` etc.
|
|
||||||
|
|
||||||
When registering a custom operator, the operator's precedence must also be provided.
|
|
||||||
|
|
||||||
The following _precedence table_ shows the built-in precedence of standard Rhai operators:
|
|
||||||
|
|
||||||
| Category | Operators | Precedence (0-255) |
|
|
||||||
| ------------------- | :-------------------------------------------------------------------------------------: | :----------------: |
|
|
||||||
| Assignments | `=`, `+=`, `-=`, `*=`, `/=`, `~=`, `%=`,<br/>`<<=`, `>>=`, `&=`, <code>\|=</code>, `^=` | 0 |
|
|
||||||
| Logic and bit masks | <code>\|\|</code>, <code>\|</code>, `^` | 30 |
|
|
||||||
| Logic and bit masks | `&&`, `&` | 60 |
|
|
||||||
| Comparisons | `==`, `!=` | 90 |
|
|
||||||
| | `in` | 110 |
|
|
||||||
| Comparisons | `>`, `>=`, `<`, `<=` | 130 |
|
|
||||||
| Arithmetic | `+`, `-` | 150 |
|
|
||||||
| Arithmetic | `*`, `/`, `%` | 180 |
|
|
||||||
| Arithmetic | `~` | 190 |
|
|
||||||
| Bit-shifts | `<<`, `>>` | 210 |
|
|
||||||
| Object | `.` _(binds to right)_ | 240 |
|
|
||||||
| Unary operators | unary `+`, `-`, `!` _(binds to right)_ | 255 |
|
|
@ -1,404 +0,0 @@
|
|||||||
Extend Rhai with Custom Syntax
|
|
||||||
=============================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
For the ultimate adventurous, there is a built-in facility to _extend_ the Rhai language
|
|
||||||
with custom-defined _syntax_.
|
|
||||||
|
|
||||||
But before going off to define the next weird statement type, heed this warning:
|
|
||||||
|
|
||||||
|
|
||||||
Don't Do It™
|
|
||||||
------------
|
|
||||||
|
|
||||||
Stick with standard language syntax as much as possible.
|
|
||||||
|
|
||||||
Having to learn Rhai is bad enough, no sane user would ever want to learn _yet_ another
|
|
||||||
obscure language syntax just to do something.
|
|
||||||
|
|
||||||
Try to use [custom operators] first. Defining a custom syntax should be considered a _last resort_.
|
|
||||||
|
|
||||||
|
|
||||||
Where This Might Be Useful
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
* Where an operation is used a _LOT_ and a custom syntax saves a lot of typing.
|
|
||||||
|
|
||||||
* Where a custom syntax _significantly_ simplifies the code and _significantly_ enhances understanding of the code's intent.
|
|
||||||
|
|
||||||
* Where certain logic cannot be easily encapsulated inside a function.
|
|
||||||
|
|
||||||
* Where you just want to confuse your user and make their lives miserable, because you can.
|
|
||||||
|
|
||||||
|
|
||||||
Step One – Design The Syntax
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
A custom syntax is simply a list of symbols.
|
|
||||||
|
|
||||||
These symbol types can be used:
|
|
||||||
|
|
||||||
* Standard [keywords]({{rootUrl}}/appendix/keywords.md)
|
|
||||||
|
|
||||||
* Standard [operators]({{rootUrl}}/appendix/operators.md#operators).
|
|
||||||
|
|
||||||
* Reserved [symbols]({{rootUrl}}/appendix/operators.md#symbols).
|
|
||||||
|
|
||||||
* Identifiers following the [variable] naming rules.
|
|
||||||
|
|
||||||
* `$expr$` – any valid expression, statement or statement block.
|
|
||||||
|
|
||||||
* `$block$` – any valid statement block (i.e. must be enclosed by `'{'` .. `'}'`).
|
|
||||||
|
|
||||||
* `$ident$` – any [variable] name.
|
|
||||||
|
|
||||||
### The First Symbol Must be an Identifier
|
|
||||||
|
|
||||||
There is no specific limit on the combination and sequencing of each symbol type,
|
|
||||||
except the _first_ symbol which must be a custom keyword that follows the naming rules
|
|
||||||
of [variables].
|
|
||||||
|
|
||||||
The first symbol also cannot be a normal or reserved [keyword].
|
|
||||||
In other words, any valid identifier that is not a [keyword] will work fine.
|
|
||||||
|
|
||||||
### The First Symbol Must be Unique
|
|
||||||
|
|
||||||
Rhai uses the _first_ symbol as a clue to parse custom syntax.
|
|
||||||
|
|
||||||
Therefore, at any one time, there can only be _one_ custom syntax starting with each unique symbol.
|
|
||||||
|
|
||||||
Any new custom syntax definition using the same first symbol simply _overwrites_ the previous one.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```rust
|
|
||||||
exec $ident$ <- $expr$ : $block$
|
|
||||||
```
|
|
||||||
|
|
||||||
The above syntax is made up of a stream of symbols:
|
|
||||||
|
|
||||||
| Position | Input | Symbol | Description |
|
|
||||||
| :------: | :---: | :-------: | -------------------------------------------------------------------------------------------------------- |
|
|
||||||
| 1 | | `exec` | custom keyword |
|
|
||||||
| 2 | 1 | `$ident$` | a variable name |
|
|
||||||
| 3 | | `<-` | the left-arrow symbol (which is a [reserved symbol]({{rootUrl}}/appendix/operators.md#symbols) in Rhai). |
|
|
||||||
| 4 | 2 | `$expr$` | an expression, which may be enclosed with `{` .. `}`, or not. |
|
|
||||||
| 5 | | `:` | the colon symbol |
|
|
||||||
| 6 | 3 | `$block$` | a statement block, which must be enclosed with `{` .. `}`. |
|
|
||||||
|
|
||||||
This syntax matches the following sample code and generates three inputs (one for each non-keyword):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Assuming the 'exec' custom syntax implementation declares the variable 'hello':
|
|
||||||
let x = exec hello <- foo(1, 2) : {
|
|
||||||
hello += bar(hello);
|
|
||||||
baz(hello);
|
|
||||||
};
|
|
||||||
|
|
||||||
print(x); // variable 'x' has a value returned by the custom syntax
|
|
||||||
|
|
||||||
print(hello); // variable declared by a custom syntax persists!
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Step Two – Implementation
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Any custom syntax must include an _implementation_ of it.
|
|
||||||
|
|
||||||
### Function Signature
|
|
||||||
|
|
||||||
The function signature of an implementation is:
|
|
||||||
|
|
||||||
> `Fn(context: &mut EvalContext, inputs: &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>`
|
|
||||||
|
|
||||||
where:
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| -------------------------- | :-------------------------------------: | ---------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `context` | `&mut EvalContext` | mutable reference to the current evaluation _context_ |
|
|
||||||
| • `scope()` | `&Scope` | reference to the current [`Scope`] |
|
|
||||||
| • `scope_mut()` | `&mut &mut Scope` | mutable reference to the current [`Scope`]; variables can be added to/removed from it |
|
|
||||||
| • `engine()` | `&Engine` | reference to the current [`Engine`] |
|
|
||||||
| • `source()` | `Option<&str>` | reference to the current source, if any |
|
|
||||||
| • `iter_imports()` | `impl Iterator<Item = (&str, &Module)>` | iterator of the current stack of [modules] imported via `import` statements |
|
|
||||||
| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature |
|
|
||||||
| • `iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
|
||||||
| • `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature |
|
|
||||||
| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
|
|
||||||
| • `call_level()` | `usize` | the current nesting level of function calls |
|
|
||||||
| `inputs` | `&[Expression]` | a list of input expression trees |
|
|
||||||
|
|
||||||
### Return Value
|
|
||||||
|
|
||||||
Return value is the result of evaluating the custom syntax expression.
|
|
||||||
|
|
||||||
### Access Arguments
|
|
||||||
|
|
||||||
The most important argument is `inputs` where the matched identifiers (`$ident$`), expressions/statements (`$expr$`)
|
|
||||||
and statement blocks (`$block$`) are provided.
|
|
||||||
|
|
||||||
To access a particular argument, use the following patterns:
|
|
||||||
|
|
||||||
| Argument type | Pattern (`n` = slot in `inputs`) | Result type | Description |
|
|
||||||
| :-----------: | ---------------------------------------- | :----------: | ------------------ |
|
|
||||||
| `$ident$` | `inputs[n].get_variable_name().unwrap()` | `&str` | name of a variable |
|
|
||||||
| `$expr$` | `inputs.get(n).unwrap()` | `Expression` | an expression tree |
|
|
||||||
| `$block$` | `inputs.get(n).unwrap()` | `Expression` | an expression tree |
|
|
||||||
|
|
||||||
### Evaluate an Expression Tree
|
|
||||||
|
|
||||||
Use the `EvalContext::eval_expression_tree` method to evaluate an arbitrary expression tree
|
|
||||||
within the current evaluation context.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let expression = inputs.get(0).unwrap();
|
|
||||||
let result = context.eval_expression_tree(expression)?;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Declare Variables
|
|
||||||
|
|
||||||
New variables maybe declared (usually with a variable name that is passed in via `$ident$).
|
|
||||||
|
|
||||||
It can simply be pushed into the [`Scope`].
|
|
||||||
|
|
||||||
However, beware that all new variables must be declared _prior_ to evaluating any expression tree.
|
|
||||||
In other words, any [`Scope`] calls that change the list of must come _before_ any
|
|
||||||
`EvalContext::eval_expression_tree` calls.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let var_name = inputs[0].get_variable_name().unwrap();
|
|
||||||
let expression = inputs.get(1).unwrap();
|
|
||||||
|
|
||||||
context.scope_mut().push(var_name, 0 as INT); // do this BEFORE 'context.eval_expression_tree'!
|
|
||||||
|
|
||||||
let result = context.eval_expression_tree(expression)?;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Step Three – Register the Custom Syntax
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
Use `Engine::register_custom_syntax` to register a custom syntax.
|
|
||||||
|
|
||||||
Again, beware that the _first_ symbol must be unique. If there already exists a custom syntax starting
|
|
||||||
with that symbol, the previous syntax will be overwritten.
|
|
||||||
|
|
||||||
The syntax is passed simply as a slice of `&str`.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Custom syntax implementation
|
|
||||||
fn implementation_func(
|
|
||||||
context: &mut EvalContext,
|
|
||||||
inputs: &[Expression]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
|
||||||
let stmt = inputs.get(1).unwrap();
|
|
||||||
let condition = inputs.get(2).unwrap();
|
|
||||||
|
|
||||||
// Push one new variable into the scope BEFORE 'context.eval_expression_tree'
|
|
||||||
context.scope_mut().push(var_name, 0 as INT);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Evaluate the statement block
|
|
||||||
context.eval_expression_tree(stmt)?;
|
|
||||||
|
|
||||||
// Evaluate the condition expression
|
|
||||||
let stop = !context.eval_expression_tree(condition)?
|
|
||||||
.as_bool().map_err(|err| Box::new(
|
|
||||||
EvalAltResult::ErrorMismatchDataType(
|
|
||||||
"bool".to_string(),
|
|
||||||
err.to_string(),
|
|
||||||
condition.position(),
|
|
||||||
)
|
|
||||||
))?;
|
|
||||||
|
|
||||||
if stop {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the custom syntax (sample): exec |x| -> { x += 1 } while x < 0
|
|
||||||
engine.register_custom_syntax(
|
|
||||||
&[ "exec", "|", "$ident$", "|", "->", "$block$", "while", "$expr$" ], // the custom syntax
|
|
||||||
1, // the number of new variables declared within this custom syntax
|
|
||||||
implementation_func
|
|
||||||
)?;
|
|
||||||
```
|
|
||||||
|
|
||||||
Remember that a custom syntax acts as an _expression_, so it can show up practically anywhere:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Use as an expression:
|
|
||||||
let foo = (exec |x| -> { x += 1 } while x < 0) * 100;
|
|
||||||
|
|
||||||
// Use as a function call argument:
|
|
||||||
do_something(exec |x| -> { x += 1 } while x < 0, 24, true);
|
|
||||||
|
|
||||||
// Use as a statement:
|
|
||||||
exec |x| -> { x += 1 } while x < 0;
|
|
||||||
// ^ terminate statement with ';'
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Step Four – Disable Unneeded Statement Types
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
When a DSL needs a custom syntax, most likely than not it is extremely specialized.
|
|
||||||
Therefore, many statement types actually may not make sense under the same usage scenario.
|
|
||||||
|
|
||||||
So, while at it, better [disable][disable keywords and operators] those built-in keywords
|
|
||||||
and operators that should not be used by the user. The would leave only the bare minimum
|
|
||||||
language surface exposed, together with the custom syntax that is tailor-designed for
|
|
||||||
the scenario.
|
|
||||||
|
|
||||||
A keyword or operator that is disabled can still be used in a custom syntax.
|
|
||||||
|
|
||||||
In an extreme case, it is possible to disable _every_ keyword in the language, leaving only
|
|
||||||
custom syntax (plus possibly expressions). But again, Don't Do It™ – unless you are certain
|
|
||||||
of what you're doing.
|
|
||||||
|
|
||||||
|
|
||||||
Step Five – Document
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
For custom syntax, documentation is crucial.
|
|
||||||
|
|
||||||
Make sure there are _lots_ of examples for users to follow.
|
|
||||||
|
|
||||||
|
|
||||||
Step Six – Profit!
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
|
|
||||||
Really Advanced – Custom Parsers
|
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
Sometimes it is desirable to have multiple custom syntax starting with the
|
|
||||||
same symbol. This is especially common for _command-style_ syntax where the
|
|
||||||
second symbol calls a particular command:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// The following simulates a command-style syntax, all starting with 'perform'.
|
|
||||||
perform hello world; // A fixed sequence of symbols
|
|
||||||
perform action 42; // Perform a system action with a parameter
|
|
||||||
perform update system; // Update the system
|
|
||||||
perform check all; // Check all system settings
|
|
||||||
perform cleanup; // Clean up the system
|
|
||||||
perform add something; // Add something to the system
|
|
||||||
perform remove something; // Delete something from the system
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, a custom syntax may have variable length, with a termination symbol:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// The following is a variable-length list terminated by '>'
|
|
||||||
tags < "foo", "bar", 123, ... , x+y, true >
|
|
||||||
```
|
|
||||||
|
|
||||||
For even more flexibility in order to handle these advanced use cases, there is a
|
|
||||||
_low level_ API for custom syntax that allows the registration of an entire mini-parser.
|
|
||||||
|
|
||||||
Use `Engine::register_custom_syntax_raw` to register a custom syntax _parser_
|
|
||||||
together with the implementation function.
|
|
||||||
|
|
||||||
### How Custom Parsers Work
|
|
||||||
|
|
||||||
A custom parser takes as input parameters two pieces of information:
|
|
||||||
|
|
||||||
* The symbols parsed so far; `$ident$` is replaced with the actual identifier parsed,
|
|
||||||
while `$expr$` and `$block$` stay as they were.
|
|
||||||
|
|
||||||
The custom parser can inspect this symbols stream to determine the next symbol to parse.
|
|
||||||
|
|
||||||
* The _look-ahead_ symbol, which is the symbol that will be parsed _next_.
|
|
||||||
|
|
||||||
If the look-ahead is an expected symbol, the customer parser just returns it to continue parsing,
|
|
||||||
or it can return `$ident$` to parse it as an identifier, or even `$expr$` to start parsing
|
|
||||||
an expression.
|
|
||||||
|
|
||||||
If the look-ahead is '`{`', then the custom parser may also return `$block$` to start parsing a
|
|
||||||
statements block.
|
|
||||||
|
|
||||||
If the look-ahead is unexpected, the custom parser should then return the symbol expected
|
|
||||||
and Rhai will fail with a parse error containing information about the expected symbol.
|
|
||||||
|
|
||||||
A custom parser always returns the _next_ symbol expected, which can also be `$ident$`,
|
|
||||||
`$expr$` or `$block$`, or `None` if parsing should terminate (_without_ reading the
|
|
||||||
look-ahead symbol).
|
|
||||||
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```rust
|
|
||||||
engine.register_custom_syntax_raw(
|
|
||||||
"perform",
|
|
||||||
// The custom parser implementation - always returns the next symbol expected
|
|
||||||
// 'look_ahead' is the next symbol about to be read
|
|
||||||
|symbols, look_ahead| match symbols.len() {
|
|
||||||
// perform ...
|
|
||||||
1 => Ok(Some("$ident$".to_string())),
|
|
||||||
// perform command ...
|
|
||||||
2 => match symbols[1].as_str() {
|
|
||||||
"action" => Ok(Some("$expr$".into())),
|
|
||||||
"hello" => Ok(Some("world".into())),
|
|
||||||
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".into())),
|
|
||||||
"cleanup" => Ok(None),
|
|
||||||
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
|
||||||
LexError::ImproperSymbol(format!("Improper command: {}", cmd))
|
|
||||||
)), Position::NONE)),
|
|
||||||
},
|
|
||||||
// perform command arg ...
|
|
||||||
3 => match (symbols[1].as_str(), symbols[2].as_str()) {
|
|
||||||
("action", _) => Ok(None),
|
|
||||||
("hello", "world") => Ok(None),
|
|
||||||
("update", arg) if arg == "system" => Ok(None),
|
|
||||||
("update", arg) if arg == "client" => Ok(None),
|
|
||||||
("check", arg) => Ok(None),
|
|
||||||
("add", arg) => Ok(None),
|
|
||||||
("remove", arg) => Ok(None),
|
|
||||||
(cmd, arg) => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
|
||||||
LexError::ImproperSymbol(
|
|
||||||
format!("Invalid argument for command {}: {}", cmd, arg)
|
|
||||||
)
|
|
||||||
)), Position::NONE)),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
// Number of new variables declared by this custom syntax
|
|
||||||
0,
|
|
||||||
// Implementation function
|
|
||||||
implementation_func
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Function Signature
|
|
||||||
|
|
||||||
The custom syntax parser has the following signature:
|
|
||||||
|
|
||||||
> `Fn(symbols: &[ImmutableString], look_ahead: &str) -> Result<Option<ImmutableString>, ParseError>`
|
|
||||||
|
|
||||||
where:
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| ------------ | :------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `symbols` | `&[ImmutableString]` | a slice of symbols that have been parsed so far, possibly containing `$expr$` and/or `$block$`; `$ident$` is replaced by the actual identifier |
|
|
||||||
| `look_ahead` | `&str` | a string slice containing the next symbol that is about to be read |
|
|
||||||
|
|
||||||
Most strings are [`ImmutableString`][string]'s so it is usually more efficient to just `clone` the appropriate one
|
|
||||||
(if any matches, or keep an internal cache for commonly-used symbols) as the return value.
|
|
||||||
|
|
||||||
### Return Value
|
|
||||||
|
|
||||||
The return value is `Result<Option<ImmutableString>, ParseError>` where:
|
|
||||||
|
|
||||||
| Value | Description |
|
|
||||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `Ok(None)` | parsing complete and there are no more symbols to match |
|
|
||||||
| `Ok(Some(symbol))` | the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$` |
|
|
||||||
| `Err(ParseError)` | error that is reflected back to the [`Engine`] – normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate that there is a syntax error, but it can be any `ParseError`. |
|
|
@ -1,28 +0,0 @@
|
|||||||
Disable Certain Keywords and/or Operators
|
|
||||||
========================================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
For certain embedded usage, it is sometimes necessary to restrict the language to a strict subset of Rhai
|
|
||||||
to prevent usage of certain language features.
|
|
||||||
|
|
||||||
Rhai supports surgically disabling a keyword or operator via the `Engine::disable_symbol` method.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::Engine;
|
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
|
|
||||||
engine
|
|
||||||
.disable_symbol("if") // disable the 'if' keyword
|
|
||||||
.disable_symbol("+="); // disable the '+=' operator
|
|
||||||
|
|
||||||
// The following all return parse errors.
|
|
||||||
|
|
||||||
engine.compile("let x = if true { 42 } else { 0 };")?;
|
|
||||||
// ^ 'if' is rejected as a reserved keyword
|
|
||||||
|
|
||||||
engine.compile("let x = 40 + 2; x += 1;")?;
|
|
||||||
// ^ '+=' is not recognized as an operator
|
|
||||||
// ^ other operators are not affected
|
|
||||||
```
|
|
@ -1,92 +0,0 @@
|
|||||||
Use Rhai as a Domain-Specific Language (DSL)
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Rhai can be successfully used as a domain-specific language (DSL).
|
|
||||||
|
|
||||||
|
|
||||||
Expressions Only
|
|
||||||
----------------
|
|
||||||
|
|
||||||
In many DSL scenarios, only evaluation of expressions is needed.
|
|
||||||
|
|
||||||
The [`Engine::eval_expression_XXX`][`eval_expression`] API can be used to restrict
|
|
||||||
a script to expressions only.
|
|
||||||
|
|
||||||
|
|
||||||
Unicode Standard Annex #31 Identifiers
|
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
Variable names and other identifiers do not necessarily need to be ASCII-only.
|
|
||||||
|
|
||||||
The [`unicode-xid-ident`] feature, when turned on, causes Rhai to allow variable names and identifiers
|
|
||||||
that follow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/).
|
|
||||||
|
|
||||||
This is sometimes useful in a non-English DSL.
|
|
||||||
|
|
||||||
|
|
||||||
Disable Keywords and/or Operators
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
In some DSL scenarios, it is necessary to further restrict the language to exclude certain
|
|
||||||
language features that are not necessary or dangerous to the application.
|
|
||||||
|
|
||||||
For example, a DSL may disable the `while` loop while keeping all other statement types intact.
|
|
||||||
|
|
||||||
It is possible, in Rhai, to surgically [disable keywords and operators].
|
|
||||||
|
|
||||||
|
|
||||||
Custom Operators
|
|
||||||
----------------
|
|
||||||
|
|
||||||
On the other hand, some DSL scenarios require special operators that make sense only for
|
|
||||||
that specific environment. In such cases, it is possible to define [custom operators] in Rhai.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let animal = "rabbit";
|
|
||||||
let food = "carrot";
|
|
||||||
|
|
||||||
animal eats food // custom operator 'eats'
|
|
||||||
|
|
||||||
eats(animal, food) // <- the above really de-sugars to this
|
|
||||||
```
|
|
||||||
|
|
||||||
Although a [custom operator] always de-sugars to a simple function call,
|
|
||||||
nevertheless it makes the DSL syntax much simpler and expressive.
|
|
||||||
|
|
||||||
|
|
||||||
Custom Syntax
|
|
||||||
-------------
|
|
||||||
|
|
||||||
For advanced DSL scenarios, it is possible to define entire expression [_syntax_][custom syntax] &ndash
|
|
||||||
essentially custom statement types.
|
|
||||||
|
|
||||||
For example, the following is a SQL-like syntax for some obscure DSL operation:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let table = [..., ..., ..., ...];
|
|
||||||
|
|
||||||
// Syntax = calculate $ident$ ( $expr$ -> $ident$ ) => $ident$ : $expr$
|
|
||||||
let total = calculate sum(table->price) => row : row.weight > 50;
|
|
||||||
|
|
||||||
// Note: There is nothing special about those symbols; to make it look exactly like SQL:
|
|
||||||
// Syntax = SELECT $ident$ ( $ident$ ) AS $ident$ FROM $expr$ WHERE $expr$
|
|
||||||
let total = SELECT sum(price) AS row FROM table WHERE row.weight > 50;
|
|
||||||
```
|
|
||||||
|
|
||||||
After registering this custom syntax with Rhai, it can be used anywhere inside a script as
|
|
||||||
a normal expression.
|
|
||||||
|
|
||||||
For its evaluation, the callback function will receive the following list of inputs:
|
|
||||||
|
|
||||||
* `inputs[0] = "sum"` - math operator
|
|
||||||
* `inputs[1] = "price"` - field name
|
|
||||||
* `inputs[2] = "row"` - loop variable name
|
|
||||||
* `inputs[3] = Expression(table)` - data source
|
|
||||||
* `inputs[4] = Expression(row.wright > 50)` - filter predicate
|
|
||||||
|
|
||||||
Other identifiers, such as `"calculate"`, `"FROM"`, as well as symbols such as `->` and `:` etc.,
|
|
||||||
are parsed in the order defined within the custom syntax.
|
|
@ -1,27 +0,0 @@
|
|||||||
Evaluate Expressions Only
|
|
||||||
========================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Sometimes a use case does not require a full-blown scripting _language_, but only needs to evaluate _expressions_.
|
|
||||||
|
|
||||||
In these cases, use the `Engine::compile_expression` and `Engine::eval_expression` methods or their `_with_scope` variants.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let result = engine.eval_expression::<i64>("2 + (10 + 10) * 2")?;
|
|
||||||
```
|
|
||||||
|
|
||||||
When evaluating _expressions_, no full-blown statement (e.g. `if`, `while`, `for`, `fn`) – not even variable assignment –
|
|
||||||
is supported and will be considered parse errors when encountered.
|
|
||||||
|
|
||||||
[Closures] and [anonymous functions] are also not supported because in the background they compile to functions.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// The following are all syntax errors because the script is not an expression.
|
|
||||||
|
|
||||||
engine.eval_expression::<()>("x = 42")?;
|
|
||||||
|
|
||||||
let ast = engine.compile_expression("let x = 42")?;
|
|
||||||
|
|
||||||
let result = engine.eval_expression_with_scope::<i64>(&mut scope, "if x { 42 } else { 123 }")?;
|
|
||||||
```
|
|
@ -1,43 +0,0 @@
|
|||||||
Create a Rust Closure from a Rhai Function
|
|
||||||
=========================================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
It is possible to further encapsulate a script in Rust such that it becomes a normal Rust function.
|
|
||||||
|
|
||||||
Such a _closure_ is very useful as call-back functions.
|
|
||||||
|
|
||||||
Creating them is accomplished via the `Func` trait which contains `create_from_script`
|
|
||||||
(as well as its companion method `create_from_ast`):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::{Engine, Func}; // use 'Func' for 'create_from_script'
|
|
||||||
|
|
||||||
let engine = Engine::new(); // create a new 'Engine' just for this
|
|
||||||
|
|
||||||
let script = "fn calc(x, y) { x + y.len < 42 }";
|
|
||||||
|
|
||||||
// Func takes two type parameters:
|
|
||||||
// 1) a tuple made up of the types of the script function's parameters
|
|
||||||
// 2) the return type of the script function
|
|
||||||
//
|
|
||||||
// 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
|
||||||
let func = Func::<(i64, String), bool>::create_from_script(
|
|
||||||
// ^^^^^^^^^^^^^ function parameter types in tuple
|
|
||||||
|
|
||||||
engine, // the 'Engine' is consumed into the closure
|
|
||||||
script, // the script, notice number of parameters must match
|
|
||||||
"calc" // the entry-point function name
|
|
||||||
)?;
|
|
||||||
|
|
||||||
func(123, "hello".to_string())? == false; // call the closure
|
|
||||||
|
|
||||||
schedule_callback(func); // pass it as a callback to another function
|
|
||||||
|
|
||||||
// Although there is nothing you can't do by manually writing out the closure yourself...
|
|
||||||
let engine = Engine::new();
|
|
||||||
let ast = engine.compile(script)?;
|
|
||||||
schedule_callback(Box::new(move |x: i64, y: String| -> Result<bool, Box<EvalAltResult>> {
|
|
||||||
engine.call_fn(&mut Scope::new(), &ast, "calc", (x, y))
|
|
||||||
}));
|
|
||||||
```
|
|
@ -1,60 +0,0 @@
|
|||||||
Hello World in Rhai
|
|
||||||
===================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
To get going with Rhai is as simple as creating an instance of the scripting engine `rhai::Engine` via
|
|
||||||
`Engine::new`, then calling the `eval` method:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::{Engine, EvalAltResult};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>>
|
|
||||||
{
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
let result = engine.eval::<i64>("40 + 2")?;
|
|
||||||
// ^^^^^^^ cast the result to an 'i64', this is required
|
|
||||||
|
|
||||||
println!("Answer: {}", result); // prints 42
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Evaluate a script file directly:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// 'eval_file' takes a 'PathBuf'
|
|
||||||
let result = engine.eval_file::<i64>("hello_world.rhai".into())?;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Error Type
|
|
||||||
----------
|
|
||||||
|
|
||||||
`rhai::EvalAltResult` is the standard Rhai error type, which is a Rust `enum` containing all errors encountered
|
|
||||||
during the parsing or evaluation process.
|
|
||||||
|
|
||||||
|
|
||||||
Return Type
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The type parameter for `Engine::eval` is used to specify the type of the return value,
|
|
||||||
which _must_ match the actual type or an error is returned. Rhai is very strict here.
|
|
||||||
|
|
||||||
There are two ways to specify the return type – _turbofish_ notation, or type inference.
|
|
||||||
|
|
||||||
Use [`Dynamic`] for uncertain return types.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let result = engine.eval::<i64>("40 + 2")?; // return type is i64, specified using 'turbofish' notation
|
|
||||||
|
|
||||||
let result: i64 = engine.eval("40 + 2")?; // return type is inferred to be i64
|
|
||||||
|
|
||||||
result.is::<i64>() == true;
|
|
||||||
|
|
||||||
let result: Dynamic = engine.eval("boo()")?; // use 'Dynamic' if you're not sure what type it'll be!
|
|
||||||
|
|
||||||
let result = engine.eval::<String>("40 + 2")?; // returns an error because the actual return type is i64, not String
|
|
||||||
```
|
|
@ -1,8 +0,0 @@
|
|||||||
Using the Engine
|
|
||||||
================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Rhai's interpreter resides in the [`Engine`] type under the master `rhai` namespace.
|
|
||||||
|
|
||||||
This section shows how to set up, configure and use this scripting engine.
|
|
@ -1,107 +0,0 @@
|
|||||||
Export Functions Metadata to JSON
|
|
||||||
================================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
`Engine::gen_fn_metadata_to_json`<br/>`Engine::gen_fn_metadata_with_ast_to_json`
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
As part of a _reflections_ API, `Engine::gen_fn_metadata_to_json` and the corresponding
|
|
||||||
`Engine::gen_fn_metadata_with_ast_to_json` export the full list of [functions metadata]
|
|
||||||
in JSON format.
|
|
||||||
|
|
||||||
The [`metadata`] feature must be used to turn on this API, which requires
|
|
||||||
the [`serde_json`](https://crates.io/crates/serde_json) crate.
|
|
||||||
|
|
||||||
### Sources
|
|
||||||
|
|
||||||
Functions from the following sources are included:
|
|
||||||
|
|
||||||
1) Script-defined functions in an [`AST`] (for `Engine::gen_fn_metadata_with_ast_to_json`)
|
|
||||||
2) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API
|
|
||||||
3) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in static modules
|
|
||||||
registered via `Engine::register_static_module`
|
|
||||||
4) Native Rust functions in global modules registered via `Engine::register_global_module` (optional)
|
|
||||||
|
|
||||||
Notice that if a function has been [overloaded][function overloading], only the overriding function's
|
|
||||||
metadata is included.
|
|
||||||
|
|
||||||
|
|
||||||
JSON Schema
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The JSON schema used to hold functions metadata is very simple, containing a nested structure of
|
|
||||||
`modules` and a list of `functions`.
|
|
||||||
|
|
||||||
### Modules Schema
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"modules":
|
|
||||||
{
|
|
||||||
"sub_module_1":
|
|
||||||
{
|
|
||||||
"modules":
|
|
||||||
{
|
|
||||||
"sub_sub_module_A":
|
|
||||||
{
|
|
||||||
"functions":
|
|
||||||
[
|
|
||||||
{ ... function metadata ... },
|
|
||||||
{ ... function metadata ... },
|
|
||||||
{ ... function metadata ... },
|
|
||||||
{ ... function metadata ... },
|
|
||||||
...
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"sub_sub_module_B":
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sub_module_2":
|
|
||||||
{
|
|
||||||
...
|
|
||||||
},
|
|
||||||
...
|
|
||||||
},
|
|
||||||
"functions":
|
|
||||||
[
|
|
||||||
{ ... function metadata ... },
|
|
||||||
{ ... function metadata ... },
|
|
||||||
{ ... function metadata ... },
|
|
||||||
{ ... function metadata ... },
|
|
||||||
...
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Function Metadata Schema
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"namespace": "internal" | "global",
|
|
||||||
"access": "public" | "private",
|
|
||||||
"name": "fn_name",
|
|
||||||
"type": "native" | "script",
|
|
||||||
"numParams": 42, /* number of parameters */
|
|
||||||
"params": /* omitted if no parameters */
|
|
||||||
[
|
|
||||||
{ "name": "param_1", "type": "type_1" },
|
|
||||||
{ "name": "param_2" }, /* no type info */
|
|
||||||
{ "name": "_", "type": "type_3" },
|
|
||||||
...
|
|
||||||
],
|
|
||||||
"returnType": "ret_type", /* omitted if unknown */
|
|
||||||
"signature": "[private] fn_name(param_1: type_1, param_2, _: type_3) -> ret_type",
|
|
||||||
"docComments": /* omitted if none */
|
|
||||||
[
|
|
||||||
"/// doc-comment line 1",
|
|
||||||
"/// doc-comment line 2",
|
|
||||||
"/** doc-comment block */",
|
|
||||||
...
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,91 +0,0 @@
|
|||||||
Generate Function Signatures
|
|
||||||
===========================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
`Engine::gen_fn_signatures`
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
As part of a _reflections_ API, `Engine::gen_fn_signatures` returns a list of function _signatures_
|
|
||||||
(as `Vec<String>`), each corresponding to a particular function available to that [`Engine`] instance.
|
|
||||||
|
|
||||||
> `fn_name ( param_1: type_1, param_2: type_2, ... , param_n : type_n ) -> return_type`
|
|
||||||
|
|
||||||
### Sources
|
|
||||||
|
|
||||||
Functions from the following sources are included, in order:
|
|
||||||
|
|
||||||
1) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API
|
|
||||||
2) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules
|
|
||||||
registered via `Engine::register_static_module`.
|
|
||||||
3) Native Rust functions in global modules registered via `Engine::register_global_module` (optional)
|
|
||||||
|
|
||||||
|
|
||||||
Functions Metadata
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Beware, however, that not all function signatures contain parameters and return value information.
|
|
||||||
|
|
||||||
### `Engine::register_XXX`
|
|
||||||
|
|
||||||
For instance, functions registered via `Engine::register_XXX` contain no information on
|
|
||||||
the names of parameter and their actual types because Rust simply does not make such metadata
|
|
||||||
available natively. The return type is also undetermined.
|
|
||||||
|
|
||||||
A function registered under the name `foo` with three parameters and unknown return type:
|
|
||||||
|
|
||||||
> `foo(_, _, _)`
|
|
||||||
|
|
||||||
An operator function – again, unknown parameters and return type.
|
|
||||||
Notice that function names do not need to be valid identifiers.
|
|
||||||
|
|
||||||
> `+(_, _)`
|
|
||||||
|
|
||||||
A [property setter][getters/setters] – again, unknown parameters and return type.
|
|
||||||
Notice that function names do not need to be valid identifiers.
|
|
||||||
In this case, the first parameter should be `&mut T` of the custom type and the return value is `()`:
|
|
||||||
|
|
||||||
> `set$prop(_, _, _)`
|
|
||||||
|
|
||||||
### Script-Defined Functions
|
|
||||||
|
|
||||||
Script-defined [function] signatures contain parameter names. Since all parameters, as well as
|
|
||||||
the return value, are [`Dynamic`] the types are simply not shown.
|
|
||||||
|
|
||||||
A script-defined function always takes dynamic arguments, and the return type is also dynamic,
|
|
||||||
so no type information is needed:
|
|
||||||
|
|
||||||
> `foo(x, y, z)`
|
|
||||||
|
|
||||||
probably defined as:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn foo(x, y, z) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
is the same as:
|
|
||||||
|
|
||||||
> `foo(x: Dynamic, y: Dynamic, z: Dynamic) -> Result<Dynamic, Box<EvalAltResult>>`
|
|
||||||
|
|
||||||
### Plugin Functions
|
|
||||||
|
|
||||||
Functions defined in [plugin modules] are the best. They contain all the metadata
|
|
||||||
describing the functions.
|
|
||||||
|
|
||||||
For example, a plugin function `merge`:
|
|
||||||
|
|
||||||
> `merge(list: &mut MyStruct<i64>, num: usize, name: &str) -> Option<bool>`
|
|
||||||
|
|
||||||
Notice that function names do not need to be valid identifiers.
|
|
||||||
|
|
||||||
For example, an operator defined as a [fallible function] in a [plugin module] via
|
|
||||||
`#[rhai_fn(name="+=", return_raw)]` returns `Result<bool, Box<EvalAltResult>>`:
|
|
||||||
|
|
||||||
> `+=(list: &mut MyStruct<i64>, num: usize, name: &str) -> Result<bool, Box<EvalAltResult>>`
|
|
||||||
|
|
||||||
For example, a [property getter][getters/setters] defined in a [plugin module]:
|
|
||||||
|
|
||||||
> `get$prop(obj: &mut MyStruct<i64>) -> String`
|
|
@ -1,28 +0,0 @@
|
|||||||
Functions Metadata
|
|
||||||
==================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
The _metadata_ of a [function] means all relevant information related to a function's
|
|
||||||
definition including:
|
|
||||||
|
|
||||||
1. Its callable name
|
|
||||||
|
|
||||||
2. Its access mode (public or [private][`private`])
|
|
||||||
|
|
||||||
3. Its parameters and types (if any)
|
|
||||||
|
|
||||||
4. Its return value and type (if any)
|
|
||||||
|
|
||||||
5. Its nature (i.e. native Rust-based or Rhai script-based)
|
|
||||||
|
|
||||||
6. Its [namespace][function namespace] (module or global)
|
|
||||||
|
|
||||||
7. Its purpose, in the form of [doc-comments]
|
|
||||||
|
|
||||||
8. Usage notes, warnings, etc., in the form of [doc-comments]
|
|
||||||
|
|
||||||
A function's _signature_ encapsulates the first four pieces of information in a single
|
|
||||||
concise line of definition:
|
|
||||||
|
|
||||||
> `[private] fn_name ( param_1: type_1, param_2: type_2, ... , param_n : type_n ) -> return_type`
|
|
@ -1,24 +0,0 @@
|
|||||||
Turn Off Script Optimizations
|
|
||||||
============================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
When scripts:
|
|
||||||
|
|
||||||
* are known to be run only _once_,
|
|
||||||
|
|
||||||
* are known to contain no dead code,
|
|
||||||
|
|
||||||
* do not use constants in calculations
|
|
||||||
|
|
||||||
the optimization pass may be a waste of time and resources. In that case, turn optimization off
|
|
||||||
by setting the optimization level to [`OptimizationLevel::None`].
|
|
||||||
|
|
||||||
Alternatively, turn off optimizations via the [`no_optimize`] feature.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let engine = rhai::Engine::new();
|
|
||||||
|
|
||||||
// Turn off the optimizer
|
|
||||||
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
|
||||||
```
|
|
@ -1,26 +0,0 @@
|
|||||||
Eager Function Evaluation When Using Full Optimization Level
|
|
||||||
==========================================================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
When the optimization level is [`OptimizationLevel::Full`], the [`Engine`] assumes all functions to be _pure_
|
|
||||||
and will _eagerly_ evaluated all function calls with constant arguments, using the result to replace the call.
|
|
||||||
|
|
||||||
This also applies to all operators (which are implemented as functions).
|
|
||||||
|
|
||||||
For instance, the same example above:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// When compiling the following with OptimizationLevel::Full...
|
|
||||||
|
|
||||||
const DECISION = 1;
|
|
||||||
// this condition is now eliminated because 'sign(DECISION) > 0'
|
|
||||||
if DECISION.sign() > 0 { // is a call to the 'sign' and '>' functions, and they return 'true'
|
|
||||||
print("hello!"); // this block is promoted to the parent level
|
|
||||||
} else {
|
|
||||||
print("boo!"); // this block is eliminated because it is never reached
|
|
||||||
}
|
|
||||||
|
|
||||||
print("hello!"); // <- the above is equivalent to this
|
|
||||||
// ('print' and 'debug' are handled specially)
|
|
||||||
```
|
|
@ -1,146 +0,0 @@
|
|||||||
Script Optimization
|
|
||||||
===================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
Rhai includes an _optimizer_ that tries to optimize a script after parsing.
|
|
||||||
This can reduce resource utilization and increase execution speed.
|
|
||||||
|
|
||||||
Script optimization can be turned off via the [`no_optimize`] feature.
|
|
||||||
|
|
||||||
|
|
||||||
Dead Code Removal
|
|
||||||
----------------
|
|
||||||
|
|
||||||
For example, in the following:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
{
|
|
||||||
let x = 999; // NOT eliminated: variable may be used later on (perhaps even an 'eval')
|
|
||||||
123; // eliminated: no effect
|
|
||||||
"hello"; // eliminated: no effect
|
|
||||||
[1, 2, x, x*2, 5]; // eliminated: no effect
|
|
||||||
foo(42); // NOT eliminated: the function 'foo' may have side-effects
|
|
||||||
666 // NOT eliminated: this is the return value of the block,
|
|
||||||
// and the block is the last one so this is the return value of the whole script
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Rhai attempts to eliminate _dead code_ (i.e. code that does nothing, for example an expression by itself as a statement,
|
|
||||||
which is allowed in Rhai).
|
|
||||||
|
|
||||||
The above script optimizes to:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
{
|
|
||||||
let x = 999;
|
|
||||||
foo(42);
|
|
||||||
666
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Constants Propagation
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Constants propagation is used to remove dead code:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
const ABC = true;
|
|
||||||
|
|
||||||
if ABC || some_work() { print("done!"); } // 'ABC' is constant so it is replaced by 'true'...
|
|
||||||
|
|
||||||
if true || some_work() { print("done!"); } // since '||' short-circuits, 'some_work' is never called
|
|
||||||
|
|
||||||
if true { print("done!"); } // <- the line above is equivalent to this
|
|
||||||
|
|
||||||
print("done!"); // <- the line above is further simplified to this
|
|
||||||
// because the condition is always true
|
|
||||||
```
|
|
||||||
|
|
||||||
These are quite effective for template-based machine-generated scripts where certain constant values
|
|
||||||
are spliced into the script text in order to turn on/off certain sections.
|
|
||||||
|
|
||||||
For fixed script texts, the constant values can be provided in a user-defined [`Scope`] object
|
|
||||||
to the [`Engine`] for use in compilation and evaluation.
|
|
||||||
|
|
||||||
### Caveat
|
|
||||||
|
|
||||||
If the [constants] are modified later on (yes, it is possible, via Rust functions),
|
|
||||||
the modified values will not show up in the optimized script.
|
|
||||||
Only the initialization values of [constants] are ever retained.
|
|
||||||
|
|
||||||
This is almost never a problem because real-world scripts seldom modify a constant,
|
|
||||||
but the possibility is always there.
|
|
||||||
|
|
||||||
|
|
||||||
Eager Operator Evaluations
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Beware, however, that most operators are actually function calls, and those functions can be overridden,
|
|
||||||
so whether they are optimized away depends on the situation:
|
|
||||||
|
|
||||||
* If the operands are not _constant_ values, it is not optimized.
|
|
||||||
|
|
||||||
* If the operator is [overloaded][operator overloading], it is not optimized because the overloading function may not be _pure_
|
|
||||||
(i.e. may cause side-effects when called).
|
|
||||||
|
|
||||||
* If the operator is not _binary_, it is not optimized. Only binary operators are built-in to Rhai.
|
|
||||||
|
|
||||||
* If the operands are not of the same type, it is not optimized.
|
|
||||||
|
|
||||||
* If the operator is not _built-in_ (see list of [built-in operators]), it is not optimized.
|
|
||||||
|
|
||||||
* If the operator is a binary built-in operator for a [standard type][standard types], it is called and replaced by a constant result.
|
|
||||||
|
|
||||||
Rhai guarantees that no external function will be run (in order not to trigger side-effects) during the
|
|
||||||
optimization process (unless the optimization level is set to [`OptimizationLevel::Full`]).
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// The following is most likely generated by machine.
|
|
||||||
|
|
||||||
const DECISION = 1; // this is an integer, one of the standard types
|
|
||||||
|
|
||||||
if DECISION == 1 { // this is optimized into 'true'
|
|
||||||
:
|
|
||||||
} else if DECISION == 2 { // this is optimized into 'false'
|
|
||||||
:
|
|
||||||
} else if DECISION == 3 { // this is optimized into 'false'
|
|
||||||
:
|
|
||||||
} else {
|
|
||||||
:
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Because of the eager evaluation of operators for [standard types], many constant expressions will be evaluated
|
|
||||||
and replaced by the result.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = (1+2) * 3-4 / 5%6; // will be replaced by 'let x = 9'
|
|
||||||
|
|
||||||
let y = (1 > 2) || (3 < =4); // will be replaced by 'let y = true'
|
|
||||||
```
|
|
||||||
|
|
||||||
For operators that are not optimized away due to one of the above reasons, the function calls
|
|
||||||
are simply left behind:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Assume 'new_state' returns some custom type that is NOT one of the standard types.
|
|
||||||
// Also assume that the '==; operator is defined for that custom type.
|
|
||||||
const DECISION_1 = new_state(1);
|
|
||||||
const DECISION_2 = new_state(2);
|
|
||||||
const DECISION_3 = new_state(3);
|
|
||||||
|
|
||||||
if DECISION == 1 { // NOT optimized away because the operator is not built-in
|
|
||||||
: // and may cause side-effects if called!
|
|
||||||
:
|
|
||||||
} else if DECISION == 2 { // same here, NOT optimized away
|
|
||||||
:
|
|
||||||
} else if DECISION == 3 { // same here, NOT optimized away
|
|
||||||
:
|
|
||||||
} else {
|
|
||||||
:
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, turn the optimizer to [`OptimizationLevel::Full`].
|
|
@ -1,34 +0,0 @@
|
|||||||
Optimization Levels
|
|
||||||
==================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
There are three levels of optimization: `None`, `Simple` and `Full`.
|
|
||||||
|
|
||||||
* `None` is obvious – no optimization on the AST is performed.
|
|
||||||
|
|
||||||
* `Simple` (default) performs only relatively _safe_ optimizations without causing side-effects
|
|
||||||
(i.e. it only relies on static analysis and [built-in operators] for constant [standard types],
|
|
||||||
and will not perform any external function calls).
|
|
||||||
|
|
||||||
However, it is important to bear in mind that _constants propagation_ is performed with the
|
|
||||||
caveat that, if [constants] are modified later on (yes, it is possible, via Rust functions),
|
|
||||||
the modified values will not show up in the optimized script. Only the initialization values
|
|
||||||
of [constants] are ever retained.
|
|
||||||
|
|
||||||
Furthermore, overriding a [built-in operator][built-in operators] in the [`Engine`] afterwards
|
|
||||||
has no effect after the optimizer replaces an expression with its calculated value.
|
|
||||||
|
|
||||||
* `Full` is _much_ more aggressive, _including_ calling external 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
|
|
||||||
// Turn on aggressive optimizations
|
|
||||||
engine.set_optimization_level(rhai::OptimizationLevel::Full);
|
|
||||||
```
|
|
@ -1,38 +0,0 @@
|
|||||||
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`].
|
|
||||||
|
|
||||||
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 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
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
scope.push_constant("SCENARIO", 1_i64);
|
|
||||||
|
|
||||||
// Re-optimize the AST
|
|
||||||
let new_ast = engine.optimize_ast(&scope, master_ast.clone(), OptimizationLevel::Simple);
|
|
||||||
|
|
||||||
// 'new_ast' is essentially: 'do_work()'
|
|
||||||
```
|
|
@ -1,43 +0,0 @@
|
|||||||
Subtle Semantic Changes After Optimization
|
|
||||||
=========================================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
Some optimizations can alter subtle semantics of the script.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
if true { // condition always true
|
|
||||||
123.456; // eliminated
|
|
||||||
hello; // eliminated, EVEN THOUGH the variable doesn't exist!
|
|
||||||
foo(42) // promoted up-level
|
|
||||||
}
|
|
||||||
|
|
||||||
foo(42) // <- the above optimizes to this
|
|
||||||
```
|
|
||||||
|
|
||||||
If the original script were evaluated instead, it would have been an error – the variable `hello` does not exist,
|
|
||||||
so the script would have been terminated at that point with an error return.
|
|
||||||
|
|
||||||
In fact, any errors inside a statement that has been eliminated will silently _disappear_:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
print("start!");
|
|
||||||
if my_decision { /* do nothing... */ } // eliminated due to no effect
|
|
||||||
print("end!");
|
|
||||||
|
|
||||||
// The above optimizes to:
|
|
||||||
|
|
||||||
print("start!");
|
|
||||||
print("end!");
|
|
||||||
```
|
|
||||||
|
|
||||||
In the script above, if `my_decision` holds anything other than a boolean value,
|
|
||||||
the script should have been terminated due to a type error.
|
|
||||||
|
|
||||||
However, after optimization, the entire `if` statement is removed (because an access to `my_decision` produces
|
|
||||||
no side-effects), thus the script silently runs to completion without errors.
|
|
||||||
|
|
||||||
It is usually a _Very Bad Idea™_ to depend on a script failing or such kind of subtleties, but if it turns out to be necessary
|
|
||||||
(why? I would never guess), turn script optimization off by setting the optimization level to [`OptimizationLevel::None`].
|
|
@ -1,22 +0,0 @@
|
|||||||
Side-Effect Considerations for Full Optimization Level
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
All of Rhai's built-in functions (and operators which are implemented as functions) are _pure_
|
|
||||||
(i.e. they do not mutate state nor cause any side-effects, with the exception of `print` and `debug`
|
|
||||||
which are handled specially) so using [`OptimizationLevel::Full`] is usually quite safe _unless_
|
|
||||||
custom types and functions are registered.
|
|
||||||
|
|
||||||
If custom functions are registered, they _may_ be called (or maybe not, if the calls happen to lie
|
|
||||||
within a pruned code block).
|
|
||||||
|
|
||||||
If custom functions are registered to overload built-in operators, they will also be called when
|
|
||||||
the operators are used (in an `if` statement, for example) causing side-effects.
|
|
||||||
|
|
||||||
Therefore, the rule-of-thumb is:
|
|
||||||
|
|
||||||
* _Always_ register custom types and functions _after_ compiling scripts if [`OptimizationLevel::Full`] is used.
|
|
||||||
|
|
||||||
* _DO NOT_ depend on knowledge that the functions have no side-effects, because those functions can change later on and,
|
|
||||||
when that happens, existing scripts may break in subtle ways.
|
|
@ -1,16 +0,0 @@
|
|||||||
Volatility Considerations for Full Optimization Level
|
|
||||||
===================================================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
Even if a custom function does not mutate state nor cause side-effects, it may still be _volatile_,
|
|
||||||
i.e. it _depends_ on the external environment and is not _pure_.
|
|
||||||
|
|
||||||
A perfect example is a function that gets the current time – obviously each run will return a different value!
|
|
||||||
|
|
||||||
The optimizer, when using [`OptimizationLevel::Full`], will _merrily assume_ that all functions are _pure_,
|
|
||||||
so when it finds constant arguments (or none) it eagerly executes the function call and replaces it with the result.
|
|
||||||
|
|
||||||
This causes the script to behave differently from the intended semantics.
|
|
||||||
|
|
||||||
Therefore, **avoid using [`OptimizationLevel::Full`]** if non-_pure_ custom types and/or functions are involved.
|
|
@ -1,19 +0,0 @@
|
|||||||
Engine Configuration Options
|
|
||||||
===========================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
A number of other configuration options are available from the `Engine` to fine-tune behavior and safeguards.
|
|
||||||
|
|
||||||
| Method | Not available under | Description |
|
|
||||||
| ------------------------ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `set_doc_comments` | | enables/disables [doc-comments] |
|
|
||||||
| `set_optimization_level` | [`no_optimize`] | sets the amount of script _optimizations_ performedSee [script optimization] |
|
|
||||||
| `set_max_expr_depths` | [`unchecked`] | sets the maximum nesting levels of an expression/statementSee [maximum statement depth] |
|
|
||||||
| `set_max_call_levels` | [`unchecked`] | sets the maximum number of function call levels (default 50) to avoid infinite recursionSee [maximum call stack depth] |
|
|
||||||
| `set_max_operations` | [`unchecked`] | sets the maximum number of _operations_ that a script is allowed to consumeSee [maximum number of operations] |
|
|
||||||
| `set_max_modules` | [`unchecked`] | sets the maximum number of [modules] that a script is allowed to loadSee [maximum number of modules] |
|
|
||||||
| `set_max_string_size` | [`unchecked`] | sets the maximum length (in UTF-8 bytes) for [strings]See [maximum length of strings] |
|
|
||||||
| `set_max_array_size` | [`unchecked`], [`no_index`] | sets the maximum size for [arrays]See [maximum size of arrays] |
|
|
||||||
| `set_max_map_size` | [`unchecked`], [`no_object`] | sets the maximum number of properties for [object maps]See [maximum size of object maps] |
|
|
||||||
| `disable_symbol` | | disables a certain keyword or operatorSee [disable keywords and operators] |
|
|
@ -1,28 +0,0 @@
|
|||||||
Raw `Engine`
|
|
||||||
===========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
`Engine::new` creates a scripting [`Engine`] with common functionalities (e.g. printing to the console via `print`).
|
|
||||||
|
|
||||||
In many controlled embedded environments, however, these may not be needed and unnecessarily occupy
|
|
||||||
application code storage space.
|
|
||||||
|
|
||||||
Use `Engine::new_raw` to create a _raw_ `Engine`, in which only a minimal set of
|
|
||||||
basic arithmetic and logical operators are supported (see below).
|
|
||||||
|
|
||||||
To add more functionalities to a _raw_ `Engine`, load [packages] into it.
|
|
||||||
|
|
||||||
|
|
||||||
Built-in Operators
|
|
||||||
------------------
|
|
||||||
|
|
||||||
| Operators | Assignment operators | Supported for types<br/>(see [standard types]) |
|
|
||||||
| ------------------------- | ---------------------------- | ----------------------------------------------------------------------------- |
|
|
||||||
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `char`, `ImmutableString` |
|
|
||||||
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
|
|
||||||
| `<<`, `>>` | `<<=`, `>>=` | `INT` |
|
|
||||||
| `&`, <code>\|</code>, `^` | `&=`, <code>\|=</code>, `^=` | `INT`, `bool` |
|
|
||||||
| `&&`, <code>\|\|</code> | | `bool` |
|
|
||||||
| `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
|
|
||||||
| `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |
|
|
@ -1,55 +0,0 @@
|
|||||||
`Scope` – Initializing and Maintaining State
|
|
||||||
=================================================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
By default, Rhai treats each [`Engine`] invocation as a fresh one, persisting only the functions that have been defined
|
|
||||||
but no global state. This gives each evaluation a clean starting slate.
|
|
||||||
|
|
||||||
In order to continue using the same global state from one invocation to the next,
|
|
||||||
such a state must be manually created and passed in.
|
|
||||||
|
|
||||||
All `Scope` variables are [`Dynamic`], meaning they can store values of any type.
|
|
||||||
|
|
||||||
Under [`sync`], however, only types that are `Send + Sync` are supported, and the entire `Scope` itself
|
|
||||||
will also be `Send + Sync`. This is extremely useful in multi-threaded applications.
|
|
||||||
|
|
||||||
In this example, a global state object (a `Scope`) is created with a few initialized variables,
|
|
||||||
then the same state is threaded through multiple invocations:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::{Engine, Scope, EvalAltResult};
|
|
||||||
|
|
||||||
let engine = Engine::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)
|
|
||||||
.push("z", 999_i64)
|
|
||||||
.push_constant("MY_NUMBER", 123_i64) // constants can also be added
|
|
||||||
.set_value("s", "hello, world!".to_string()); //'set_value' adds a variable when one doesn't exist
|
|
||||||
// remember to use 'String', not '&str'
|
|
||||||
|
|
||||||
// First invocation
|
|
||||||
engine.eval_with_scope::<()>(&mut scope, r"
|
|
||||||
let x = 4 + 5 – y + z + MY_NUMBER + s.len;
|
|
||||||
y = 1;
|
|
||||||
")?;
|
|
||||||
|
|
||||||
// Second invocation using the same state
|
|
||||||
let result = engine.eval_with_scope::<i64>(&mut scope, "x")?;
|
|
||||||
|
|
||||||
println!("result: {}", result); // prints 1102
|
|
||||||
|
|
||||||
// Variable y is changed in the script – read it with 'get_value'
|
|
||||||
assert_eq!(scope.get_value::<i64>("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::<i64>("y").expect("variable y should exist"), 42);
|
|
||||||
```
|
|
@ -1,93 +0,0 @@
|
|||||||
Variable Resolver
|
|
||||||
=================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
By default, Rhai looks up access to variables from the enclosing block scope,
|
|
||||||
working its way outwards until it reaches the top (global) level, then it
|
|
||||||
searches the [`Scope`] that is passed into the `Engine::eval` call.
|
|
||||||
|
|
||||||
There is a built-in facility for advanced users to _hook_ into the variable
|
|
||||||
resolution service and to override its default behavior.
|
|
||||||
|
|
||||||
To do so, provide a closure to the [`Engine`] via the `Engine::on_var` method:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
|
|
||||||
// Register a variable resolver.
|
|
||||||
engine.on_var(|name, index, context| {
|
|
||||||
match name {
|
|
||||||
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
|
||||||
// Override a variable - make it not found even if it exists!
|
|
||||||
"DO_NOT_USE" => Err(Box::new(
|
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
|
||||||
)),
|
|
||||||
// Silently maps 'chameleon' into 'innocent'.
|
|
||||||
"chameleon" => context.scope().get_value("innocent").map(Some).ok_or_else(|| Box::new(
|
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
|
||||||
)),
|
|
||||||
// Return Ok(None) to continue with the normal variable resolution process.
|
|
||||||
_ => Ok(None)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Returned Values are Constants
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Variable values, if any returned, are treated as _constants_ by the script and cannot be assigned to.
|
|
||||||
This is to avoid needing a mutable reference to the underlying data provider which may not be possible to obtain.
|
|
||||||
|
|
||||||
In order to change these variables, it is best to push them into a custom [`Scope`] instead of using
|
|
||||||
a variable resolver. Then these variables can be assigned to and their updated values read back after
|
|
||||||
the script is evaluated.
|
|
||||||
|
|
||||||
|
|
||||||
Benefits of Using a Variable Resolver
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
1. Avoid having to maintain a custom [`Scope`] with all variables regardless of need (because a script may not use them all).
|
|
||||||
|
|
||||||
2. _Short-circuit_ variable access, essentially overriding standard behavior.
|
|
||||||
|
|
||||||
3. _Lazy-load_ variables when they are accessed, not up-front. This benefits when the number of variables is very large, when they are timing-dependent, or when they are expensive to load.
|
|
||||||
|
|
||||||
4. Rename system variables on a script-by-script basis without having to construct different [`Scope`]'s.
|
|
||||||
|
|
||||||
|
|
||||||
Function Signature
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The function signature passed to `Engine::on_var` takes the following form:
|
|
||||||
|
|
||||||
> `Fn(name: &str, index: usize, context: &EvalContext)`
|
|
||||||
> `-> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static`
|
|
||||||
|
|
||||||
where:
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
| -------------------------- | :-------------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `name` | `&str` | variable name |
|
|
||||||
| `index` | `usize` | an offset from the bottom of the current [`Scope`] that the variable is supposed to reside.<br/>Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`.<br/>If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. |
|
|
||||||
| `context` | `&EvalContext` | reference to the current evaluation _context_ |
|
|
||||||
| • `scope()` | `&Scope` | reference to the current [`Scope`] |
|
|
||||||
| • `engine()` | `&Engine` | reference to the current [`Engine`] |
|
|
||||||
| • `source()` | `Option<&str>` | reference to the current source, if any |
|
|
||||||
| • `iter_imports()` | `impl Iterator<Item = (&str, &Module)>` | iterator of the current stack of [modules] imported via `import` statements |
|
|
||||||
| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature |
|
|
||||||
| • `iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
|
||||||
| • `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature |
|
|
||||||
| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
|
|
||||||
| • `call_level()` | `usize` | the current nesting level of function calls |
|
|
||||||
|
|
||||||
### Return Value
|
|
||||||
|
|
||||||
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
|
|
||||||
|
|
||||||
| Value | Description |
|
|
||||||
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] |
|
|
||||||
| `Ok(Some(Dynamic))` | value of the variable, treated as a constant |
|
|
||||||
| `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, Position::NONE)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |
|
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-2132,0)">
|
|
||||||
<g id="favicon" transform="matrix(0.591824,0,0,0.558091,1146.76,0)">
|
|
||||||
<rect x="1664.76" y="0" width="54.07" height="57.338" style="fill:none;"/>
|
|
||||||
<g transform="matrix(0.180947,0,0,0.191884,1645.83,-30.569)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear1);"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 22 KiB |
@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 960 272" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,0,-962)">
|
|
||||||
<g id="rhai-banner-transparent-colour" transform="matrix(0.588957,0,0,0.998422,0,1.39642)">
|
|
||||||
<rect x="0" y="962.121" width="1630" height="272.43" style="fill:none;"/>
|
|
||||||
<g transform="matrix(1.69792,0,0,0.751185,-277.179,872.981)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear1);"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 23 KiB |
@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 800 450" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-830,-480)">
|
|
||||||
<g id="rhai-colour-white" transform="matrix(1,0,0,0.75,830,480)">
|
|
||||||
<rect x="0" y="0" width="800" height="600" style="fill:none;"/>
|
|
||||||
<clipPath id="_clip1">
|
|
||||||
<rect x="0" y="0" width="800" height="600"/>
|
|
||||||
</clipPath>
|
|
||||||
<g clip-path="url(#_clip1)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-641.217)">
|
|
||||||
<rect x="0" y="480.913" width="800" height="450"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear2);"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 23 KiB |
@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 800 450" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-830,0)">
|
|
||||||
<g id="rhai-colour-white" transform="matrix(1,0,0,0.75,830,0)">
|
|
||||||
<rect x="0" y="0" width="800" height="600" style="fill:none;"/>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,0)">
|
|
||||||
<rect x="0" y="0" width="800" height="450" style="fill:white;"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear1);"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 17 KiB |
@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 450 450" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-1656,-480)">
|
|
||||||
<g id="rhai-icon-colour-black" transform="matrix(1.55094,0,0,1.55094,-913.734,480)">
|
|
||||||
<rect x="1656.88" y="0" width="290.146" height="290.146" style="fill:none;"/>
|
|
||||||
<clipPath id="_clip1">
|
|
||||||
<rect x="1656.88" y="0" width="290.146" height="290.146"/>
|
|
||||||
</clipPath>
|
|
||||||
<g clip-path="url(#_clip1)">
|
|
||||||
<g transform="matrix(0.362683,0,0,0.644769,1656.89,-310.078)">
|
|
||||||
<rect x="0" y="480.913" width="800" height="450"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.644769,0,0,0.644769,1638.17,-54.3573)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear2);"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 17 KiB |
@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 450 450" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-1656,0)">
|
|
||||||
<g id="rhai-icon-colour-white" transform="matrix(1.55094,0,0,1.55094,-913.734,0)">
|
|
||||||
<rect x="1656.88" y="0" width="290.146" height="290.146" style="fill:none;"/>
|
|
||||||
<clipPath id="_clip1">
|
|
||||||
<rect x="1656.88" y="0" width="290.146" height="290.146"/>
|
|
||||||
</clipPath>
|
|
||||||
<g clip-path="url(#_clip1)">
|
|
||||||
<g transform="matrix(0.644769,0,0,0.644769,589.147,0)">
|
|
||||||
<rect x="1656" y="0" width="450" height="450" style="fill:white;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.644769,0,0,0.644769,1638.17,-54.3573)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear2);"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.9 KiB |
@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 132 224" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-188,-113)">
|
|
||||||
<g transform="matrix(1,0,0,0.75,0,0)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 15 KiB |
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 132 224" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-1815,-113)">
|
|
||||||
<g transform="matrix(1.55094,0,0,1.55094,-913.734,0)">
|
|
||||||
<g transform="matrix(0.644769,0,0,0.644769,1638.17,-54.3573)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear1);"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 4.4 KiB |
@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 132 224" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-188,-593)">
|
|
||||||
<g transform="matrix(1,0,0,0.75,0,480)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:white;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 20 KiB |
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 424 224" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-1018,-113)">
|
|
||||||
<g transform="matrix(1,0,0,0.75,830,0)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear1);"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 20 KiB |
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 424 224" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-1018,-593)">
|
|
||||||
<g transform="matrix(1,0,0,0.75,830,480)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:url(#_Linear1);"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.36312e-14,222.614,-222.614,1.36312e-14,254.029,197.998)"><stop offset="0" style="stop-color:rgb(255,215,118);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(246,117,0);stop-opacity:1"/></linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 7.2 KiB |
@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 424 224" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-188,-113)">
|
|
||||||
<g transform="matrix(1,0,0,0.75,0,0)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 8.1 KiB |
@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 424 224" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,-188,-593)">
|
|
||||||
<g transform="matrix(1,0,0,0.75,0,480)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:white;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 11 KiB |
@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 800 450" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1,0,0,1,0,-480)">
|
|
||||||
<g id="rhai-sil-black" transform="matrix(1,0,0,0.75,0,480)">
|
|
||||||
<rect x="0" y="0" width="800" height="600" style="fill:none;"/>
|
|
||||||
<clipPath id="_clip1">
|
|
||||||
<rect x="0" y="0" width="800" height="600"/>
|
|
||||||
</clipPath>
|
|
||||||
<g clip-path="url(#_clip1)">
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-641.217)">
|
|
||||||
<rect x="0" y="480.913" width="800" height="450"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z" style="fill:white;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill:white;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 12 KiB |
@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 800 450" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g id="rhai-sil-white" transform="matrix(1,0,0,0.75,0,0)">
|
|
||||||
<rect x="0" y="0" width="800" height="600" style="fill:none;"/>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,0)">
|
|
||||||
<rect x="0" y="0" width="800" height="450" style="fill:white;"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,0,-112.407)">
|
|
||||||
<path d="M244.724,402.002L226.114,402.002C205.572,402.002 188.894,385.324 188.894,364.782L188.894,253.122L188.895,253.242C188.959,263.418 197.208,271.667 207.384,271.732L207.504,271.732L218.288,271.732C211.035,256.892 213.572,238.455 225.902,226.126L254.029,197.998C254.029,197.998 282.157,226.126 282.157,226.126C294.486,238.455 297.023,256.892 289.77,271.732L300.554,271.732L300.674,271.732C310.85,271.667 319.099,263.418 319.164,253.242L319.164,253.122L319.164,364.782C319.164,385.324 302.486,402.002 281.944,402.002L263.334,402.002L263.334,420.612L244.724,420.612L244.724,402.002ZM244.724,383.392L244.724,364.782C244.724,354.511 236.385,346.172 226.114,346.172L207.504,346.172L207.504,364.782C207.504,375.053 215.843,383.392 226.114,383.392L244.724,383.392ZM263.334,364.782C263.334,354.511 271.673,346.172 281.944,346.172L300.554,346.172L300.554,364.782C300.554,375.053 292.215,383.392 281.944,383.392L263.334,383.392L263.334,364.782ZM263.334,308.952C263.334,298.681 271.673,290.342 281.944,290.342L300.554,290.342L300.554,308.952C300.554,319.223 292.215,327.562 281.944,327.562L263.334,327.562L263.334,308.952ZM244.724,308.952C244.724,298.681 236.385,290.342 226.114,290.342L207.504,290.342L207.504,308.952C207.504,319.223 215.843,327.562 226.114,327.562L244.724,327.562L244.724,308.952ZM267.188,267.412C274.451,260.15 274.451,248.357 267.188,241.094C267.188,241.094 254.029,227.935 254.029,227.935L240.87,241.094C233.607,248.357 233.607,260.15 240.87,267.412L254.029,280.572L267.188,267.412Z"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1.33333,95.1496,-261.61)">
|
|
||||||
<g transform="matrix(144,0,0,144,256.612,491.233)">
|
|
||||||
<path d="M0.231,-0.512C0.246,-0.522 0.263,-0.53 0.281,-0.535C0.301,-0.54 0.32,-0.543 0.339,-0.543C0.352,-0.543 0.363,-0.542 0.374,-0.54L0.374,-0.428C0.362,-0.431 0.349,-0.433 0.336,-0.433C0.309,-0.433 0.284,-0.427 0.262,-0.415C0.238,-0.402 0.22,-0.383 0.209,-0.359C0.197,-0.335 0.191,-0.307 0.191,-0.274L0.191,-0L0.07,-0L0.07,-0.54L0.177,-0.54L0.177,-0.454C0.192,-0.479 0.21,-0.499 0.231,-0.512Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,314.068,491.233)">
|
|
||||||
<path d="M0.353,-0.555C0.438,-0.555 0.495,-0.528 0.524,-0.472C0.552,-0.418 0.566,-0.361 0.566,-0.302L0.566,-0L0.444,-0L0.444,-0.26C0.444,-0.381 0.402,-0.442 0.317,-0.442C0.281,-0.442 0.251,-0.43 0.227,-0.405C0.203,-0.38 0.191,-0.338 0.191,-0.278L0.191,-0L0.069,-0L0.069,-0.72L0.177,-0.72L0.177,-0.48C0.218,-0.53 0.277,-0.555 0.353,-0.555Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,404.212,491.233)">
|
|
||||||
<path d="M0.496,-0.455C0.505,-0.438 0.51,-0.42 0.513,-0.402C0.515,-0.383 0.516,-0.36 0.516,-0.331L0.516,-0L0.411,-0L0.411,-0.073C0.388,-0.043 0.361,-0.021 0.331,-0.007C0.3,0.008 0.264,0.015 0.221,0.015C0.183,0.015 0.15,0.008 0.123,-0.007C0.096,-0.022 0.075,-0.041 0.061,-0.066C0.047,-0.091 0.04,-0.118 0.04,-0.148C0.04,-0.187 0.05,-0.221 0.07,-0.248C0.09,-0.275 0.121,-0.295 0.163,-0.31C0.189,-0.318 0.22,-0.325 0.256,-0.332C0.292,-0.338 0.339,-0.345 0.398,-0.353C0.396,-0.385 0.386,-0.408 0.369,-0.423C0.351,-0.438 0.324,-0.445 0.287,-0.445C0.26,-0.445 0.236,-0.439 0.215,-0.427C0.193,-0.414 0.178,-0.395 0.17,-0.369L0.059,-0.404C0.073,-0.451 0.099,-0.488 0.138,-0.515C0.176,-0.542 0.226,-0.555 0.288,-0.555C0.393,-0.555 0.462,-0.522 0.496,-0.455ZM0.384,-0.171C0.391,-0.188 0.396,-0.216 0.397,-0.255C0.358,-0.249 0.324,-0.243 0.295,-0.237C0.265,-0.232 0.241,-0.226 0.224,-0.221C0.202,-0.212 0.186,-0.203 0.174,-0.192C0.164,-0.181 0.158,-0.167 0.158,-0.15C0.158,-0.129 0.166,-0.113 0.181,-0.1C0.196,-0.087 0.217,-0.081 0.245,-0.081C0.282,-0.081 0.313,-0.09 0.338,-0.109C0.362,-0.128 0.377,-0.148 0.384,-0.171Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(144,0,0,144,487.156,491.233)">
|
|
||||||
<path d="M0.08,-0.734L0.2,-0.734L0.2,-0.624L0.08,-0.624L0.08,-0.734ZM0.08,-0.54L0.2,-0.54L0.2,-0L0.08,-0L0.08,-0.54Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.8 KiB |
@ -1,218 +0,0 @@
|
|||||||
Arrays
|
|
||||||
======
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Arrays are first-class citizens in Rhai. Like C, arrays are accessed with zero-based, non-negative integer indices:
|
|
||||||
|
|
||||||
> _array_ `[` _index_ `]`
|
|
||||||
|
|
||||||
Array literals are built within square brackets '`[`' ... '`]`' and separated by commas '`,`':
|
|
||||||
|
|
||||||
> `[` _value_ `,` _value_ `,` `...` `,` _value_ `]`
|
|
||||||
>
|
|
||||||
> `[` _value_ `,` _value_ `,` `...` `,` _value_ `,` `]` `// trailing comma is OK`
|
|
||||||
|
|
||||||
All elements stored in an array are [`Dynamic`], and the array can freely grow or shrink with elements added or removed.
|
|
||||||
|
|
||||||
The Rust type of a Rhai array is `rhai::Array`.
|
|
||||||
|
|
||||||
[`type_of()`] an array returns `"array"`.
|
|
||||||
|
|
||||||
Arrays are disabled via the [`no_index`] feature.
|
|
||||||
|
|
||||||
The maximum allowed size of an array can be controlled via `Engine::set_max_array_size`
|
|
||||||
(see [maximum size of arrays].
|
|
||||||
|
|
||||||
|
|
||||||
Built-in Functions
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
The following methods (mostly defined in the [`BasicArrayPackage`][packages] but excluded if using a [raw `Engine`]) operate on arrays:
|
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
|
||||||
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `push` | element to insert | inserts an element at the end |
|
|
||||||
| `append` | array to append | concatenates the second array to the end of the first |
|
|
||||||
| `+=` operator | 1) array<br/>2) element to insert (not another array) | inserts an element at the end |
|
|
||||||
| `+=` operator | 1) array<br/>2) array to append | concatenates the second array to the end of the first |
|
|
||||||
| `+` operator | 1) first array<br/>2) second array | concatenates the first array with the second |
|
|
||||||
| `==` operator | 1) first array<br/>2) second array | are the two arrays the same (elements compared with the `==` operator, if defined)? |
|
|
||||||
| `!=` operator | 1) first array<br/>2) second array | are the two arrays different (elements compared with the `==` operator, if defined)? |
|
|
||||||
| `in` operator | item to find | does the array contain the item (compared with the `==` operator, if defined)? |
|
|
||||||
| `insert` | 1) element to insert<br/>2) position, beginning if < 0, end if > length | inserts an element at a certain index |
|
|
||||||
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
|
|
||||||
| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) |
|
|
||||||
| `extract` | 1) start position, beginning if < 0, end if > length<br/>2) _(optional)_ number of items to extract, none if < 0 | extracts a portion of the array into a new array |
|
|
||||||
| `remove` | index | removes an element at a particular index and returns it ([`()`] if the index is not valid) |
|
|
||||||
| `reverse` | _none_ | reverses the array |
|
|
||||||
| `len` method and property | _none_ | returns the number of elements |
|
|
||||||
| `pad` | 1) target length<br/>2) element to pad | pads the array with an element to at least a specified length |
|
|
||||||
| `clear` | _none_ | empties the array |
|
|
||||||
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
|
|
||||||
| `chop` | target length | cuts off the head of the array, leaving the tail at exactly a specified length |
|
|
||||||
| `drain` | 1) [function pointer] to predicate (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
|
||||||
| `drain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0 | removes a portion of the array, returning the removed items (not in original order) |
|
|
||||||
| `retain` | 1) [function pointer] to predicate (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that do not return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
|
||||||
| `retain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to retain, none if < 0 | retains a portion of the array, removes all other items and returning them (not in original order) |
|
|
||||||
| `splice` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0<br/>3) array to insert | replaces a portion of the array with another (not necessarily of the same length as the replaced portion) |
|
|
||||||
| `filter` | [function pointer] to predicate (usually a [closure]) | constructs a new array with all items that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
|
||||||
| `index_of` | [function pointer] to predicate (usually a [closure]) | returns the index of the first item in the array that returns `true` when called with the predicate function, or -1 if not found:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
|
||||||
| `map` | [function pointer] to conversion function (usually a [closure]) | constructs a new array with all items mapped to the result of applying the conversion function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
|
||||||
| `reduce` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
|
||||||
| `reduce_rev` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array (in reverse order) into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
|
||||||
| `some` | [function pointer] to predicate (usually a [closure]) | returns `true` if any item returns `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
|
||||||
| `all` | [function pointer] to predicate (usually a [closure]) | returns `true` if all items return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
|
||||||
| `sort` | [function pointer] to a comparison function (usually a [closure]) | sorts the array with a comparison function:<br/>1st parameter: first item<br/>2nd parameter: second item<br/>return value: `INT` < 0 if first < second, > 0 if first > second, 0 if first == second |
|
|
||||||
|
|
||||||
|
|
||||||
Use Custom Types With Arrays
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
To use a [custom type] with arrays, a number of array functions need to be manually implemented,
|
|
||||||
in particular `push`, `insert`, `pad` and the `+=` operator. In addition, the `==` operator must be
|
|
||||||
implemented for the [custom type] in order to support the `in` operator which uses `==` to
|
|
||||||
compare elements.
|
|
||||||
|
|
||||||
See the section on [custom types] for more details.
|
|
||||||
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let y = [2, 3]; // y == [2, 3]
|
|
||||||
|
|
||||||
let y = [2, 3,]; // y == [2, 3]
|
|
||||||
|
|
||||||
y.insert(0, 1); // y == [1, 2, 3]
|
|
||||||
|
|
||||||
y.insert(999, 4); // y == [1, 2, 3, 4]
|
|
||||||
|
|
||||||
y.len == 4;
|
|
||||||
|
|
||||||
y[0] == 1;
|
|
||||||
y[1] == 2;
|
|
||||||
y[2] == 3;
|
|
||||||
y[3] == 4;
|
|
||||||
|
|
||||||
(1 in y) == true; // use 'in' to test if an item exists in the array
|
|
||||||
(42 in y) == false; // 'in' uses the '==' operator (which users can override)
|
|
||||||
// to check if the target item exists in the array
|
|
||||||
|
|
||||||
y[1] = 42; // y == [1, 42, 3, 4]
|
|
||||||
|
|
||||||
(42 in y) == true;
|
|
||||||
|
|
||||||
y.remove(2) == 3; // y == [1, 42, 4]
|
|
||||||
|
|
||||||
y.len == 3;
|
|
||||||
|
|
||||||
y[2] == 4; // elements after the removed element are shifted
|
|
||||||
|
|
||||||
ts.list = y; // arrays can be assigned completely (by value copy)
|
|
||||||
|
|
||||||
ts.list[1] == 42;
|
|
||||||
|
|
||||||
[1, 2, 3][0] == 1; // indexing on array literal
|
|
||||||
|
|
||||||
fn abc() {
|
|
||||||
[42, 43, 44] // a function returning an array
|
|
||||||
}
|
|
||||||
|
|
||||||
abc()[0] == 42;
|
|
||||||
|
|
||||||
y.push(4); // y == [1, 42, 4, 4]
|
|
||||||
|
|
||||||
y += 5; // y == [1, 42, 4, 4, 5]
|
|
||||||
|
|
||||||
y.len == 5;
|
|
||||||
|
|
||||||
y.shift() == 1; // y == [42, 4, 4, 5]
|
|
||||||
|
|
||||||
y.chop(3); // y == [4, 4, 5]
|
|
||||||
|
|
||||||
y.len == 3;
|
|
||||||
|
|
||||||
y.pop() == 5; // y == [4, 4]
|
|
||||||
|
|
||||||
y.len == 2;
|
|
||||||
|
|
||||||
for item in y { // arrays can be iterated with a 'for' statement
|
|
||||||
print(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
y.pad(6, "hello"); // y == [4, 4, "hello", "hello", "hello", "hello"]
|
|
||||||
|
|
||||||
y.len == 6;
|
|
||||||
|
|
||||||
y.truncate(4); // y == [4, 4, "hello", "hello"]
|
|
||||||
|
|
||||||
y.len == 4;
|
|
||||||
|
|
||||||
y.clear(); // y == []
|
|
||||||
|
|
||||||
y.len == 0;
|
|
||||||
|
|
||||||
let a = [42, 123, 99];
|
|
||||||
|
|
||||||
a.map(|v| v + 1); // returns [43, 124, 100]
|
|
||||||
|
|
||||||
a.map(|v, i| v + i); // returns [42, 124, 101]
|
|
||||||
|
|
||||||
a.filter(|v| v > 50); // returns [123, 99]
|
|
||||||
|
|
||||||
a.filter(|v, i| i == 1); // returns [123]
|
|
||||||
|
|
||||||
// Use a closure to provide the initial value
|
|
||||||
a.reduce(|sum, v| sum + v, || 0) == 264;
|
|
||||||
|
|
||||||
// Detect the initial value of '()'
|
|
||||||
a.reduce(
|
|
||||||
|sum, v| if sum.type_of() == "()" { v } else { sum + v }
|
|
||||||
) == 264;
|
|
||||||
|
|
||||||
// Detect the initial value via index
|
|
||||||
a.reduce(|sum, v, i|
|
|
||||||
if i == 0 { v } else { sum + v }
|
|
||||||
) == 264;
|
|
||||||
|
|
||||||
// Use a closure to provide the initial value
|
|
||||||
a.reduce_rev(|sum, v| sum + v, || 0) == 264;
|
|
||||||
|
|
||||||
// Detect the initial value of '()'
|
|
||||||
a.reduce_rev(
|
|
||||||
|sum, v| if sum.type_of() == "()" { v } else { sum + v }
|
|
||||||
) == 264;
|
|
||||||
|
|
||||||
// Detect the initial value via index
|
|
||||||
a.reduce_rev(|sum, v, i|
|
|
||||||
if i == 2 { v } else { sum + v }
|
|
||||||
) == 264;
|
|
||||||
|
|
||||||
a.some(|v| v > 50); // returns true
|
|
||||||
|
|
||||||
a.some(|v, i| v < i); // returns false
|
|
||||||
|
|
||||||
a.none(|v| v != 0); // returns false
|
|
||||||
|
|
||||||
a.none(|v, i| v == i); // returns true
|
|
||||||
|
|
||||||
a.all(|v| v > 50); // returns false
|
|
||||||
|
|
||||||
a.all(|v, i| v > i); // returns true
|
|
||||||
|
|
||||||
a.splice(1, 1, [1, 3, 2]); // a == [42, 1, 3, 2, 99]
|
|
||||||
|
|
||||||
a.extract(1, 3); // returns [1, 3, 2]
|
|
||||||
|
|
||||||
a.sort(|x, y| x - y); // a == [1, 2, 3, 42, 99]
|
|
||||||
|
|
||||||
a.drain(|v| v <= 1); // a == [2, 3, 42, 99]
|
|
||||||
|
|
||||||
a.drain(|v, i| i >= 3); // a == [2, 3, 42]
|
|
||||||
|
|
||||||
a.retain(|v| v > 10); // a == [42]
|
|
||||||
|
|
||||||
a.retain(|v, i| i > 0); // a == []
|
|
||||||
```
|
|
@ -1,66 +0,0 @@
|
|||||||
Compound Assignment Operators
|
|
||||||
=============================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
```rust
|
|
||||||
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 Flexible `+=`
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The the `+` and `+=` operators are often [overloaded][function overloading] to perform
|
|
||||||
build-up operations for different data types.
|
|
||||||
|
|
||||||
For example, it is used to build [strings]:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let my_str = "abc";
|
|
||||||
my_str += "ABC";
|
|
||||||
my_str += 12345;
|
|
||||||
|
|
||||||
my_str == "abcABC12345"
|
|
||||||
```
|
|
||||||
|
|
||||||
to concatenate [arrays]:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let my_array = [1, 2, 3];
|
|
||||||
my_array += [4, 5];
|
|
||||||
|
|
||||||
my_array == [1, 2, 3, 4, 5];
|
|
||||||
```
|
|
||||||
|
|
||||||
and mix two [object maps] together:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let my_obj = #{a:1, b:2};
|
|
||||||
my_obj += #{c:3, d:4, e:5};
|
|
||||||
|
|
||||||
my_obj.len() == 5;
|
|
||||||
```
|
|
@ -1,24 +0,0 @@
|
|||||||
Comments
|
|
||||||
========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Comments are C-style, including '`/*` ... `*/`' pairs for block comments
|
|
||||||
and '`//`' for comments to the end of the line.
|
|
||||||
|
|
||||||
Comments can be nested.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let /* intruder comment */ name = "Bob";
|
|
||||||
|
|
||||||
// This is a very important one-line comment
|
|
||||||
|
|
||||||
/* This comment spans
|
|
||||||
multiple lines, so it
|
|
||||||
only makes sense that
|
|
||||||
it is even more important */
|
|
||||||
|
|
||||||
/* Fear not, Rhai satisfies all nesting needs with nested comments:
|
|
||||||
/*/*/*/*/**/*/*/*/*/
|
|
||||||
*/
|
|
||||||
```
|
|
@ -1,97 +0,0 @@
|
|||||||
Constants
|
|
||||||
=========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Constants can be defined using the `const` keyword and are immutable.
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
```rust
|
|
||||||
const x; // 'x' is a constant '()'
|
|
||||||
|
|
||||||
const x = 40 + 2; // 'x' is a constant 42
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Manually Add Constant into Custom Scope
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
It is possible to add a constant into a custom [`Scope`] so it'll be available to scripts
|
|
||||||
running with that [`Scope`].
|
|
||||||
|
|
||||||
When added to a custom [`Scope`], a constant can hold any value, not just a literal value.
|
|
||||||
|
|
||||||
It is very useful to have a constant value hold a [custom type], which essentially acts
|
|
||||||
as a [_singleton_](../patterns/singleton.md).
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::{Engine, Scope, RegisterFn};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct TestStruct(i64); // custom type
|
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
|
|
||||||
engine
|
|
||||||
.register_type_with_name::<TestStruct>("TestStruct") // register custom type
|
|
||||||
.register_get("value", |obj: &mut TestStruct| obj.0), // property getter
|
|
||||||
.register_fn("update_value",
|
|
||||||
|obj: &mut TestStruct, value: i64| obj.0 = value // mutating method
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut scope = Scope::new(); // create custom scope
|
|
||||||
|
|
||||||
scope.push_constant("MY_NUMBER", TestStruct(123_i64)); // add constant variable
|
|
||||||
|
|
||||||
// Beware: constant objects can still be modified via a method call!
|
|
||||||
engine.consume_with_scope(&mut scope,
|
|
||||||
r"
|
|
||||||
MY_NUMBER.update_value(42);
|
|
||||||
print(MY_NUMBER.value); // prints 42
|
|
||||||
")?;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Caveat – Constants Can be Modified via Rust
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
A custom type stored as a constant cannot be modified via script, but _can_ be modified via
|
|
||||||
a registered Rust function that takes a first `&mut` parameter – because there is no way for
|
|
||||||
Rhai to know whether the Rust function modifies its argument!
|
|
||||||
|
|
||||||
```rust
|
|
||||||
const x = 42; // a constant
|
|
||||||
|
|
||||||
x.increment(); // call 'increment' defined in Rust with '&mut' first parameter
|
|
||||||
|
|
||||||
x == 43; // value of 'x' is changed!
|
|
||||||
|
|
||||||
fn double() {
|
|
||||||
this *= 2; // function doubles 'this'
|
|
||||||
}
|
|
||||||
|
|
||||||
let y = 1; // 'y' is not constant and mutable
|
|
||||||
|
|
||||||
y.double(); // double it...
|
|
||||||
|
|
||||||
y == 2; // value of 'y' is changed as expected
|
|
||||||
|
|
||||||
x.double(); // <- error: cannot modify constant 'this'
|
|
||||||
|
|
||||||
x == 43; // value of 'x' is unchanged by script
|
|
||||||
```
|
|
||||||
|
|
||||||
This is important to keep in mind because the script [optimizer][script optimization]
|
|
||||||
by default does _constant propagation_ as a operation.
|
|
||||||
|
|
||||||
If a constant is eventually modified by a Rust function, the optimizer will not see
|
|
||||||
the updated value and will propagate the original initialization value instead.
|
|
@ -1,56 +0,0 @@
|
|||||||
Value Conversions
|
|
||||||
=================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
Convert Between Integer and Floating-Point
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
The `to_float` function converts a supported number to `FLOAT` (defaults to `f64`).
|
|
||||||
|
|
||||||
The `to_int` function converts a supported number to `INT` (`i32` or `i64` depending on [`only_i32`]).
|
|
||||||
|
|
||||||
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"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Parse String into Number
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
The `parse_float` function converts a [string] into a `FLOAT` (defaults to `f64`).
|
|
||||||
|
|
||||||
The `parse_int` function converts a [string] into an `INT` (`i32` or `i64` depending on [`only_i32`]).
|
|
||||||
An optional radix (2-36) can be provided to parse the [string] into a number of the specified radix.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = parse_float("123.4"); // parse as floating-point
|
|
||||||
x == 123.4;
|
|
||||||
type_of(x) == "f64";
|
|
||||||
|
|
||||||
let dec = parse_int("42"); // parse as decimal
|
|
||||||
let dec = parse_int("42", 10); // radix = 10 is the default
|
|
||||||
dec == 42;
|
|
||||||
type_of(dec) == "i64";
|
|
||||||
|
|
||||||
let bin = parse_int("110", 2); // parse as binary (radix = 2)
|
|
||||||
bin == 0b110;
|
|
||||||
type_of(bin) == "i64";
|
|
||||||
|
|
||||||
let hex = parse_int("ab", 16); // parse as hex (radix = 16)
|
|
||||||
hex == 0xab;
|
|
||||||
type_of(hex) == "i64";
|
|
||||||
```
|
|
@ -1,28 +0,0 @@
|
|||||||
`do` Loop
|
|
||||||
=========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
`do` loops have two opposite variants: `do` ... `while` and `do` ... `until`.
|
|
||||||
|
|
||||||
Like the `while` loop, `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;
|
|
||||||
|
|
||||||
do {
|
|
||||||
x -= 1;
|
|
||||||
if x < 6 { continue; } // skip to the next iteration
|
|
||||||
print(x);
|
|
||||||
if x == 5 { break; } // break out of do loop
|
|
||||||
} while x > 0;
|
|
||||||
|
|
||||||
|
|
||||||
do {
|
|
||||||
x -= 1;
|
|
||||||
if x < 6 { continue; } // skip to the next iteration
|
|
||||||
print(x);
|
|
||||||
if x == 5 { break; } // break out of do loop
|
|
||||||
} until x == 0;
|
|
||||||
```
|
|
@ -1,77 +0,0 @@
|
|||||||
Doc-Comments
|
|
||||||
============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Similar to Rust, comments starting with `///` (three slashes) or `/**` (two asterisks) are
|
|
||||||
_doc-comments_.
|
|
||||||
|
|
||||||
Doc-comments can only appear in front of [function] definitions, not any other elements:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
/// This is a valid one-line doc-comment
|
|
||||||
fn foo() {}
|
|
||||||
|
|
||||||
/** This is a
|
|
||||||
** valid block
|
|
||||||
** doc-comment
|
|
||||||
**/
|
|
||||||
fn bar(x) {
|
|
||||||
/// Syntax error - this doc-comment is invalid
|
|
||||||
x + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Syntax error - this doc-comment is invalid */
|
|
||||||
let x = 42;
|
|
||||||
|
|
||||||
/// Syntax error - this doc-comment is also invalid
|
|
||||||
{
|
|
||||||
let x = 42;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Special Cases
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Long streams of `//////...` and `/*****...` do _NOT_ form doc-comments.
|
|
||||||
This is consistent with popular comment block styles for C-like languages.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
/////////////////////////////// <- this is not a doc-comment
|
|
||||||
// This is not a doc-comment // <- this is a normal comment
|
|
||||||
/////////////////////////////// <- this is not a doc-comment
|
|
||||||
|
|
||||||
// However, watch out for comment lines starting with '///'
|
|
||||||
|
|
||||||
////////////////////////////////////////// <- this is not a doc-comment
|
|
||||||
/// This, however, IS a doc-comment!!! /// <- this starts with '///'
|
|
||||||
////////////////////////////////////////// <- this is not a doc-comment
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
* *
|
|
||||||
* This is also not a doc-comment block *
|
|
||||||
* so we don't have to put this in *
|
|
||||||
* front of a function. *
|
|
||||||
* *
|
|
||||||
****************************************/
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Using Doc-Comments
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Doc-comments are stored within the script's [`AST`] after compilation.
|
|
||||||
|
|
||||||
The `AST::iter_functions` method provides a `ScriptFnMetadata` instance
|
|
||||||
for each function defined within the script, which includes doc-comments.
|
|
||||||
|
|
||||||
Doc-comments never affect the evaluation of a script nor do they incur
|
|
||||||
significant performance overhead. However, third party tools can take advantage
|
|
||||||
of this information to auto-generate documentation for Rhai script functions.
|
|
||||||
|
|
||||||
|
|
||||||
Disabling Doc-Comments
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Doc-comments can be disabled via the `Engine::set_doc_comments` method.
|
|
@ -1,100 +0,0 @@
|
|||||||
Dynamic Values
|
|
||||||
==============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
A `Dynamic` value can be _any_ type. However, under [`sync`], all types must be `Send + Sync`.
|
|
||||||
|
|
||||||
|
|
||||||
Use `type_of()` to Get Value Type
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Because [`type_of()`] a `Dynamic` value returns the type of the actual value,
|
|
||||||
it is usually used to perform type-specific actions based on the actual value's type.
|
|
||||||
|
|
||||||
```c
|
|
||||||
let mystery = get_some_dynamic_value();
|
|
||||||
|
|
||||||
switch type_of(mystery) {
|
|
||||||
"i64" => print("Hey, I got an integer here!"),
|
|
||||||
"f64" => print("Hey, I got a float here!"),
|
|
||||||
"string" => print("Hey, I got a string here!"),
|
|
||||||
"bool" => print("Hey, I got a boolean here!"),
|
|
||||||
"array" => print("Hey, I got an array here!"),
|
|
||||||
"map" => print("Hey, I got an object map here!"),
|
|
||||||
"Fn" => print("Hey, I got a function pointer here!"),
|
|
||||||
"TestStruct" => print("Hey, I got the TestStruct custom type here!"),
|
|
||||||
_ => print("I don't know what this is: " + type_of(mystery))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Functions Returning `Dynamic`
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
In Rust, sometimes a `Dynamic` forms part of a returned value – a good example is an [array]
|
|
||||||
which contains `Dynamic` elements, or an [object map] which contains `Dynamic` property values.
|
|
||||||
|
|
||||||
To get the _real_ values, the actual value types _must_ be known in advance.
|
|
||||||
There is no easy way for Rust to decide, at run-time, what type the `Dynamic` value is
|
|
||||||
(short of using the `type_name` function and match against the name).
|
|
||||||
|
|
||||||
|
|
||||||
Type Checking and Casting
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
A `Dynamic` value's actual type can be checked via the `is` method.
|
|
||||||
|
|
||||||
The `cast` method then converts the value into a specific, known type.
|
|
||||||
|
|
||||||
Alternatively, use the `try_cast` method which does not panic but returns `None` when the cast fails.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let list: Array = engine.eval("...")?; // return type is 'Array'
|
|
||||||
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
|
||||||
|
|
||||||
item.is::<i64>() == true; // 'is' returns whether a 'Dynamic' value is of a particular type
|
|
||||||
|
|
||||||
let value = item.cast::<i64>(); // if the element is 'i64', this succeeds; otherwise it panics
|
|
||||||
let value: i64 = item.cast(); // type can also be inferred
|
|
||||||
|
|
||||||
let value = item.try_cast::<i64>()?; // 'try_cast' does not panic when the cast fails, but returns 'None'
|
|
||||||
```
|
|
||||||
|
|
||||||
Type Name
|
|
||||||
---------
|
|
||||||
|
|
||||||
The `type_name` method gets the name of the actual type as a static string slice,
|
|
||||||
which can be `match`-ed against.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let list: Array = engine.eval("...")?; // return type is 'Array'
|
|
||||||
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
|
||||||
|
|
||||||
match item.type_name() { // 'type_name' returns the name of the actual Rust type
|
|
||||||
"i64" => ...
|
|
||||||
"alloc::string::String" => ...
|
|
||||||
"bool" => ...
|
|
||||||
"crate::path::to::module::TestStruct" => ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** `type_name` always returns the _full_ Rust path name of the type, even when the type
|
|
||||||
has been registered with a friendly name via `Engine::register_type_with_name`. This behavior
|
|
||||||
is different from that of the [`type_of`][`type_of()`] function in Rhai.
|
|
||||||
|
|
||||||
|
|
||||||
Conversion Traits
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The following conversion traits are implemented for `Dynamic`:
|
|
||||||
|
|
||||||
* `From<i64>` (`i32` if [`only_i32`])
|
|
||||||
* `From<f64>` (if not [`no_float`])
|
|
||||||
* `From<bool>`
|
|
||||||
* `From<rhai::ImmutableString>`
|
|
||||||
* `From<String>`
|
|
||||||
* `From<char>`
|
|
||||||
* `From<Vec<T>>` (into an [array])
|
|
||||||
* `From<HashMap<String, T>>` (into an [object map])
|
|
||||||
* `From<Instant>` (into a [timestamp] if not [`no_std`])
|
|
@ -1,83 +0,0 @@
|
|||||||
`eval` Function
|
|
||||||
===============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Or "How to Shoot Yourself in the Foot even Easier"
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
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
|
|
||||||
script += "y += foo(y);";
|
|
||||||
script += "x + y";
|
|
||||||
|
|
||||||
let result = eval(script); // <- look, JavaScript, we can also do this!
|
|
||||||
|
|
||||||
result == 42;
|
|
||||||
|
|
||||||
x == 10; // prints 10: functions call arguments are passed by value
|
|
||||||
y == 32; // prints 32: variables defined in 'eval' persist!
|
|
||||||
|
|
||||||
eval("{ let z = y }"); // to keep a variable local, use a statement block
|
|
||||||
|
|
||||||
print(z); // <- error: variable 'z' not found
|
|
||||||
|
|
||||||
"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_,
|
|
||||||
including all variables that are visible at that position in code! It is almost as if the script segments were
|
|
||||||
physically pasted in at the position of the `eval` call.
|
|
||||||
|
|
||||||
|
|
||||||
Cannot Define New Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
New functions cannot be defined within an `eval` call, since functions can only be defined at the _global_ level,
|
|
||||||
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
|
|
||||||
|
|
||||||
// The above is equivalent to:
|
|
||||||
let script = "x += 32";
|
|
||||||
let x = 10;
|
|
||||||
x += 32;
|
|
||||||
print(x);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
`eval` is Evil
|
|
||||||
--------------
|
|
||||||
|
|
||||||
For those who subscribe to the (very sensible) motto of ["`eval` is evil"](http://linterrors.com/js/eval-is-evil),
|
|
||||||
disable `eval` using [`Engine::disable_symbol`][disable keywords and operators]:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
engine.disable_symbol("eval"); // disable usage of 'eval'
|
|
||||||
```
|
|
||||||
|
|
||||||
`eval` can also be disabled 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"); // throws "eval is evil! I refuse to run 40 + 2"
|
|
||||||
```
|
|
||||||
|
|
||||||
Or overload it from Rust:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
engine.register_result_fn("eval", |script: String| -> Result<(), Box<EvalAltResult>> {
|
|
||||||
Err(format!("eval is evil! I refuse to run {}", script).into())
|
|
||||||
});
|
|
||||||
```
|
|
@ -1,60 +0,0 @@
|
|||||||
Anonymous Functions
|
|
||||||
===================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Sometimes it gets tedious to define separate functions only to dispatch them via single [function pointers].
|
|
||||||
This scenario is especially common when simulating object-oriented programming ([OOP]).
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Define object
|
|
||||||
let obj = #{
|
|
||||||
data: 42,
|
|
||||||
increment: Fn("inc_obj"), // use function pointers to
|
|
||||||
decrement: Fn("dec_obj"), // refer to method functions
|
|
||||||
print: Fn("print_obj")
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define method functions one-by-one
|
|
||||||
fn inc_obj(x) { this.data += x; }
|
|
||||||
fn dec_obj(x) { this.data -= x; }
|
|
||||||
fn print_obj() { print(this.data); }
|
|
||||||
```
|
|
||||||
|
|
||||||
The above can be replaced by using _anonymous functions_ which have the same syntax as Rust's closures
|
|
||||||
(but they are **NOT** real closures, merely syntactic sugar):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let obj = #{
|
|
||||||
data: 42,
|
|
||||||
increment: |x| this.data += x, // one-liner
|
|
||||||
decrement: |x| this.data -= x,
|
|
||||||
print_obj: || { print(this.data); } // full function body
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The anonymous functions will be hoisted into separate functions in the global namespace.
|
|
||||||
The above is equivalent to:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let obj = #{
|
|
||||||
data: 42,
|
|
||||||
increment: Fn("anon_fn_1000"),
|
|
||||||
decrement: Fn("anon_fn_1001"),
|
|
||||||
print: Fn("anon_fn_1002")
|
|
||||||
};
|
|
||||||
|
|
||||||
fn anon_fn_1000(x) { this.data += x; }
|
|
||||||
fn anon_fn_1001(x) { this.data -= x; }
|
|
||||||
fn anon_fn_1002() { print this.data; }
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
WARNING – NOT Real Closures
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
|
|
||||||
**not** real closures.
|
|
||||||
|
|
||||||
In particular, they capture their execution environment via [automatic currying]
|
|
||||||
(disabled via [`no_closure`]).
|
|
@ -1,71 +0,0 @@
|
|||||||
Capture The Calling Scope for Function Call
|
|
||||||
==========================================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
Peeking Out of The Pure Box
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Rhai functions are _pure_, meaning that they depend on on their arguments and have no
|
|
||||||
access to the calling environment.
|
|
||||||
|
|
||||||
When a function accesses a variable that is not defined within that function's scope,
|
|
||||||
it raises an evaluation error.
|
|
||||||
|
|
||||||
It is possible, through a special syntax, to capture the calling scope – i.e. the scope
|
|
||||||
that makes the function call – and access variables defined there.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
|
|
||||||
x += y; // 'x' is modified in this function
|
|
||||||
x
|
|
||||||
}
|
|
||||||
|
|
||||||
let x = 1;
|
|
||||||
|
|
||||||
foo(41); // error: variable 'x' not found
|
|
||||||
|
|
||||||
// Calling a function with a '!' causes it to capture the calling scope
|
|
||||||
|
|
||||||
foo!(41) == 42; // the function can access the value of 'x', but cannot change it
|
|
||||||
|
|
||||||
x == 1; // 'x' is still the original value
|
|
||||||
|
|
||||||
x.method!(); // <- syntax error: capturing is not allowed in method-call style
|
|
||||||
|
|
||||||
// Capturing also works for function pointers
|
|
||||||
|
|
||||||
let f = Fn("foo");
|
|
||||||
|
|
||||||
call!(f, 41) == 42; // must use function-call style
|
|
||||||
|
|
||||||
f.call!(41); // <- syntax error: capturing is not allowed in method-call style
|
|
||||||
|
|
||||||
// Capturing is not available for module functions
|
|
||||||
|
|
||||||
import "hello" as h;
|
|
||||||
|
|
||||||
h::greet!(); // <- syntax error: capturing is not allowed in namespace-qualified calls
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
No Mutations
|
|
||||||
------------
|
|
||||||
|
|
||||||
Variables in the calling scope are captured as cloned copies.
|
|
||||||
Changes to them do **not** reflect back to the calling scope.
|
|
||||||
|
|
||||||
Rhai functions remain _pure_ in the sense that they can never mutate their environment.
|
|
||||||
|
|
||||||
|
|
||||||
Caveat Emptor
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Functions relying on the calling scope is often a _Very Bad Idea™_ because it makes code
|
|
||||||
almost impossible to reason and maintain, as their behaviors are volatile and unpredictable.
|
|
||||||
|
|
||||||
They behave more like macros that are expanded inline than actual function calls, thus the
|
|
||||||
syntax is also similar to Rust's macro invocations.
|
|
||||||
|
|
||||||
This usage should be at the last resort. YOU HAVE BEEN WARNED.
|
|
@ -1,192 +0,0 @@
|
|||||||
Simulating Closures
|
|
||||||
===================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Capture External Variables via Automatic Currying
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
Since [anonymous functions] de-sugar to standard function definitions, they retain all the behaviors of
|
|
||||||
Rhai functions, including being _pure_, having no access to external variables.
|
|
||||||
|
|
||||||
The anonymous function syntax, however, automatically _captures_ variables that are not defined within
|
|
||||||
the current scope, but are defined in the external scope – i.e. the scope where the anonymous function
|
|
||||||
is created.
|
|
||||||
|
|
||||||
Variables that are accessible during the time the [anonymous function] is created can be captured,
|
|
||||||
as long as they are not shadowed by local variables defined within the function's scope.
|
|
||||||
|
|
||||||
The captured variables are automatically converted into **reference-counted shared values**
|
|
||||||
(`Rc<RefCell<Dynamic>>` in normal builds, `Arc<RwLock<Dynamic>>` in [`sync`] builds).
|
|
||||||
|
|
||||||
Therefore, similar to closures in many languages, these captured shared values persist through
|
|
||||||
reference counting, and may be read or modified even after the variables that hold them
|
|
||||||
go out of scope and no longer exist.
|
|
||||||
|
|
||||||
Use the `Dynamic::is_shared` function to check whether a particular value is a shared value.
|
|
||||||
|
|
||||||
Automatic currying can be turned off via the [`no_closure`] feature.
|
|
||||||
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = 1; // a normal variable
|
|
||||||
|
|
||||||
x.is_shared() == false;
|
|
||||||
|
|
||||||
let f = |y| x + y; // variable 'x' is auto-curried (captured) into 'f'
|
|
||||||
|
|
||||||
x.is_shared() == true; // 'x' is now a shared value!
|
|
||||||
|
|
||||||
f.call(2) == 3; // 1 + 2 == 3
|
|
||||||
|
|
||||||
x = 40; // changing 'x'...
|
|
||||||
|
|
||||||
f.call(2) == 42; // the value of 'x' is 40 because 'x' is shared
|
|
||||||
|
|
||||||
// The above de-sugars into this:
|
|
||||||
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
|
|
||||||
|
|
||||||
$make_shared(x); // convert variable 'x' into a shared value
|
|
||||||
|
|
||||||
let f = Fn("anon$1001").curry(x); // shared 'x' is curried
|
|
||||||
|
|
||||||
f.call(2) == 42;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Beware: Captured Variables are Truly Shared
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
The example below is a typical tutorial sample for many languages to illustrate the traps
|
|
||||||
that may accompany capturing external scope variables in closures.
|
|
||||||
|
|
||||||
It prints `9`, `9`, `9`, ... `9`, `9`, not `0`, `1`, `2`, ... `8`, `9`, because there is
|
|
||||||
ever only _one_ captured variable, and all ten closures capture the _same_ variable.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let funcs = [];
|
|
||||||
|
|
||||||
for i in range(0, 10) {
|
|
||||||
funcs.push(|| print(i)); // the for loop variable 'i' is captured
|
|
||||||
}
|
|
||||||
|
|
||||||
funcs.len() == 10; // 10 closures stored in the array
|
|
||||||
|
|
||||||
funcs[0].type_of() == "Fn"; // make sure these are closures
|
|
||||||
|
|
||||||
for f in funcs {
|
|
||||||
f.call(); // all references to 'i' are the same variable!
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Therefore – Be Careful to Prevent Data Races
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
Rust does not have data races, but that doesn't mean Rhai doesn't.
|
|
||||||
|
|
||||||
Avoid performing a method call on a captured shared variable (which essentially takes a
|
|
||||||
mutable reference to the shared object) while using that same variable as a parameter
|
|
||||||
in the method call – this is a sure-fire way to generate a data race error.
|
|
||||||
|
|
||||||
If a shared value is used as the `this` pointer in a method call to a closure function,
|
|
||||||
then the same shared value _must not_ be captured inside that function, or a data race
|
|
||||||
will occur and the script will terminate with an error.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = 20;
|
|
||||||
|
|
||||||
x.is_shared() == false; // 'x' is not shared, so no data race is possible
|
|
||||||
|
|
||||||
let f = |a| this += x + a; // 'x' is captured in this closure
|
|
||||||
|
|
||||||
x.is_shared() == true; // now 'x' is shared
|
|
||||||
|
|
||||||
x.call(f, 2); // <- error: data race detected on 'x'
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Data Races in `sync` Builds Can Become Deadlocks
|
|
||||||
-----------------------------------------------
|
|
||||||
|
|
||||||
Under the [`sync`] feature, shared values are guarded with a `RwLock`, meaning that data race
|
|
||||||
conditions no longer raise an error.
|
|
||||||
|
|
||||||
Instead, they wait endlessly for the `RwLock` to be freed, and thus can become deadlocks.
|
|
||||||
|
|
||||||
On the other hand, since the same thread (i.e. the [`Engine`] thread) that is holding the lock
|
|
||||||
is attempting to read it again, this may also [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1)
|
|
||||||
depending on the O/S.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = 20;
|
|
||||||
|
|
||||||
let f = |a| this += x + a; // 'x' is captured in this closure
|
|
||||||
|
|
||||||
// Under `sync`, the following may wait forever, or may panic,
|
|
||||||
// because 'x' is locked as the `this` pointer but also accessed
|
|
||||||
// via a captured shared value.
|
|
||||||
x.call(f, 2);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
TL;DR
|
|
||||||
-----
|
|
||||||
|
|
||||||
### Q: How is it actually implemented?
|
|
||||||
|
|
||||||
The actual implementation of closures de-sugars to:
|
|
||||||
|
|
||||||
1. Keeping track of what variables are accessed inside the anonymous function,
|
|
||||||
|
|
||||||
2. If a variable is not defined within the anonymous function's scope, it is looked up _outside_ the function and
|
|
||||||
in the current execution scope – where the anonymous function is created.
|
|
||||||
|
|
||||||
3. The variable is added to the parameters list of the anonymous function, at the front.
|
|
||||||
|
|
||||||
4. The variable is then converted into a **reference-counted shared value**.
|
|
||||||
|
|
||||||
An [anonymous function] which captures an external variable is the only way to create a reference-counted shared value in Rhai.
|
|
||||||
|
|
||||||
5. The shared value is then [curried][currying] into the [function pointer] itself, essentially carrying a reference to that shared value
|
|
||||||
and inserting it into future calls of the function.
|
|
||||||
|
|
||||||
This process is called _Automatic Currying_, and is the mechanism through which Rhai simulates normal closures.
|
|
||||||
|
|
||||||
### Q: Why are closures implemented as automatic currying?
|
|
||||||
|
|
||||||
In concept, a closure _closes_ over captured variables from the outer scope – that's why
|
|
||||||
they are called _closures_. When this happen, a typical language implementation hoists
|
|
||||||
those variables that are captured away from the stack frame and into heap-allocated storage.
|
|
||||||
This is because those variables may be needed after the stack frame goes away.
|
|
||||||
|
|
||||||
These heap-allocated captured variables only go away when all the closures that need them
|
|
||||||
are finished with them. A garbage collector makes this trivial to implement – they are
|
|
||||||
automatically collected as soon as all closures needing them are destroyed.
|
|
||||||
|
|
||||||
In Rust, this can be done by reference counting instead, with the potential pitfall of creating
|
|
||||||
reference loops that will prevent those variables from being deallocated forever.
|
|
||||||
Rhai avoids this by clone-copying most data values, so reference loops are hard to create.
|
|
||||||
|
|
||||||
Rhai does the hoisting of captured variables into the heap by converting those values
|
|
||||||
into reference-counted locked values, also allocated on the heap. The process is identical.
|
|
||||||
|
|
||||||
Closures are usually implemented as a data structure containing two items:
|
|
||||||
|
|
||||||
1) A function pointer to the function body of the closure,
|
|
||||||
2) A data structure containing references to the captured shared variables on the heap.
|
|
||||||
|
|
||||||
Usually a language implementation passes the structure containing references to captured
|
|
||||||
shared variables into the function pointer, the function body taking this data structure
|
|
||||||
as an additional parameter.
|
|
||||||
|
|
||||||
This is essentially what Rhai does, except that Rhai passes each variable individually
|
|
||||||
as separate parameters to the function, instead of creating a structure and passing that
|
|
||||||
structure as a single parameter. This is the only difference.
|
|
||||||
|
|
||||||
Therefore, in most languages, essentially all closures are implemented as automatic currying of
|
|
||||||
shared variables hoisted into the heap, automatically passing those variables as parameters into
|
|
||||||
the function. Rhai just brings this directly up to the front.
|
|
@ -1,39 +0,0 @@
|
|||||||
Function Pointer Currying
|
|
||||||
========================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
It is possible to _curry_ a [function pointer] by providing partial (or all) arguments.
|
|
||||||
|
|
||||||
Currying is done via the `curry` keyword and produces a new [function pointer] which carries
|
|
||||||
the curried arguments.
|
|
||||||
|
|
||||||
When the curried [function pointer] is called, the curried arguments are inserted starting from the left.
|
|
||||||
The actual call arguments should be reduced by the number of curried arguments.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn mul(x, y) { // function with two parameters
|
|
||||||
x * y
|
|
||||||
}
|
|
||||||
|
|
||||||
let func = Fn("mul");
|
|
||||||
|
|
||||||
func.call(21, 2) == 42; // two arguments are required for 'mul'
|
|
||||||
|
|
||||||
let curried = func.curry(21); // currying produces a new function pointer which
|
|
||||||
// carries 21 as the first argument
|
|
||||||
|
|
||||||
let curried = curry(func, 21); // function-call style also works
|
|
||||||
|
|
||||||
curried.call(2) == 42; // <- de-sugars to 'func.call(21, 2)'
|
|
||||||
// only one argument is now required
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Automatic Currying
|
|
||||||
------------------
|
|
||||||
|
|
||||||
[Anonymous functions] defined via a closure syntax _capture_ external variables
|
|
||||||
that are not shadowed inside the function's scope.
|
|
||||||
|
|
||||||
This is accomplished via [automatic currying].
|
|
@ -1,119 +0,0 @@
|
|||||||
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` and `AST::combine` methods (or the equivalent `+` and `+=` operators)
|
|
||||||
allow combining all functions in one [`AST`] into another, forming a new, unified, group of functions.
|
|
||||||
|
|
||||||
In general, there are two types of _namespaces_ where functions are looked up:
|
|
||||||
|
|
||||||
| Namespace | How Many | Source | Lookup | Sub-modules? | Variables? |
|
|
||||||
| --------- | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | :----------: | :--------: |
|
|
||||||
| Global | One | 1) [`AST`] being evaluated<br/>2) `Engine::register_XXX` API<br/>3) global [modules] registered via `Engine::register_global_module`<br/>4) functions in static [modules] registered via `Engine::register_static_module` and marked _global_ | simple name | ignored | ignored |
|
|
||||||
| Module | Many | 1) [Module] registered via `Engine::register_static_module`<br/>2) [Module] loaded via [`import`] statement | namespace-qualified name | yes | yes |
|
|
||||||
|
|
||||||
|
|
||||||
Module Namespaces
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
There can be multiple module namespaces at any time during a script evaluation, usually loaded via the
|
|
||||||
[`import`] statement.
|
|
||||||
|
|
||||||
_Static_ module namespaces can also be registered into an [`Engine`] via `Engine::register_static_module`.
|
|
||||||
|
|
||||||
Functions and variables in module namespaces are isolated and encapsulated within their own environments.
|
|
||||||
|
|
||||||
They must be called or accessed in a _namespace-qualified_ manner.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
import "my_module" as m; // new module namespace 'm' created via 'import'
|
|
||||||
|
|
||||||
let x = m::calc_result(); // namespace-qualified function call
|
|
||||||
|
|
||||||
let y = m::MY_NUMBER; // namespace-qualified variable (constant) access
|
|
||||||
|
|
||||||
let x = calc_result(); // <- error: function 'calc_result' not found
|
|
||||||
// in global namespace!
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Global Namespace
|
|
||||||
----------------
|
|
||||||
|
|
||||||
There is one _global_ namespace for every [`Engine`], which includes (in the following search order):
|
|
||||||
|
|
||||||
* All functions defined in the [`AST`] currently being evaluated.
|
|
||||||
|
|
||||||
* All native Rust functions and iterators registered via the `Engine::register_XXX` API.
|
|
||||||
|
|
||||||
* All functions and iterators defined in global [modules] that are registered into the [`Engine`] via
|
|
||||||
`Engine::register_global_module`.
|
|
||||||
|
|
||||||
* Functions defined in [modules] registered via `Engine::register_static_module` that are specifically
|
|
||||||
marked for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]).
|
|
||||||
|
|
||||||
Anywhere in a Rhai script, when a function call is made, the function is searched within the
|
|
||||||
global namespace, in the above search order.
|
|
||||||
|
|
||||||
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 get_message() {
|
|
||||||
"Hello!" // greeting message
|
|
||||||
}
|
|
||||||
|
|
||||||
fn say_hello() {
|
|
||||||
print(get_message()); // prints message
|
|
||||||
}
|
|
||||||
|
|
||||||
say_hello();
|
|
||||||
"#
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Compile another script with an overriding function
|
|
||||||
let ast2 = engine.compile(r#"fn get_message() { "Boo!" }"#)?;
|
|
||||||
|
|
||||||
// Combine the two AST's
|
|
||||||
ast1 += ast2; // 'message' will be overwritten
|
|
||||||
|
|
||||||
engine.consume_ast(&ast1)?; // 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 get_message() { "Hello!" }
|
|
||||||
|
|
||||||
|
|
||||||
---------------
|
|
||||||
| script.rhai |
|
|
||||||
---------------
|
|
||||||
|
|
||||||
import "message" as msg;
|
|
||||||
|
|
||||||
fn say_hello() {
|
|
||||||
print(msg::get_message());
|
|
||||||
}
|
|
||||||
say_hello();
|
|
||||||
```
|
|
@ -1,271 +0,0 @@
|
|||||||
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.
|
|
||||||
|
|
||||||
A function pointer is created via the `Fn` function, which takes a [string] parameter.
|
|
||||||
|
|
||||||
Call a function pointer using the `call` method.
|
|
||||||
|
|
||||||
|
|
||||||
Built-in methods
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if
|
|
||||||
using a [raw `Engine`]) operate on function pointers:
|
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
|
||||||
| ---------------------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
|
|
||||||
| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer |
|
|
||||||
| `is_anonymous` method and property | _none_ | does the function pointer refer to an [anonymous function]? Not available under [`no_function`]. |
|
|
||||||
| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ |
|
|
||||||
|
|
||||||
|
|
||||||
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); // normal function call style also works for 'call'
|
|
||||||
|
|
||||||
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 [`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_work()'
|
|
||||||
|
|
||||||
f::do_work(); // works!
|
|
||||||
|
|
||||||
let p = Fn("f::do_work"); // error: invalid function name
|
|
||||||
|
|
||||||
fn do_work_now() { // call it from a local function
|
|
||||||
f::do_work();
|
|
||||||
}
|
|
||||||
|
|
||||||
let p = Fn("do_work_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);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Bind the `this` Pointer
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
When `call` is called as a _method_ but not on a function pointer, it is possible to dynamically dispatch
|
|
||||||
to a function call while binding the object in the method call to the `this` pointer of the function.
|
|
||||||
|
|
||||||
To achieve this, pass the function pointer as the _first_ argument to `call`:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn add(x) { // define function which uses 'this'
|
|
||||||
this += x;
|
|
||||||
}
|
|
||||||
|
|
||||||
let func = Fn("add"); // function pointer to 'add'
|
|
||||||
|
|
||||||
func.call(1); // error: 'this' pointer is not bound
|
|
||||||
|
|
||||||
let x = 41;
|
|
||||||
|
|
||||||
func.call(x, 1); // error: function 'add (i64, i64)' not found
|
|
||||||
|
|
||||||
call(func, x, 1); // error: function 'add (i64, i64)' not found
|
|
||||||
|
|
||||||
x.call(func, 1); // 'this' is bound to 'x', dispatched to 'func'
|
|
||||||
|
|
||||||
x == 42;
|
|
||||||
```
|
|
||||||
|
|
||||||
Beware that this only works for _method-call_ style. Normal function-call style cannot bind
|
|
||||||
the `this` pointer (for syntactic reasons).
|
|
||||||
|
|
||||||
Therefore, obviously, binding the `this` pointer is unsupported under [`no_object`].
|
|
||||||
|
|
||||||
|
|
||||||
Call a Function Pointer in Rust
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
It is completely normal to register a Rust function with an [`Engine`] that takes parameters
|
|
||||||
whose types are function pointers. The Rust type in question is `rhai::FnPtr`.
|
|
||||||
|
|
||||||
A function pointer in Rhai is essentially syntactic sugar wrapping the _name_ of a function
|
|
||||||
to call in script. Therefore, the script's [`AST`] is required to call a function pointer,
|
|
||||||
as well as the entire _execution context_ that the script is running in.
|
|
||||||
|
|
||||||
For a rust function taking a function pointer as parameter, the [Low-Level API](../rust/register-raw.md)
|
|
||||||
must be used to register the function.
|
|
||||||
|
|
||||||
Essentially, use the low-level `Engine::register_raw_fn` method to register the function.
|
|
||||||
`FnPtr::call_dynamic` is used to actually call the function pointer, passing to it the
|
|
||||||
current _native call context_, the `this` pointer, and other necessary arguments.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::{Engine, Module, Dynamic, FnPtr, NativeCallContext};
|
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
|
|
||||||
// Define Rust function in required low-level API signature
|
|
||||||
fn call_fn_ptr_with_value(context: NativeCallContext, args: &mut [&mut Dynamic])
|
|
||||||
-> Result<Dynamic, Box<EvalAltResult>>
|
|
||||||
{
|
|
||||||
// 'args' is guaranteed to contain enough arguments of the correct types
|
|
||||||
let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer
|
|
||||||
let value = args[2].clone(); // 3rd argument - function argument
|
|
||||||
let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer
|
|
||||||
|
|
||||||
// Use 'FnPtr::call_dynamic' to call the function pointer.
|
|
||||||
// Beware, private script-defined functions will not be found.
|
|
||||||
fp.call_dynamic(context, Some(this_ptr), [value])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register a Rust function using the low-level API
|
|
||||||
engine.register_raw_fn("super_call",
|
|
||||||
&[ // parameter types
|
|
||||||
std::any::TypeId::of::<i64>(),
|
|
||||||
std::any::TypeId::of::<FnPtr>(),
|
|
||||||
std::any::TypeId::of::<i64>()
|
|
||||||
],
|
|
||||||
call_fn_ptr_with_value
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
`NativeCallContext`
|
|
||||||
------------------
|
|
||||||
|
|
||||||
`FnPtr::call_dynamic` takes a parameter of type `NativeCallContext` which holds the _native call context_
|
|
||||||
of the particular call to a registered Rust function. It is a type that exposes the following:
|
|
||||||
|
|
||||||
| Field | Type | Description |
|
|
||||||
| ------------------- | :-------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
||||||
| `engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.<br/>This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. |
|
|
||||||
| `source()` | `Option<&str>` | reference to the current source, if any |
|
|
||||||
| `iter_imports()` | `impl Iterator<Item = (&str, &Module)>` | iterator of the current stack of [modules] imported via `import` statements |
|
|
||||||
| `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements; requires the [`internals`] feature |
|
|
||||||
| `iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
|
||||||
| `namespaces()` | `&[&Module]` | reference to the namespaces (as [modules]) containing all script-defined functions; requires the [`internals`] feature |
|
|
||||||
|
|
||||||
|
|
||||||
This type is normally provided by the [`Engine`] (e.g. when using [`Engine::register_fn_raw`](../rust/register-raw.md)).
|
|
||||||
However, it may also be manually constructed from a tuple:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::{Engine, FnPtr, NativeCallContext};
|
|
||||||
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
// Compile script to AST
|
|
||||||
let mut ast = engine.compile(
|
|
||||||
r#"
|
|
||||||
let test = "hello";
|
|
||||||
|x| test + x // this creates an closure
|
|
||||||
"#,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Save the closure together with captured variables
|
|
||||||
let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?;
|
|
||||||
|
|
||||||
// Get rid of the script, retaining only functions
|
|
||||||
ast.retain_functions(|_, _, _| true);
|
|
||||||
|
|
||||||
// Create function namespace from the 'AST'
|
|
||||||
let lib = [ast.as_ref()];
|
|
||||||
|
|
||||||
// Create native call context
|
|
||||||
let context = NativeCallContext::new(&engine, &lib);
|
|
||||||
|
|
||||||
// 'f' captures: the engine, the AST, and the closure
|
|
||||||
let f = move |x: i64| fn_ptr.call_dynamic(context, None, [x.into()]);
|
|
||||||
|
|
||||||
// 'f' can be called like a normal function
|
|
||||||
let result = f(42)?;
|
|
||||||
```
|
|
@ -1,103 +0,0 @@
|
|||||||
`for` Loop
|
|
||||||
==========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Iterating through a range or an [array], or any type with a registered [type iterator],
|
|
||||||
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.
|
|
||||||
|
|
||||||
To loop through a number sequence (with or without steps), use the `range` function to
|
|
||||||
return a numeric iterator.
|
|
||||||
|
|
||||||
|
|
||||||
Iterate Through Strings
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Iterating through a [string] yields characters.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let s = "hello, world!";
|
|
||||||
|
|
||||||
for ch in s {
|
|
||||||
if ch > 'z' { continue; } // skip to the next iteration
|
|
||||||
|
|
||||||
print(ch);
|
|
||||||
|
|
||||||
if x == '@' { break; } // break out of for loop
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Iterate Through Arrays
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Iterating through an [array] yields cloned _copies_ of each element.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let array = [1, 3, 5, 7, 9, 42];
|
|
||||||
|
|
||||||
for x in array {
|
|
||||||
if x > 10 { continue; } // skip to the next iteration
|
|
||||||
|
|
||||||
print(x);
|
|
||||||
|
|
||||||
if x == 42 { break; } // break out of for loop
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Iterate Through Numeric Ranges
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
The `range` function allows iterating through a range of numbers
|
|
||||||
(not including the last number).
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Iterate starting from 0 and stopping at 49.
|
|
||||||
for x in range(0, 50) {
|
|
||||||
if x > 10 { continue; } // skip to the next iteration
|
|
||||||
|
|
||||||
print(x);
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
print(x);
|
|
||||||
|
|
||||||
if x == 42 { break; } // break out of for loop
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Iterate Through Object Maps
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Two methods, `keys` and `values`, return [arrays] containing cloned _copies_
|
|
||||||
of all property names and values of an [object map], respectively.
|
|
||||||
|
|
||||||
These [arrays] can be iterated.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let map = #{a:1, b:3, c:5, d:7, e:9};
|
|
||||||
|
|
||||||
// Property names are returned in unsorted, random order
|
|
||||||
for x in map.keys() {
|
|
||||||
if x > 10 { continue; } // skip to the next iteration
|
|
||||||
|
|
||||||
print(x);
|
|
||||||
|
|
||||||
if x == 42 { break; } // break out of for loop
|
|
||||||
}
|
|
||||||
|
|
||||||
// Property values are returned in unsorted, random order
|
|
||||||
for val in map.values() {
|
|
||||||
print(val);
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,190 +0,0 @@
|
|||||||
Functions
|
|
||||||
=========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Rhai supports defining functions in script (unless disabled with [`no_function`]):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn add(x, y) {
|
|
||||||
return x + y;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub(x, y,) { // trailing comma in parameters list is OK
|
|
||||||
return x - y;
|
|
||||||
}
|
|
||||||
|
|
||||||
add(2, 3) == 5;
|
|
||||||
|
|
||||||
sub(2, 3,) == -1; // trailing comma in arguments list is OK
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Implicit Return
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Just like in Rust, an implicit return can be used. In fact, the last statement of a block is _always_ the block's return value
|
|
||||||
regardless of whether it is terminated with a semicolon `';'`. This is different from Rust.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn add(x, y) { // implicit return:
|
|
||||||
x + y; // value of the last statement (no need for ending semicolon)
|
|
||||||
// is used as the return value
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add2(x) {
|
|
||||||
return x + 2; // explicit return
|
|
||||||
}
|
|
||||||
|
|
||||||
add(2, 3) == 5;
|
|
||||||
|
|
||||||
add2(42) == 44;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Global Definitions Only
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Functions can only be defined at the global level, never inside a block or another function.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Global level is OK
|
|
||||||
fn add(x, y) {
|
|
||||||
x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following will not compile
|
|
||||||
fn do_addition(x) {
|
|
||||||
fn add_y(n) { // <- syntax error: functions cannot be defined inside another function
|
|
||||||
n + y
|
|
||||||
}
|
|
||||||
|
|
||||||
add_y(x)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
No Access to External Scope
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Functions are not _closures_. They do not capture the calling environment
|
|
||||||
and can only access their own parameters.
|
|
||||||
They cannot access variables external to the function itself.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = 42;
|
|
||||||
|
|
||||||
fn foo() { x } // <- syntax error: variable 'x' doesn't exist
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
But Can Call Other Functions
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
All functions in the same [`AST`] can call each other.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn foo(x) { x + 1 } // function defined in the global namespace
|
|
||||||
|
|
||||||
fn bar(x) { foo(x) } // OK! function 'foo' can be called
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Use Before Definition Allowed
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
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 are Passed by Value
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Functions defined in script always take [`Dynamic`] parameters (i.e. they can be of any types).
|
|
||||||
Therefore, functions with the same name and same _number_ of parameters are equivalent.
|
|
||||||
|
|
||||||
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
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
Script-defined functions can also be called in method-call style.
|
|
||||||
When this happens, 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 unbound
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
`is_def_fn`
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Use `is_def_fn` to detect if a Rhai function is defined (and therefore callable),
|
|
||||||
based on its name and the number of parameters.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn foo(x) { x + 1 }
|
|
||||||
|
|
||||||
is_def_fn("foo", 1) == true;
|
|
||||||
|
|
||||||
is_def_fn("foo", 0) == false;
|
|
||||||
|
|
||||||
is_def_fn("foo", 2) == false;
|
|
||||||
|
|
||||||
is_def_fn("bar", 1) == false;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Metadata
|
|
||||||
--------
|
|
||||||
|
|
||||||
The function `get_fn_metadata_list` is a _reflection_ API that returns an array of the metadata
|
|
||||||
of all script-defined functions in scope.
|
|
||||||
|
|
||||||
Functions from the following sources are returned, in order:
|
|
||||||
|
|
||||||
1) Encapsulated script environment (e.g. when loading a [module] from a script file),
|
|
||||||
2) Current script,
|
|
||||||
3) [Modules] imported via the [`import`] statement (latest imports first),
|
|
||||||
4) [Modules] added via [`Engine::register_static_module`]({{rootUrl}}/rust/modules/create.md) (latest registrations first)
|
|
||||||
|
|
||||||
The return value is an [array] of [object maps] (so `get_fn_metadata_list` is not available under
|
|
||||||
[`no_index`] or [`no_object`]), containing the following fields:
|
|
||||||
|
|
||||||
| Field | Type | Optional? | Description |
|
|
||||||
| -------------- | :------------------: | :-------: | ---------------------------------------------------------------------- |
|
|
||||||
| `namespace` | [string] | yes | the module _namespace_ if the function is defined within a module |
|
|
||||||
| `access` | [string] | no | `"public"` if the function is public,<br/>`"private"` if it is private |
|
|
||||||
| `name` | [string] | no | function name |
|
|
||||||
| `params` | [array] of [strings] | no | parameter names |
|
|
||||||
| `is_anonymous` | `bool` | no | is this function an anonymous function? |
|
|
@ -1,51 +0,0 @@
|
|||||||
`if` Statement
|
|
||||||
==============
|
|
||||||
|
|
||||||
{{#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 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!");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Braces Are Mandatory
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
`if`-Expressions
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Like Rust, `if` statements can also be used as _expressions_, replacing the `? :` conditional operators
|
|
||||||
in other C-like languages.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// The following is equivalent to C: int x = 1 + (decision ? 42 : 123) / 2;
|
|
||||||
let x = 1 + if decision { 42 } else { 123 } / 2;
|
|
||||||
x == 22;
|
|
||||||
|
|
||||||
let x = if decision { 42 }; // no else branch defaults to '()'
|
|
||||||
x == ();
|
|
||||||
```
|
|
@ -1,7 +0,0 @@
|
|||||||
Rhai Language Reference
|
|
||||||
======================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
This section outlines the Rhai language.
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
|||||||
Iterators for Custom Types
|
|
||||||
==========================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
If a [custom type] is iterable, the [`for`](for.md) loop can be used to iterate through
|
|
||||||
its items in sequence.
|
|
||||||
|
|
||||||
In order to use a [`for`](for.md) statement, a _type iterator_ must be registered for
|
|
||||||
the [custom type] in question.
|
|
||||||
|
|
||||||
`Engine::register_iterator<T>` allows registration of a _type iterator_ for any type
|
|
||||||
that implements `IntoIterator`:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Custom type
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct TestStruct { ... }
|
|
||||||
|
|
||||||
// Implement 'IntoIterator' trait
|
|
||||||
impl IntoIterator<Item = ...> for TestStruct {
|
|
||||||
type Item = ...;
|
|
||||||
type IntoIter = SomeIterType<Self::Item>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
engine
|
|
||||||
.register_type_with_name::<TestStruct>("TestStruct")
|
|
||||||
.register_fn("new_ts", || TestStruct { ... })
|
|
||||||
.register_iterator::<TestStruct>(); // register type iterator
|
|
||||||
```
|
|
||||||
|
|
||||||
With a type iterator registered, the [custom type] can be iterated through:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let ts = new_ts();
|
|
||||||
|
|
||||||
// Use 'for' statement to loop through items in 'ts'
|
|
||||||
for item in ts {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,95 +0,0 @@
|
|||||||
Parse an Object Map from JSON
|
|
||||||
============================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
The syntax for an [object map] is extremely similar to the JSON representation of a object hash,
|
|
||||||
with the exception of `null` values which can technically be mapped to [`()`].
|
|
||||||
|
|
||||||
A valid JSON string does not start with a hash character `#` while a Rhai [object map] does – that's the major difference!
|
|
||||||
|
|
||||||
Use the `Engine::parse_json` method to parse a piece of JSON into an object map.
|
|
||||||
The JSON text must represent a single object hash (i.e. must be wrapped within "`{ .. }`")
|
|
||||||
otherwise it returns a syntax error.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// JSON string - notice that JSON property names are always quoted
|
|
||||||
// notice also that comments are acceptable within the JSON string
|
|
||||||
let json = r#"{
|
|
||||||
"a": 1, // <- this is an integer number
|
|
||||||
"b": true,
|
|
||||||
"c": 123.0, // <- this is a floating-point number
|
|
||||||
"$d e f!": "hello", // <- any text can be a property name
|
|
||||||
"^^^!!!": [1,42,"999"], // <- value can be array or another hash
|
|
||||||
"z": null // <- JSON 'null' value
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
// Parse the JSON expression as an object map
|
|
||||||
// Set the second boolean parameter to true in order to map 'null' to '()'
|
|
||||||
let map = engine.parse_json(json, true)?;
|
|
||||||
|
|
||||||
map.len() == 6; // 'map' contains all properties in the JSON string
|
|
||||||
|
|
||||||
// Put the object map into a 'Scope'
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
scope.push("map", map);
|
|
||||||
|
|
||||||
let result = engine.eval_with_scope::<INT>(r#"map["^^^!!!"].len()"#)?;
|
|
||||||
|
|
||||||
result == 3; // the object map is successfully used in the script
|
|
||||||
```
|
|
||||||
|
|
||||||
Representation of Numbers
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
JSON numbers are all floating-point while Rhai supports integers (`INT`) and floating-point (`FLOAT`) if
|
|
||||||
the [`no_float`] feature is not used.
|
|
||||||
|
|
||||||
Most common generators of JSON data distinguish between integer and floating-point values by always
|
|
||||||
serializing a floating-point number with a decimal point (i.e. `123.0` instead of `123` which is
|
|
||||||
assumed to be an integer).
|
|
||||||
|
|
||||||
This style can be used successfully with Rhai [object maps].
|
|
||||||
|
|
||||||
|
|
||||||
Parse JSON with Sub-Objects
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
`Engine::parse_json` depends on the fact that the [object map] literal syntax in Rhai is _almost_
|
|
||||||
the same as a JSON object. However, it is _almost_ because the syntax for a sub-object in JSON
|
|
||||||
(i.e. "`{ ... }`") is different from a Rhai [object map] literal (i.e. "`#{ ... }`").
|
|
||||||
|
|
||||||
When `Engine::parse_json` encounters JSON with sub-objects, it fails with a syntax error.
|
|
||||||
|
|
||||||
If it is certain that no text string in the JSON will ever contain the character '`{`',
|
|
||||||
then it is possible to parse it by first replacing all occupance of '`{`' with "`#{`".
|
|
||||||
|
|
||||||
A JSON object hash starting with `#{` is handled transparently by `Engine::parse_json`.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// JSON with sub-object 'b'.
|
|
||||||
let json = r#"{"a":1, "b":{"x":true, "y":false}}"#;
|
|
||||||
|
|
||||||
// Our JSON text does not contain the '{' character, so off we go!
|
|
||||||
let new_json = json.replace("{", "#{");
|
|
||||||
|
|
||||||
// The leading '{' will also be replaced to '#{', but 'parse_json' handles this just fine.
|
|
||||||
let map = engine.parse_json(&new_json, false)?;
|
|
||||||
|
|
||||||
map.len() == 2; // 'map' contains two properties: 'a' and 'b'
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Use `serde` to Serialize/Deserialize to/from JSON
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
Remember, `Engine::parse_json` is nothing more than a _cheap_ alternative to true JSON parsing.
|
|
||||||
|
|
||||||
If correctness is needed, or for more configuration possibilities, turn on the [`serde`][features]
|
|
||||||
feature to pull in the [`serde`](https://crates.io/crates/serde) crate which enables
|
|
||||||
serialization and deserialization to/from multiple formats, including JSON.
|
|
||||||
|
|
||||||
Beware, though... the [`serde`](https://crates.io/crates/serde) crate is quite heavy.
|
|
||||||
|
|
||||||
See _[Serialization/Deserialization of `Dynamic` with `serde`][`serde`]_ for more details.
|
|
@ -1,26 +0,0 @@
|
|||||||
Keywords
|
|
||||||
========
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
The following are reserved keywords in Rhai:
|
|
||||||
|
|
||||||
| Active keywords | Reserved keywords | Usage | Inactive under feature |
|
|
||||||
| ---------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------- | :--------------------: |
|
|
||||||
| `true`, `false` | | constants | |
|
|
||||||
| `let`, `const` | `var`, `static` | variables | |
|
|
||||||
| | `begin`, `end` | block scopes | |
|
|
||||||
| `is_shared` | | shared values | [`no_closure`] |
|
|
||||||
| `if`, `else` | `then`, `unless`, `goto`, `exit` | control flow | |
|
|
||||||
| `switch` | `match`, `case` | switching and matching | |
|
|
||||||
| `do`, `while`, `loop`, `until`, `for`, `in`, `continue`, `break` | `each` | looping | |
|
|
||||||
| `fn`, `private` | `public`, `new` | functions | [`no_function`] |
|
|
||||||
| `return` | | return values | |
|
|
||||||
| `throw`, `try`, `catch` | | throw/catch exceptions | |
|
|
||||||
| `import`, `export`, `as` | `use`, `with`, `module`, `package` | modules/packages | [`no_module`] |
|
|
||||||
| `Fn`, `call`, `curry` | | function pointers | |
|
|
||||||
| | `spawn`, `thread`, `go`, `sync`, `async`, `await`, `yield` | threading/async | |
|
|
||||||
| `type_of`, `print`, `debug`, `eval` | | special functions | |
|
|
||||||
| | `default`, `void`, `null`, `nil` | special values | |
|
|
||||||
|
|
||||||
Keywords cannot become the name of a [function] or [variable], even when they are disabled.
|
|
@ -1,78 +0,0 @@
|
|||||||
Logic Operators
|
|
||||||
==============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Comparison Operators
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
| Operator | Description |
|
|
||||||
| :------: | ------------------------- |
|
|
||||||
| `==` | equals to |
|
|
||||||
| `!=` | not equals to |
|
|
||||||
| `>` | greater than |
|
|
||||||
| `>=` | greater than or equals to |
|
|
||||||
| `<` | less than |
|
|
||||||
| `<=` | less than or equals to |
|
|
||||||
|
|
||||||
Comparing most values of the same data type work out-of-the-box for all [standard types] supported by the system.
|
|
||||||
|
|
||||||
However, if using a [raw `Engine`] without loading any [packages], comparisons can only be made between a limited
|
|
||||||
set of types (see [built-in operators]).
|
|
||||||
|
|
||||||
```rust
|
|
||||||
42 == 42; // true
|
|
||||||
|
|
||||||
42 > 42; // false
|
|
||||||
|
|
||||||
"hello" > "foo"; // true
|
|
||||||
|
|
||||||
"42" == 42; // false
|
|
||||||
```
|
|
||||||
|
|
||||||
Comparing two values of _different_ data types, or of unknown data types, always results in `false`,
|
|
||||||
except for '`!=`' (not equals) which results in `true`. This is in line with intuition.
|
|
||||||
|
|
||||||
```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
|
|
||||||
```
|
|
||||||
|
|
||||||
Boolean operators
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
| Operator | Description | Short-Circuits? |
|
|
||||||
| :---------------: | ------------- | :-------------: |
|
|
||||||
| `!` (prefix) | boolean _NOT_ | no |
|
|
||||||
| `&&` | boolean _AND_ | yes |
|
|
||||||
| `&` | boolean _AND_ | no |
|
|
||||||
| <code>\|\|</code> | boolean _OR_ | yes |
|
|
||||||
| <code>\|</code> | boolean _OR_ | no |
|
|
||||||
|
|
||||||
Double boolean operators `&&` and `||` _short-circuit_ – meaning that the second operand will not be evaluated
|
|
||||||
if the first one already proves the condition wrong.
|
|
||||||
|
|
||||||
Single boolean operators `&` and `|` always evaluate both operands.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
a() || b(); // b() is not evaluated if a() is true
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
All boolean operators are [built in][built-in operators] for the `bool` data type.
|
|
@ -1,26 +0,0 @@
|
|||||||
Infinite `loop`
|
|
||||||
===============
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Infinite loops follow Rust syntax.
|
|
||||||
|
|
||||||
Like Rust, `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 -= 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.
|
|
@ -1,95 +0,0 @@
|
|||||||
Call Method as Function
|
|
||||||
======================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
First `&mut` Parameter
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
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
|
|
||||||
let a = new_ts(); // constructor function
|
|
||||||
a.field = 500; // property setter
|
|
||||||
a.update(); // method call, 'a' can be modified
|
|
||||||
|
|
||||||
update(a); // <- this de-sugars to 'a.update()' thus if 'a' is a simple variable
|
|
||||||
// unlike scripted functions, 'a' can be modified and is not a copy
|
|
||||||
|
|
||||||
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
|
|
||||||
// except waste a lot of time cloning
|
|
||||||
|
|
||||||
array[0].update(); // <- call in method-call style will update 'a'
|
|
||||||
```
|
|
||||||
|
|
||||||
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
|
|
||||||
|
|
||||||
|
|
||||||
Number of Parameters in Methods
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Native Rust methods registered with an [`Engine`] take _one additional parameter_ more than
|
|
||||||
an equivalent method coded in script, where the object is accessed via the `this` pointer instead.
|
|
||||||
|
|
||||||
The following table illustrates the differences:
|
|
||||||
|
|
||||||
| Function type | Parameters | Object reference | Function signature |
|
|
||||||
| :-----------: | :--------: | :-----------------------: | :---------------------------: |
|
|
||||||
| Native Rust | _N_ + 1 | first `&mut T` parameter | `Fn(obj: &mut T, x: U, y: V)` |
|
|
||||||
| Rhai script | _N_ | `this` (of type `&mut T`) | `Fn(x: U, y: V)` |
|
|
||||||
|
|
||||||
|
|
||||||
`&mut` is Efficient, Except for `&mut ImmutableString`
|
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone,
|
|
||||||
even when the intention is not to mutate that argument, because it avoids cloning that argument value.
|
|
||||||
|
|
||||||
Even when a function is never intended to be a method – for example an operator,
|
|
||||||
it is still sometimes beneficial to make it method-like (i.e. with a first `&mut` parameter)
|
|
||||||
if the first parameter is not modified.
|
|
||||||
|
|
||||||
For types that are expensive to clone (remember, all function calls are passed cloned
|
|
||||||
copies of argument values), this may result in a significant performance boost.
|
|
||||||
|
|
||||||
For primary types that are cheap to clone (e.g. those that implement `Copy`), including `ImmutableString`,
|
|
||||||
this is not necessary.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// This is a type that is very expensive to clone.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct VeryComplexType { ... }
|
|
||||||
|
|
||||||
// Calculate some value by adding 'VeryComplexType' with an integer number.
|
|
||||||
fn do_add(obj: &VeryComplexType, offset: i64) -> i64 {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
engine
|
|
||||||
.register_type::<VeryComplexType>()
|
|
||||||
.register_fn("+", add_pure /* or add_method*/);
|
|
||||||
|
|
||||||
// Very expensive to call, as the 'VeryComplexType' is cloned before each call.
|
|
||||||
fn add_pure(obj: VeryComplexType, offset: i64) -> i64 {
|
|
||||||
do_add(obj, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Efficient to call, as only a reference to the 'VeryComplexType' is passed.
|
|
||||||
fn add_method(obj: &mut VeryComplexType, offset: i64) -> i64 {
|
|
||||||
do_add(obj, offset)
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,98 +0,0 @@
|
|||||||
Export Variables, Functions and Sub-Modules in Module
|
|
||||||
===================================================
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
The easiest way to expose a collection of functions as a self-contained [module] is to do it via a Rhai script itself.
|
|
||||||
|
|
||||||
See the section on [_Creating a Module from AST_]({{rootUrl}}/rust/modules/ast.md) for more details.
|
|
||||||
|
|
||||||
The script text is evaluated, variables are then selectively exposed via the [`export`] statement.
|
|
||||||
Functions defined by the script are automatically exported.
|
|
||||||
|
|
||||||
Modules loaded within this module at the global level become _sub-modules_ and are also automatically exported.
|
|
||||||
|
|
||||||
|
|
||||||
Export Global Variables
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
The `export` statement, which can only be at global level, exposes selected variables as members of a module.
|
|
||||||
|
|
||||||
Variables not exported are _private_ and hidden. They are merely used to initialize the module,
|
|
||||||
but cannot be accessed from outside.
|
|
||||||
|
|
||||||
Everything exported from a module is **constant** (i.e. read-only).
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// This is a module script.
|
|
||||||
|
|
||||||
let hidden = 123; // variable not exported - default hidden
|
|
||||||
let x = 42; // this will be exported below
|
|
||||||
|
|
||||||
export x; // the variable 'x' is exported under its own name
|
|
||||||
|
|
||||||
export let x = 42; // convenient short-hand to declare a variable and export it
|
|
||||||
// under its own name
|
|
||||||
|
|
||||||
export x as answer; // the variable 'x' is exported under the alias 'answer'
|
|
||||||
// another script can load this module and access 'x' as 'module::answer'
|
|
||||||
|
|
||||||
{
|
|
||||||
let inner = 0; // local variable - it disappears when the statement block ends,
|
|
||||||
// therefore it is not 'global' and cannot be exported
|
|
||||||
|
|
||||||
export inner; // <- syntax error: cannot export a local variable
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple Exports
|
|
||||||
|
|
||||||
One `export` statement can export multiple variables, even under multiple names.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// The following exports three variables:
|
|
||||||
// - 'x' (as 'x' and 'hello')
|
|
||||||
// - 'y' (as 'foo' and 'bar')
|
|
||||||
// - 'z' (as 'z')
|
|
||||||
export x, x as hello, x as world, y as foo, y as bar, z;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Export Functions
|
|
||||||
----------------
|
|
||||||
|
|
||||||
All functions are automatically exported, _unless_ it is explicitly opt-out with the [`private`] prefix.
|
|
||||||
|
|
||||||
Functions declared [`private`] are hidden to the outside.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// This is a module script.
|
|
||||||
|
|
||||||
fn inc(x) { x + 1 } // script-defined function - default public
|
|
||||||
|
|
||||||
private fn foo() {} // private function - hidden
|
|
||||||
```
|
|
||||||
|
|
||||||
[`private`] functions are commonly called to initialize the module.
|
|
||||||
They cannot be called apart from this.
|
|
||||||
|
|
||||||
|
|
||||||
Sub-Modules
|
|
||||||
-----------
|
|
||||||
|
|
||||||
All loaded modules are automatically exported as sub-modules.
|
|
||||||
|
|
||||||
To prevent a module from being exported, load it inside a block statement so that it goes away at the
|
|
||||||
end of the block.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// This is a module script.
|
|
||||||
|
|
||||||
import "hello" as foo; // exported as sub-module 'foo'
|
|
||||||
|
|
||||||
{
|
|
||||||
import "world" as bar; // not exported - the module disappears at the end
|
|
||||||
// of the statement block and is not 'global'
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,113 +0,0 @@
|
|||||||
Import a Module
|
|
||||||
===============
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
|
|
||||||
Before a module can be used (via an `import` statement) in a script, there must be a [module resolver]
|
|
||||||
registered into the [`Engine`], the default being the `FileModuleResolver`.
|
|
||||||
|
|
||||||
See the section on [_Module Resolvers_][module resolver] for more details.
|
|
||||||
|
|
||||||
|
|
||||||
`import` Statement
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
A module can be _imported_ via the `import` statement, and be given a name.
|
|
||||||
Its members can be accessed via '`::`' similar to C++.
|
|
||||||
|
|
||||||
A module that is only `import`-ed but not under any module name is commonly used for initialization purposes,
|
|
||||||
where the module script contains initialization statements that puts the functions registered with the
|
|
||||||
[`Engine`] into a particular state.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
import "crypto_init"; // run the script file 'crypto_init.rhai' without creating an imported module
|
|
||||||
|
|
||||||
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
|
|
||||||
|
|
||||||
const SECRET_NUMBER = 42;
|
|
||||||
|
|
||||||
let mod_file = "crypto_" + SECRET_NUMBER;
|
|
||||||
|
|
||||||
import mod_file as my_mod; // load the script file "crypto_42.rhai" and import it as a module named 'my_mod'
|
|
||||||
// notice that module path names can be dynamically constructed!
|
|
||||||
// any expression that evaluates to a string is acceptable after the 'import' keyword
|
|
||||||
|
|
||||||
lock::encrypt(secret); // use functions defined under the module via '::'
|
|
||||||
|
|
||||||
lock::hash::sha256(key); // sub-modules are also supported
|
|
||||||
|
|
||||||
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 not advised to deviate from this common practice unless
|
|
||||||
there is a _Very Good Reason™_.
|
|
||||||
|
|
||||||
Especially, do not place an `import` statement within a loop; doing so will repeatedly re-load the same module
|
|
||||||
during every iteration of the loop!
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let mod = "crypto";
|
|
||||||
|
|
||||||
if secured { // new block scope
|
|
||||||
import mod as c; // import module (the path needs not be a constant string)
|
|
||||||
|
|
||||||
c::encrypt(key); // use a function in the module
|
|
||||||
} // the module disappears at the end of the block scope
|
|
||||||
|
|
||||||
c::encrypt(others); // <- this causes a run-time error because the 'crypto' module
|
|
||||||
// is no longer available!
|
|
||||||
|
|
||||||
for x in range(0, 1000) {
|
|
||||||
import "crypto" as c; // <- importing a module inside a loop is a Very Bad Idea™
|
|
||||||
|
|
||||||
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
|
|
||||||
--------------
|
|
||||||
| hello.rhai |
|
|
||||||
--------------
|
|
||||||
|
|
||||||
import "hello" as foo; // import itself - infinite recursion!
|
|
||||||
|
|
||||||
foo::do_something();
|
|
||||||
```
|
|
||||||
|
|
||||||
Modules cross-referencing also cause infinite recursion:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
--------------
|
|
||||||
| hello.rhai |
|
|
||||||
--------------
|
|
||||||
|
|
||||||
import "world" as foo;
|
|
||||||
foo::do_something();
|
|
||||||
|
|
||||||
|
|
||||||
--------------
|
|
||||||
| world.rhai |
|
|
||||||
--------------
|
|
||||||
|
|
||||||
import "hello" as bar;
|
|
||||||
bar::do_something_else();
|
|
||||||
```
|
|
@ -1,14 +0,0 @@
|
|||||||
Modules
|
|
||||||
=======
|
|
||||||
|
|
||||||
{{#include ../../links.md}}
|
|
||||||
|
|
||||||
Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_.
|
|
||||||
Modules can be disabled via the [`no_module`] feature.
|
|
||||||
|
|
||||||
A module is of the type `Module` and holds a collection of functions, variables, [type iterators] and sub-modules.
|
|
||||||
It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions
|
|
||||||
and variables defined by that script.
|
|
||||||
|
|
||||||
Other scripts can then load this module and use the functions and variables exported
|
|
||||||
as if they were defined inside the same script.
|
|
@ -1,46 +0,0 @@
|
|||||||
Numeric Functions
|
|
||||||
================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Integer Functions
|
|
||||||
----------------
|
|
||||||
|
|
||||||
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 | No available under | Description |
|
|
||||||
| -------- | :----------------: | ----------------------------------------------------------------------- |
|
|
||||||
| `abs` | | absolute value |
|
|
||||||
| `sign` | | returns -1 (`INT`) if the number is negative, +1 if positive, 0 if zero |
|
|
||||||
|
|
||||||
|
|
||||||
Floating-Point Functions
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`])
|
|
||||||
operate on `f64` only:
|
|
||||||
|
|
||||||
| Category | Functions |
|
|
||||||
| ---------------- | --------------------------------------------------------------------- |
|
|
||||||
| Trigonometry | `sin`, `cos`, `tan`, `sinh`, `cosh`, `tanh` in degrees |
|
|
||||||
| Arc-trigonometry | `asin`, `acos`, `atan`, `asinh`, `acosh`, `atanh` in degrees |
|
|
||||||
| Square root | `sqrt` |
|
|
||||||
| Exponential | `exp` (base _e_) |
|
|
||||||
| Logarithmic | `ln` (base _e_), `log10` (base 10), `log` (any base) |
|
|
||||||
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` methods and properties |
|
|
||||||
| Conversion | [`to_int`] |
|
|
||||||
| Testing | `is_nan`, `is_finite`, `is_infinite` methods and properties |
|
|
||||||
|
|
||||||
|
|
||||||
Conversion Functions
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`])
|
|
||||||
parse numbers:
|
|
||||||
|
|
||||||
| Function | No available under | Description |
|
|
||||||
| --------------- | :----------------: | --------------------------------------------------- |
|
|
||||||
| [`to_float`] | [`no_float`] | converts an integer type to `FLOAT` |
|
|
||||||
| [`parse_int`] | | converts a [string] to `INT` with an optional radix |
|
|
||||||
| [`parse_float`] | [`no_float`] | converts a [string] to `FLOAT` |
|
|
@ -1,51 +0,0 @@
|
|||||||
Numeric Operators
|
|
||||||
=================
|
|
||||||
|
|
||||||
{{#include ../links.md}}
|
|
||||||
|
|
||||||
Numeric operators generally follow C styles.
|
|
||||||
|
|
||||||
Unary Operators
|
|
||||||
---------------
|
|
||||||
|
|
||||||
| Operator | Description |
|
|
||||||
| -------- | ----------- |
|
|
||||||
| `+` | positive |
|
|
||||||
| `-` | negative |
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let number = -5;
|
|
||||||
|
|
||||||
number = -5 - +5;
|
|
||||||
```
|
|
||||||
|
|
||||||
Binary Operators
|
|
||||||
----------------
|
|
||||||
|
|
||||||
| Operator | Description | Integers only |
|
|
||||||
| --------------- | ---------------------------------------------------- | :-----------: |
|
|
||||||
| `+` | plus | |
|
|
||||||
| `-` | minus | |
|
|
||||||
| `*` | multiply | |
|
|
||||||
| `/` | divide (integer division if acting on integer types) | |
|
|
||||||
| `%` | modulo (remainder) | |
|
|
||||||
| `~` | power | |
|
|
||||||
| `&` | bit-wise _And_ | Yes |
|
|
||||||
| <code>\|</code> | 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
|
|
||||||
|
|
||||||
let reminder = 42 % 10; // modulo
|
|
||||||
|
|
||||||
let power = 42 ~ 2; // power (i64 and f64 only)
|
|
||||||
|
|
||||||
let left_shifted = 42 << 3; // left shift
|
|
||||||
|
|
||||||
let right_shifted = 42 >> 3; // right shift
|
|
||||||
|
|
||||||
let bit_op = 42 | 99; // bit masking
|
|
||||||
```
|
|