Improve writeup.

This commit is contained in:
Stephen Chung 2020-07-26 10:07:40 +08:00
parent 353df6bea1
commit 5e48478496
19 changed files with 156 additions and 89 deletions

View File

@ -73,7 +73,7 @@ Licensed under either:
* [Apache License, Version 2.0](https://github.com/jonathandturner/rhai/blob/master/LICENSE-APACHE.txt), or * [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) * [MIT license](https://github.com/jonathandturner/rhai/blob/master/LICENSE-MIT.txt)
at your option. at your choice.
Unless explicitly stated otherwise, any contribution intentionally submitted Unless explicitly stated otherwise, any contribution intentionally submitted
for inclusion in this crate, as defined in the Apache-2.0 license, shall for inclusion in this crate, as defined in the Apache-2.0 license, shall

View File

@ -21,7 +21,7 @@ The Rhai Scripting Language
2. [Scripts](start/examples/scripts.md) 2. [Scripts](start/examples/scripts.md)
3. [Using the `Engine`](engine/index.md) 3. [Using the `Engine`](engine/index.md)
1. [Hello World in Rhai - Evaluate a Script](engine/hello-world.md) 1. [Hello World in Rhai - Evaluate a Script](engine/hello-world.md)
2. [Compile a Script to AST for Repeated Evaluations](engine/compile.md) 2. [Compile to AST for Repeated Evaluations](engine/compile.md)
3. [Call a Rhai Function from Rust](engine/call-fn.md) 3. [Call a Rhai Function from Rust](engine/call-fn.md)
4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md) 4. [Create a Rust Anonymous Function from a Rhai Function](engine/func.md)
5. [Evaluate Expressions Only](engine/expressions.md) 5. [Evaluate Expressions Only](engine/expressions.md)
@ -65,21 +65,22 @@ The Rhai Scripting Language
5. [Variables](language/variables.md) 5. [Variables](language/variables.md)
6. [Constants](language/constants.md) 6. [Constants](language/constants.md)
7. [Logic Operators](language/logic.md) 7. [Logic Operators](language/logic.md)
8. [If Statement](language/if.md) 8. [Other Operators](language/other-op.md)
9. [While Loop](language/while.md) 9. [If Statement](language/if.md)
10. [Loop Statement](language/loop.md) 10. [While Loop](language/while.md)
11. [For Loop](language/for.md) 11. [Loop Statement](language/loop.md)
12. [Return Values](language/return.md) 12. [For Loop](language/for.md)
13. [Throw Exception on Error](language/throw.md) 13. [Return Values](language/return.md)
14. [Functions](language/functions.md) 14. [Throw Exception on Error](language/throw.md)
15. [Functions](language/functions.md)
1. [Call Method as Function](language/method.md) 1. [Call Method as Function](language/method.md)
2. [Overloading](language/overload.md) 2. [Overloading](language/overload.md)
3. [Namespaces](language/fn-namespaces.md) 3. [Namespaces](language/fn-namespaces.md)
4. [Function Pointers](language/fn-ptr.md) 4. [Function Pointers](language/fn-ptr.md)
5. [Anonymous Functions](language/fn-anon.md) 5. [Anonymous Functions](language/fn-anon.md)
6. [Currying](language/fn-curry.md) 6. [Currying](language/fn-curry.md)
15. [Print and Debug](language/print-debug.md) 16. [Print and Debug](language/print-debug.md)
16. [Modules](language/modules/index.md) 17. [Modules](language/modules/index.md)
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md) 1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
2. [Import Modules](language/modules/import.md) 2. [Import Modules](language/modules/import.md)
3. [Create from Rust](rust/modules/index.md) 3. [Create from Rust](rust/modules/index.md)

View File

@ -7,6 +7,9 @@ Rhai is an embedded scripting language and evaluation engine for Rust that gives
to add scripting to any application. to add scripting to any application.
This Book is for version {{version}} of Rhai. Versions
--------
This Book is for version **{{version}}** of Rhai.
For the latest development version, see [here]({{rootUrl}}/vnext/). For the latest development version, see [here]({{rootUrl}}/vnext/).

View File

@ -6,9 +6,10 @@ Licensing
Rhai is licensed under either: Rhai is licensed under either:
* [Apache License, Version 2.0]({{repoHome}}/LICENSE-APACHE.txt), or * [Apache License, Version 2.0]({{repoHome}}/LICENSE-APACHE.txt), or
* [MIT license]({{repoHome}}/LICENSE-MIT.txt) * [MIT license]({{repoHome}}/LICENSE-MIT.txt)
at your option. at your choice.
Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate, 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, as defined in the Apache-2.0 license, shall be dual-licensed as above,

View File

@ -18,21 +18,38 @@ It doesn't attempt to be a new language. For example:
* No first-class functions - Code your functions in Rust instead, and register them with Rhai. * No first-class functions - Code your functions in Rust instead, and register them with Rhai.
There is, however, support for simple [function pointers] allowing runtime dispatch by function name. There is, however, support for simple [function pointers] to allow runtime dispatch by function name.
* No garbage collection - this should be expected, so... * No garbage collection - this should be expected, so...
* No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md). * No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
But you can [curry][currying] a [function pointer] with arguments to simulate it somewhat.
* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. The purpose of Rhai is not * No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. The purpose of Rhai is not
to be extremely _fast_, but to make it as easy as possible to integrate with native Rust applications. to be extremely _fast_, but to make it as easy as possible to integrate with native Rust applications.
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 Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting advanced language features
such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc. such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc.
Avoid the temptation to write full-fledge application logic entirely in Rhai - that use case is best fulfilled by 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. more complete languages such as JavaScript or Lua.
Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call.
All your core functionalities should be in Rust. 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. 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.

View File

@ -5,7 +5,7 @@ Advanced Topics
This section covers advanced features such as: This section covers advanced features such as:
* Simulated [Object Oriented Programming][OOP]. * Simulated [Object Oriented Programming (OOP)][OOP].
* [`serde`] integration. * [`serde`] integration.
@ -13,4 +13,6 @@ This section covers advanced features such as:
* [Domain-Specific Languages][DSL]. * [Domain-Specific Languages][DSL].
* Low-level [function registration API]({{rootUrl}}/rust/register-raw.md)
* The dreaded (or beloved for those with twisted tastes) [`eval`] statement. * The dreaded (or beloved for those with twisted tastes) [`eval`] statement.

View File

@ -22,19 +22,31 @@ fn main() -> Result<(), Box<EvalAltResult>>
} }
``` ```
`rhai::EvalAltResult` is a Rust `enum` containing all errors encountered during the parsing or evaluation process. Evaluate a script file directly:
```rust
// 'eval_file' takes a 'PathBuf'
let result = engine.eval_file::<i64>("hello_world.rhai".into())?;
```
Evaluate a Script Error Type
---------------- ----------
The type parameter is used to specify the type of the return value, which _must_ match the actual type or an error is returned. `rhai::EvalAltResult` is the standard Rhai error type, which is a Rust `enum` containing all errors encountered
Rhai is very strict here. during the parsing or evaluation process.
Use [`Dynamic`] for uncertain return types.
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. There are two ways to specify the return type - _turbofish_ notation, or type inference.
Use [`Dynamic`] for uncertain return types.
```rust ```rust
let result = engine.eval::<i64>("40 + 2")?; // return type is i64, specified using 'turbofish' notation let result = engine.eval::<i64>("40 + 2")?; // return type is i64, specified using 'turbofish' notation
@ -46,9 +58,3 @@ let result: Dynamic = engine.eval("boo()")?; // use 'Dynamic' if you're not s
let result = engine.eval::<String>("40 + 2")?; // returns an error because the actual return type is i64, not String let result = engine.eval::<String>("40 + 2")?; // returns an error because the actual return type is i64, not String
``` ```
Evaluate a script file directly:
```rust
let result = engine.eval_file::<i64>("hello_world.rhai".into())?; // 'eval_file' takes a 'PathBuf'
```

View File

@ -10,7 +10,10 @@ In many controlled embedded environments, however, these may not be needed and u
application code storage space. application code storage space.
Use `Engine::new_raw` to create a _raw_ `Engine`, in which only a minimal set of Use `Engine::new_raw` to create a _raw_ `Engine`, in which only a minimal set of
basic arithmetic and logical operators are supported. basic arithmetic and logical operators are supported (see below).
To add more functionalities to a _raw_ `Engine`, load [packages] into it.
Built-in Operators Built-in Operators
------------------ ------------------
@ -20,7 +23,7 @@ Built-in Operators
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` | | `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` |
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) | | `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
| `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` | | `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` |
| `&`, `|`, | `&=`, `|=` | `INT`, `bool` | | `&`, <code>\|</code>, | `&=`, <code>\|=</code> | `INT`, `bool` |
| `&&`, `||` | | `bool` | | `&&`, <code>\|\|</code> | | `bool` |
| `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` | | `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
| `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` | | `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |

View File

@ -3,7 +3,8 @@
{{#include ../links.md}} {{#include ../links.md}}
Iterating through a range or an [array] is provided by the `for` ... `in` loop. Iterating through a range or an [array], or any type with a registered _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; 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. `break` can be used to break out of the loop unconditionally.

View File

@ -19,6 +19,9 @@ if foo(x) {
} }
``` ```
Braces Are Mandatory
--------------------
Unlike C, the condition expression does _not_ need to be enclosed in parentheses '`(`' .. '`)`', but 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 '`{`' .. '`}`', all branches of the `if` statement must be enclosed within braces '`{`' .. '`}`',
even when there is only one statement inside the branch. even when there is only one statement inside the branch.

View File

@ -65,42 +65,3 @@ a() | b(); // both a() and b() are evaluated
a() & b(); // both a() and b() are evaluated a() & b(); // both a() and b() are evaluated
``` ```
Compound Assignment Operators
----------------------------
```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 `+=` operator can also be used to build [strings]:
```rust
let my_str = "abc";
my_str += "ABC";
my_str += 12345;
my_str == "abcABC12345"
```

View File

@ -4,8 +4,8 @@ Call Method as Function
{{#include ../links.md}} {{#include ../links.md}}
First `&mut` Reference Parameter First `&mut` Parameter
------------------------------- ----------------------
Property [getters/setters] and [methods][custom types] in a Rust custom type registered with the [`Engine`] can be called Property [getters/setters] and [methods][custom types] in a Rust custom type registered with the [`Engine`] can be called
just like a regular function. In fact, like Rust, property getters/setters and object methods just like a regular function. In fact, like Rust, property getters/setters and object methods
@ -37,19 +37,20 @@ array[0].update(); // <- call in method-call style will update 'a'
``` ```
Encouraged Usage `&mut` is Efficient
---------------- ------------------
Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone, Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone,
even when the intention is not to mutate that argument, because it avoids cloning that argument value. even when the intention is not to mutate that argument, because it avoids cloning that argument value.
For primary types that are cheap to clone, including `ImmutableString`, this is not necessary. For primary types that are cheap to clone (e.g. those that implement `Copy`),
including `ImmutableString`, this is not necessary.
Avoid `&mut ImmutableString` Avoid `&mut ImmutableString`
--------------------------- ---------------------------
`ImmutableString`, Rhai internal [string] type, is an exception. `ImmutableString`, Rhai's internal [string] type, is an exception.
`ImmutableString` is cheap to clone, but expensive to take a mutable reference (because the underlying `ImmutableString` is cheap to clone, but expensive to take a mutable reference (because the underlying
string must be cloned to make a private copy). string must be cloned to make a private copy).

View File

@ -0,0 +1,66 @@
Other Operators
===============
{{#include ../links.md}}
Compound Assignment Operators
----------------------------
```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 `+=` operator can also be used to build [strings]:
```rust
let my_str = "abc";
my_str += "ABC";
my_str += 12345;
my_str == "abcABC12345"
```
It may also be used to concatenate [arrays]:
```rust
let my_array = [1, 2, 3];
my_array += [4, 5];
my_array == [1, 2, 3, 4, 5];
```
or 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;
```

View File

@ -66,8 +66,8 @@ let mut engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type::<TestStruct>();
``` ```
Methods on Custom Type Methods on The Custom Type
--------------------- -------------------------
To use native custom types, methods and functions in Rhai scripts, simply register them To use native custom types, methods and functions in Rhai scripts, simply register them
using one of the `Engine::register_XXX` API. using one of the `Engine::register_XXX` API.

View File

@ -11,8 +11,8 @@ see [fallible functions]({{rootUrl}}/rust/fallible.md)).
```rust ```rust
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString}; use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString};
use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn' use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn'
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn' use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
// Normal function that returns a standard type // Normal function that returns a standard type
// Remember to use 'ImmutableString' and not 'String' // Remember to use 'ImmutableString' and not 'String'
@ -26,7 +26,7 @@ fn add_len_str(x: i64, s: &str) -> i64 {
// Function that returns a 'Dynamic' value - must return a 'Result' // Function that returns a 'Dynamic' value - must return a 'Result'
fn get_any_value() -> Result<Dynamic, Box<EvalAltResult>> { fn get_any_value() -> Result<Dynamic, Box<EvalAltResult>> {
Ok((42_i64).into()) // standard types can use 'into()' Ok((42_i64).into()) // standard types can use 'into()'
} }
let mut engine = Engine::new(); let mut engine = Engine::new();

View File

@ -30,7 +30,7 @@ impl ModuleResolver for MyModuleResolver {
&self, &self,
engine: &Engine, // reference to the current 'Engine' engine: &Engine, // reference to the current 'Engine'
path: &str, // the module path path: &str, // the module path
pos: Position, // location of the 'import' statement pos: Position, // position of the 'import' statement
) -> Result<Module, Box<EvalAltResult>> { ) -> Result<Module, Box<EvalAltResult>> {
// Check module path. // Check module path.
if is_valid_module_path(path) { if is_valid_module_path(path) {

View File

@ -12,7 +12,9 @@ fn to_int(num) {
print("Ha! Gotcha! " + num); print("Ha! Gotcha! " + num);
} }
print(to_int(123)); // what happens? let x = (123).to_int();
print(x); // what happens?
``` ```
A registered native Rust function, in turn, overrides any built-in function of the A registered native Rust function, in turn, overrides any built-in function of the

View File

@ -37,8 +37,8 @@ Use `ImmutableString`
Internally, Rhai uses _immutable_ [strings] instead of the Rust `String` type. This is mainly to avoid excessive Internally, Rhai uses _immutable_ [strings] instead of the Rust `String` type. This is mainly to avoid excessive
cloning when passing function arguments. cloning when passing function arguments.
The encapsulated immutable string type is `ImmutableString`. It is cheap to clone (just an `Rc` or `Arc` reference Rhai's internal string type is `ImmutableString` (basically `Rc<String>` or `Arc<String>` depending on the [`sync`] feature).
count increment depending on the [`sync`] feature). It is cheap to clone, but expensive to modify (a new copy of the string must be made in order to change it).
Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`) Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`)
for the best performance with Rhai. for the best performance with Rhai.

View File

@ -25,7 +25,7 @@ more control over what a script can (or cannot) do.
| `no_module` | Disable loading external [modules]. | | `no_module` | Disable loading external [modules]. |
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
| `serde` | Enable serialization/deserialization via [`serde`]. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. | | `serde` | Enable serialization/deserialization via [`serde`]. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
| `internals` | Expose internal data structures (e.g. [`AST`] nodes) and enable defining [custom syntax]. Beware that Rhai internals are volatile and may change from version to version. | | `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
Example Example
@ -46,7 +46,7 @@ rhai = { version = "{{version}}", features = [ "sync", "unchecked", "only_i32",
``` ```
The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32`, `i16` or `i64`), The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32`, `i16` or `i64`),
no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining [functions] no floating-point, is `Send + Sync` (so it can be safely used across threads), and does not support defining [functions]
nor loading external [modules]. nor loading external [modules].
This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware. This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware.