1
.gitignore
vendored
@ -2,6 +2,5 @@ target/
|
||||
Cargo.lock
|
||||
.vscode/
|
||||
.cargo/
|
||||
doc/book/
|
||||
before*
|
||||
after*
|
||||
|
@ -10,8 +10,8 @@ version = "0.19.10"
|
||||
edition = "2018"
|
||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||
description = "Embedded scripting for Rust"
|
||||
homepage = "https://schungx.github.io/rhai"
|
||||
repository = "https://github.com/jonathandturner/rhai"
|
||||
homepage = "https://rhaiscript.github.io/book"
|
||||
repository = "https://github.com/rhaiscript/rhai"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
include = [
|
||||
|
50
README.md
@ -1,16 +1,16 @@
|
||||
Rhai - Embedded Scripting for Rust
|
||||
=================================
|
||||
|
||||
![GitHub last commit](https://img.shields.io/github/last-commit/jonathandturner/rhai?logo=github)
|
||||
[![Build Status](https://github.com/jonathandturner/rhai/workflows/Build/badge.svg)](https://github.com/jonathandturner/rhai/actions)
|
||||
[![license](https://img.shields.io/crates/l/rhai)](https://github.com/license/jonathandturner/rhai)
|
||||
![GitHub last commit](https://img.shields.io/github/last-commit/rhaiscript/rhai?logo=github)
|
||||
[![Build Status](https://github.com/rhaiscript/rhai/workflows/Build/badge.svg)](https://github.com/rhaiscript/rhai/actions)
|
||||
[![license](https://img.shields.io/crates/l/rhai)](https://github.com/license/rhaiscript/rhai)
|
||||
[![crates.io](https://img.shields.io/crates/v/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
||||
[![crates.io](https://img.shields.io/crates/d/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
||||
[![API Docs](https://docs.rs/rhai/badge.svg?logo=docs.rs)](https://docs.rs/rhai/)
|
||||
[![chat](https://img.shields.io/discord/767611025456889857.svg?logo=discord)](https://discord.gg/HquqbYFcZ9)
|
||||
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/Rhai?logo=reddit)](https://www.reddit.com/r/Rhai)
|
||||
|
||||
![Rhai logo](https://schungx.github.io/rhai/images/logo/rhai-banner-transparent-colour.svg)
|
||||
![Rhai logo](https://rhaiscript.github.io/book/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.
|
||||
@ -31,44 +31,44 @@ Standard features
|
||||
* Easy-to-use language similar to JavaScript+Rust with dynamic typing.
|
||||
* Fairly low compile-time overhead.
|
||||
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single core, 2.3 GHz Linux VM).
|
||||
* Tight integration with native Rust [functions](https://schungx.github.io/rhai/rust/functions.html) and [types]([#custom-types-and-methods](https://schungx.github.io/rhai/rust/custom.html)), including [getters/setters](https://schungx.github.io/rhai/rust/getters-setters.html), [methods](https://schungx.github.io/rhai/rust/custom.html) and [indexers](https://schungx.github.io/rhai/rust/indexers.html).
|
||||
* Freely pass Rust variables/constants into a script via an external [`Scope`](https://schungx.github.io/rhai/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait.
|
||||
* Easily [call a script-defined function](https://schungx.github.io/rhai/engine/call-fn.html) from Rust.
|
||||
* Tight integration with native Rust [functions](https://rhaiscript.github.io/book/rust/functions.html) and [types]([#custom-types-and-methods](https://rhaiscript.github.io/book/rust/custom.html)), including [getters/setters](https://rhaiscript.github.io/book/rust/getters-setters.html), [methods](https://rhaiscript.github.io/book/rust/custom.html) and [indexers](https://rhaiscript.github.io/book/rust/indexers.html).
|
||||
* Freely pass Rust variables/constants into a script via an external [`Scope`](https://rhaiscript.github.io/book/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait.
|
||||
* Easily [call a script-defined function](https://rhaiscript.github.io/book/engine/call-fn.html) from Rust.
|
||||
* Relatively little `unsafe` code (yes there are some for performance reasons).
|
||||
* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec)).
|
||||
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
|
||||
* Scripts are [optimized](https://schungx.github.io/rhai/engine/optimize.html) (useful for template-based machine-generated scripts) for repeated evaluations.
|
||||
* Easy custom API development via [plugins](https://schungx.github.io/rhai/plugins/index.html) system powered by procedural macros.
|
||||
* [Function overloading](https://schungx.github.io/rhai/language/overload.html) and [operator overloading](https://schungx.github.io/rhai/rust/operators.html).
|
||||
* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html) with additional support for [currying](https://schungx.github.io/rhai/language/fn-curry.html).
|
||||
* [Closures](https://schungx.github.io/rhai/language/fn-closure.html) (anonymous functions) that can capture shared values.
|
||||
* Some syntactic support for [object-oriented programming (OOP)](https://schungx.github.io/rhai/language/oop.html).
|
||||
* Organize code base with dynamically-loadable [modules](https://schungx.github.io/rhai/language/modules.html).
|
||||
* Scripts are [optimized](https://rhaiscript.github.io/book/engine/optimize.html) (useful for template-based machine-generated scripts) for repeated evaluations.
|
||||
* Easy custom API development via [plugins](https://rhaiscript.github.io/book/plugins/index.html) system powered by procedural macros.
|
||||
* [Function overloading](https://rhaiscript.github.io/book/language/overload.html) and [operator overloading](https://rhaiscript.github.io/book/rust/operators.html).
|
||||
* Dynamic dispatch via [function pointers](https://rhaiscript.github.io/book/language/fn-ptr.html) with additional support for [currying](https://rhaiscript.github.io/book/language/fn-curry.html).
|
||||
* [Closures](https://rhaiscript.github.io/book/language/fn-closure.html) (anonymous functions) that can capture shared values.
|
||||
* Some syntactic support for [object-oriented programming (OOP)](https://rhaiscript.github.io/book/language/oop.html).
|
||||
* Organize code base with dynamically-loadable [modules](https://rhaiscript.github.io/book/language/modules.html).
|
||||
* Serialization/deserialization support via [serde](https://crates.io/crates/serde) (requires the `serde` feature).
|
||||
* Support for [minimal builds](https://schungx.github.io/rhai/start/builds/minimal.html) by excluding unneeded language [features](https://schungx.github.io/rhai/start/features.html).
|
||||
* Support for [minimal builds](https://rhaiscript.github.io/book/start/builds/minimal.html) by excluding unneeded language [features](https://rhaiscript.github.io/book/start/features.html).
|
||||
|
||||
|
||||
Protected against attacks
|
||||
-------------------------
|
||||
|
||||
* Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless [explicitly permitted](https://schungx.github.io/rhai/patterns/control.html).
|
||||
* Rugged - protected against malicious attacks (such as [stack-overflow](https://schungx.github.io/rhai/safety/max-call-stack.html), [over-sized data](https://schungx.github.io/rhai/safety/max-string-size.html), and [runaway scripts](https://schungx.github.io/rhai/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts.
|
||||
* Track script evaluation [progress](https://schungx.github.io/rhai/safety/progress.html) and manually terminate a script run.
|
||||
* Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless [explicitly permitted](https://rhaiscript.github.io/book/patterns/control.html).
|
||||
* Rugged - protected against malicious attacks (such as [stack-overflow](https://rhaiscript.github.io/book/safety/max-call-stack.html), [over-sized data](https://rhaiscript.github.io/book/safety/max-string-size.html), and [runaway scripts](https://rhaiscript.github.io/book/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts.
|
||||
* Track script evaluation [progress](https://rhaiscript.github.io/book/safety/progress.html) and manually terminate a script run.
|
||||
|
||||
|
||||
For those who actually want their own language
|
||||
---------------------------------------------
|
||||
|
||||
* Use as a [DSL](https://schungx.github.io/rhai/engine/dsl.html).
|
||||
* Restrict the language by surgically [disabling keywords and operators](https://schungx.github.io/rhai/engine/disable.html).
|
||||
* Define [custom operators](https://schungx.github.io/rhai/engine/custom-op.html).
|
||||
* Extend the language with [custom syntax](https://schungx.github.io/rhai/engine/custom-syntax.html).
|
||||
* Use as a [DSL](https://rhaiscript.github.io/book/engine/dsl.html).
|
||||
* Restrict the language by surgically [disabling keywords and operators](https://rhaiscript.github.io/book/engine/disable.html).
|
||||
* Define [custom operators](https://rhaiscript.github.io/book/engine/custom-op.html).
|
||||
* Extend the language with [custom syntax](https://rhaiscript.github.io/book/engine/custom-syntax.html).
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai scripting engine and language.
|
||||
See [The Rhai Book](https://rhaiscript.github.io/book) for details on the Rhai scripting engine and language.
|
||||
|
||||
To build _The Book_, first install [`mdbook`](https://github.com/rust-lang/mdBook)
|
||||
and [`mdbook-tera`](https://github.com/avitex/mdbook-tera) (for templating).
|
||||
@ -87,8 +87,8 @@ License
|
||||
|
||||
Licensed under either of the following, at your choice:
|
||||
|
||||
* [Apache License, Version 2.0](https://github.com/jonathandturner/rhai/blob/master/LICENSE-APACHE.txt), or
|
||||
* [MIT license](https://github.com/jonathandturner/rhai/blob/master/LICENSE-MIT.txt)
|
||||
* [Apache License, Version 2.0](https://github.com/rhaiscript/rhai/blob/master/LICENSE-APACHE.txt), or
|
||||
* [MIT license](https://github.com/rhaiscript/rhai/blob/master/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
|
||||
|
@ -8,12 +8,15 @@ Breaking changes
|
||||
----------------
|
||||
|
||||
* The error variant `EvalAltResult::ErrorInFunctionCall` has a new parameter holding the _source_ of the function.
|
||||
* `ParseErrorType::WrongFnDefinition` is renamed `FnWrongDefinition`.
|
||||
* Redefining an existing function within the same script now throws a new `ParseErrorType::FnDuplicatedDefinition`. This is to prevent accidental overwriting an earlier function definition.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* Source information is provided when there is an error within a call to a function defined in another module.
|
||||
* Source information is provided to the `NativeCallContext` for native Rust functions.
|
||||
* `EvalAltResult::clear_position` to clear the position information of an error - useful when only the message is needed and the position doesn't need to be printed out.
|
||||
|
||||
|
||||
Version 0.19.9
|
||||
@ -478,7 +481,7 @@ Version 0.16.0
|
||||
|
||||
The major new feature in this version is OOP - well, poor man's OOP, that is.
|
||||
|
||||
The `README` is officially transferred to [The Rhai Book](https://schungx.github.io/rhai).
|
||||
The `README` is officially transferred to [The Rhai Book](https://rhaiscript.github.io/book).
|
||||
|
||||
An online [Playground](https://alvinhochun.github.io/rhai-demo/) is available.
|
||||
|
||||
@ -503,7 +506,7 @@ New features
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* [The Rhai Book](https://schungx.github.io/rhai) is online. Most content in the original `README` was transferred to the Book.
|
||||
* [The Rhai Book](https://rhaiscript.github.io/book) is online. Most content in the original `README` was transferred to the Book.
|
||||
* New feature `internals` to expose internal data structures (e.g. the AST nodes).
|
||||
|
||||
|
||||
|
@ -4,8 +4,8 @@ version = "0.3.1"
|
||||
edition = "2018"
|
||||
authors = ["jhwgh1968"]
|
||||
description = "Procedural macro support package for Rhai, a scripting language for Rust"
|
||||
homepage = "https://schungx.github.io/rhai/plugins/index.html"
|
||||
repository = "https://github.com/jonathandturner/rhai"
|
||||
homepage = "https://rhaiscript.github.io/book/plugins/index.html"
|
||||
repository = "https://github.com/rhaiscript/rhai"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[lib]
|
||||
|
@ -2,4 +2,4 @@ Procedural Macros for Plugins
|
||||
=============================
|
||||
|
||||
This crate holds procedural macros for code generation, supporting the plugins system
|
||||
for [Rhai](https://github.com/jonathandturner/rhai).
|
||||
for [Rhai](https://github.com/rhaiscript/rhai).
|
||||
|
@ -17,7 +17,7 @@ pub mod test_module {
|
||||
fn main() {
|
||||
let n = Point {
|
||||
x: 0.0,
|
||||
y: 10.0,
|
||||
y: 10.0
|
||||
};
|
||||
if test_module::test_fn(n) {
|
||||
println!("yes");
|
||||
|
@ -19,5 +19,5 @@ help: consider importing one of these items
|
||||
|
|
||||
11 | use std::fmt::Pointer;
|
||||
|
|
||||
11 | use syn::export::fmt::Pointer;
|
||||
11 | use syn::__private::fmt::Pointer;
|
||||
|
|
||||
|
@ -1,41 +0,0 @@
|
||||
The Rhai Book
|
||||
=============
|
||||
|
||||
[_The Rhai Book_](https://schungx.github.io/rhai) 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/jonathandturner/rhai/blob/master) |
|
||||
| `repoTree` | points to the [root of the GitHub repo tree](https://github.com/jonathandturner/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/jonathandturner/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/jonathandturner/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/jonathandturner/rhai/blob/master",
|
||||
"repoTree": "https://github.com/jonathandturner/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.
|