Add custom operators.

This commit is contained in:
Stephen Chung
2020-07-05 17:41:45 +08:00
parent 936a3ff44a
commit e390dd73e6
13 changed files with 326 additions and 139 deletions

View File

@@ -95,18 +95,19 @@ The Rhai Scripting Language
8. [Maximum Call Stack Depth](safety/max-call-stack.md)
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
8. [Advanced Topics](advanced.md)
1. [Disable Keywords and/or Operators](engine/disable.md)
2. [Object-Oriented Programming (OOP)](language/oop.md)
3. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md)
4. [Script Optimization](engine/optimize/index.md)
1. [Object-Oriented Programming (OOP)](language/oop.md)
2. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md)
3. [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)
5. [Eval Statement](language/eval.md)
9. [Appendix](appendix/index.md)
6. [Keywords](appendix/keywords.md)
7. [Operators](appendix/operators.md)
8. [Literals](appendix/literals.md)
4. [Disable Keywords and/or Operators](engine/disable.md)
5. [Custom Operators](engine/custom-op.md)
6. [Eval Statement](language/eval.md)
9. [Appendix](appendix/index.md)
1. [Keywords](appendix/keywords.md)
2. [Operators](appendix/operators.md)
3. [Literals](appendix/literals.md)

View File

@@ -66,3 +66,5 @@ Flexible
* Supports [most build targets](targets.md) including `no-std` and [WASM].
* Surgically [disable keywords and operators] to restrict the language.
* [Custom operators].

View File

@@ -3,28 +3,28 @@ Operators
{{#include ../links.md}}
| Operator | Description | Binary? |
| :---------------: | ------------------------------ | :-----: |
| `+` | Add | Yes |
| `-` | Subtract, Minus | Yes/No |
| `*` | Multiply | Yes |
| `/` | Divide | Yes |
| `%` | Modulo | Yes |
| `~` | Power | Yes |
| `>>` | Right bit-shift | Yes |
| `<<` | Left bit-shift | Yes |
| `&` | Bit-wise _And_, Boolean _And_ | Yes |
| <code>\|</code> | Bit-wise _Or_, Boolean _Or_ | Yes |
| `^` | Bit-wise _Xor_ | Yes |
| `==` | Equals to | Yes |
| `~=` | Not equals to | Yes |
| `>` | Greater than | Yes |
| `>=` | Greater than or equals to | Yes |
| `<` | Less than | Yes |
| `<=` | Less than or equals to | Yes |
| `>=` | Greater than or equals to | Yes |
| `&&` | Boolean _And_ (short-circuits) | Yes |
| <code>\|\|</code> | Boolean _Or_ (short-circuits) | Yes |
| `!` | Boolean _Not_ | No |
| `[` .. `]` | Indexing | Yes |
| `.` | Property access, Method call | Yes |
| Operator | Description | Binary? | Binding direction |
| :---------------: | ------------------------------ | :-----: | :---------------: |
| `+` | Add | Yes | Left |
| `-` | Subtract, Minus | Yes/No | Left |
| `*` | Multiply | Yes | Left |
| `/` | Divide | Yes | Left |
| `%` | Modulo | Yes | Left |
| `~` | Power | Yes | Left |
| `>>` | Right bit-shift | Yes | Left |
| `<<` | Left bit-shift | Yes | Left |
| `&` | Bit-wise _And_, Boolean _And_ | Yes | Left |
| <code>\|</code> | Bit-wise _Or_, Boolean _Or_ | Yes | Left |
| `^` | Bit-wise _Xor_ | Yes | Left |
| `==` | 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 |
| `>=` | Greater 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 |
| `.` | Property access, Method call | Yes | Right |

105
doc/src/engine/custom-op.md Normal file
View File

@@ -0,0 +1,105 @@
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.
Example
-------
```rust
use rhai::{Engine, RegisterFn};
let mut engine = Engine::new();
// Register a custom operator called 'foo' and give it
// a precedence of 140 (i.e. between +|- and *|/)
engine.register_custom_operator("foo", 140).unwrap();
// Register the implementation of the customer operator as a function
engine.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 short-cut.
Must Follow Variable Naming
--------------------------
All custom operators must be _identifiers_ that follow the same naming rules as [variables].
```rust
engine.register_custom_operator("foo", 20); // 'foo' is a valid custom operator
engine.register_custom_operator("=>", 30); // <- error: '=>' is not a valid custom operator
```
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", 140).unwrap();
engine.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.
The following _precedence table_ show 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 |
| Arithmetic | `+`, `-` | 130 |
| Arithmetic | `*`, `/`, `~` | 160 |
| Bit-shifts | `<<`, `>>` | 190 |
| Arithmetic | `%` | 210 |
| Object | `.` _(binds to right)_ | 240 |
| _Others_ | | 0 |
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.

View File

@@ -104,3 +104,5 @@
[`OptimizationLevel::None`]: {{rootUrl}}/engine/optimize/optimize-levels.md
[disable keywords and operators]: {{rootUrl}}/engine/disable.md
[custom operator]: {{rootUrl}}/engine/custom-op.md
[custom operators]: {{rootUrl}}/engine/custom-op.md