Update docs.
This commit is contained in:
parent
f8c14ba1c4
commit
95b8dcc623
@ -489,7 +489,7 @@ mod generate_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
let expected_tokens = quote! {
|
||||||
impl PluginFunction for MyType {
|
impl PluginFunction for TestStruct {
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
@ -499,7 +499,7 @@ mod generate_tests {
|
|||||||
|
|
||||||
fn is_method_call(&self) -> bool { false }
|
fn is_method_call(&self) -> bool { false }
|
||||||
fn is_variadic(&self) -> bool { false }
|
fn is_variadic(&self) -> bool { false }
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(MyType()) }
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(TestStruct()) }
|
||||||
fn input_names(&self) -> Box<[&'static str]> {
|
fn input_names(&self) -> Box<[&'static str]> {
|
||||||
new_vec!["x: usize"].into_boxed_slice()
|
new_vec!["x: usize"].into_boxed_slice()
|
||||||
}
|
}
|
||||||
@ -513,7 +513,7 @@ mod generate_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
assert_streams_eq(item_fn.generate_impl("MyType"), expected_tokens);
|
assert_streams_eq(item_fn.generate_impl("TestStruct"), expected_tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -10,16 +10,16 @@ Operators
|
|||||||
| Operator | Description | Binary? | Binding direction |
|
| Operator | Description | Binary? | Binding direction |
|
||||||
| :-----------------------------------------------------------------------------------------: | -------------------------------------- | :--------: | :---------------: |
|
| :-----------------------------------------------------------------------------------------: | -------------------------------------- | :--------: | :---------------: |
|
||||||
| `+` | add | yes | left |
|
| `+` | add | yes | left |
|
||||||
| `-` | 1) subtract<br/>2) negative | yes<br/>no | left<br/>right |
|
| `-` | 1) subtract<br/>2) negative (prefix) | yes<br/>no | left<br/>right |
|
||||||
| `*` | multiply | yes | left |
|
| `*` | multiply | yes | left |
|
||||||
| `/` | divide | yes | left |
|
| `/` | divide | yes | left |
|
||||||
| `%` | modulo | yes | left |
|
| `%` | modulo | yes | left |
|
||||||
| `~` | power | yes | left |
|
| `~` | power | yes | left |
|
||||||
| `>>` | right bit-shift | yes | left |
|
| `>>` | right bit-shift | yes | left |
|
||||||
| `<<` | left bit-shift | yes | left |
|
| `<<` | left bit-shift | yes | left |
|
||||||
| `&` | 1) bit-wise _And_<br/>2) boolean _And_ | yes | left |
|
| `&` | 1) bit-wise _AND_<br/>2) boolean _AND_ | yes | left |
|
||||||
| <code>\|</code> | 1) bit-wise _Or_<br/>2) boolean _Or_ | yes | left |
|
| <code>\|</code> | 1) bit-wise _OR_<br/>2) boolean _OR_ | yes | left |
|
||||||
| `^` | 1) bit-wise _Xor_<br/>2) boolean _Xor_ | yes | left |
|
| `^` | 1) bit-wise _XOR_<br/>2) boolean _XOR_ | yes | left |
|
||||||
| `=`, `+=`, `-=`, `*=`, `/=`,<br/>`~=`, `%=`, `<<=`, `>>=`, `&=`,<br/><code>\|=</code>, `^=` | assignments | yes | right |
|
| `=`, `+=`, `-=`, `*=`, `/=`,<br/>`~=`, `%=`, `<<=`, `>>=`, `&=`,<br/><code>\|=</code>, `^=` | assignments | yes | right |
|
||||||
| `==` | equals to | yes | left |
|
| `==` | equals to | yes | left |
|
||||||
| `~=` | not equals to | yes | left |
|
| `~=` | not equals to | yes | left |
|
||||||
@ -27,9 +27,9 @@ Operators
|
|||||||
| `>=` | greater than or equals to | yes | left |
|
| `>=` | greater than or equals to | yes | left |
|
||||||
| `<` | less than | yes | left |
|
| `<` | less than | yes | left |
|
||||||
| `<=` | less than or equals to | yes | left |
|
| `<=` | less than or equals to | yes | left |
|
||||||
| `&&` | boolean _And_ (short-circuits) | yes | left |
|
| `&&` | boolean _AND_ (short-circuits) | yes | left |
|
||||||
| <code>\|\|</code> | boolean _Or_ (short-circuits) | yes | left |
|
| <code>\|\|</code> | boolean _OR_ (short-circuits) | yes | left |
|
||||||
| `!` | boolean _Not_ | no | left |
|
| `!` | boolean _NOT_ | no | left |
|
||||||
| `[` .. `]` | indexing | yes | right |
|
| `[` .. `]` | indexing | yes | right |
|
||||||
| `.` | 1) property access<br/>2) method call | yes | right |
|
| `.` | 1) property access<br/>2) method call | yes | right |
|
||||||
|
|
||||||
|
@ -3,7 +3,10 @@ Compile a Script (to AST)
|
|||||||
|
|
||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
To repeatedly evaluate a script, _compile_ it first into an `AST` (abstract syntax tree) form:
|
To repeatedly evaluate a script, _compile_ it first with `Engine::compile` into an `AST`
|
||||||
|
(abstract syntax tree) form.
|
||||||
|
|
||||||
|
`Engine::eval_ast` evaluates a pre-compiled `AST`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Compile to an AST and store it for later evaluations
|
// Compile to an AST and store it for later evaluations
|
||||||
@ -16,7 +19,8 @@ for _ in 0..42 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Compiling a script file is also supported (not available under [`no_std`] or in [WASM] builds):
|
Compiling a script file is also supported with `Engine::compile_file`
|
||||||
|
(not available under [`no_std`] or in [WASM] builds):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let ast = engine.compile_file("hello_world.rhai".into())?;
|
let ast = engine.compile_file("hello_world.rhai".into())?;
|
||||||
|
@ -116,16 +116,17 @@ The function signature of an implementation is:
|
|||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| ----------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------- |
|
| -------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------- |
|
||||||
| `context` | `&mut EvalContext` | mutable reference to the current evaluation _context_ |
|
| `context` | `&mut EvalContext` | mutable reference to the current evaluation _context_ |
|
||||||
| - `context.scope` | `&mut Scope` | mutable reference to the current [`Scope`]; variables can be added to/removed from it |
|
| • `scope()` | `&Scope` | reference to the current [`Scope`] |
|
||||||
| - `context.engine()` | `&Engine` | reference to the current [`Engine`] |
|
| • `scope_mut()` | `&mut Scope` | mutable reference to the current [`Scope`]; variables can be added to/removed from it |
|
||||||
| - `context.imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements |
|
| • `engine()` | `&Engine` | reference to the current [`Engine`] |
|
||||||
| - `context.iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements |
|
||||||
| - `context.this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
|
| • `iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
||||||
| - `context.call_level()` | `usize` | the current nesting level of function calls |
|
| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
|
||||||
| `inputs` | `&[Expression]` | a list of input expression trees |
|
| • `call_level()` | `usize` | the current nesting level of function calls |
|
||||||
|
| `inputs` | `&[Expression]` | a list of input expression trees |
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
@ -168,7 +169,7 @@ In other words, any [`Scope`] calls that change the list of must come _before_ a
|
|||||||
let var_name = inputs[0].get_variable_name().unwrap();
|
let var_name = inputs[0].get_variable_name().unwrap();
|
||||||
let expression = inputs.get(1).unwrap();
|
let expression = inputs.get(1).unwrap();
|
||||||
|
|
||||||
context.scope.push(var_name, 0 as INT); // do this BEFORE 'context.eval_expression_tree'!
|
context.scope_mut().push(var_name, 0 as INT); // do this BEFORE 'context.eval_expression_tree'!
|
||||||
|
|
||||||
let result = context.eval_expression_tree(expression)?;
|
let result = context.eval_expression_tree(expression)?;
|
||||||
```
|
```
|
||||||
@ -195,7 +196,7 @@ fn implementation_func(
|
|||||||
let condition = inputs.get(2).unwrap();
|
let condition = inputs.get(2).unwrap();
|
||||||
|
|
||||||
// Push one new variable into the scope BEFORE 'context.eval_expression_tree'
|
// Push one new variable into the scope BEFORE 'context.eval_expression_tree'
|
||||||
context.scope.push(var_name, 0 as INT);
|
context.scope_mut().push(var_name, 0 as INT);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Evaluate the statement block
|
// Evaluate the statement block
|
||||||
@ -258,8 +259,8 @@ Step Six - Profit!
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
||||||
Really Advanced - Low Level Custom Syntax API
|
Really Advanced - Custom Parsers
|
||||||
--------------------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Sometimes it is desirable to have multiple custom syntax starting with the
|
Sometimes it is desirable to have multiple custom syntax starting with the
|
||||||
same symbol. This is especially common for _command-style_ syntax where the
|
same symbol. This is especially common for _command-style_ syntax where the
|
||||||
@ -276,30 +277,67 @@ perform add something; // Add something to the system
|
|||||||
perform remove something; // Delete something from the system
|
perform remove something; // Delete something from the system
|
||||||
```
|
```
|
||||||
|
|
||||||
For even more flexibility, there is a _low level_ API for custom syntax that
|
Alternatively, a custom syntax may have variable length, with a termination symbol:
|
||||||
allows the registration of an entire mini-parser.
|
|
||||||
|
```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_
|
Use `Engine::register_custom_syntax_raw` to register a custom syntax _parser_
|
||||||
together with the implementation function:
|
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
|
```rust
|
||||||
engine.register_custom_syntax_raw(
|
engine.register_custom_syntax_raw(
|
||||||
"perform",
|
"perform",
|
||||||
|stream| match stream.len() {
|
// 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 ...
|
// perform ...
|
||||||
1 => Ok(Some("$ident$".to_string())),
|
1 => Ok(Some("$ident$".to_string())),
|
||||||
// perform command ...
|
// perform command ...
|
||||||
2 => match stream[1].as_str() {
|
2 => match symbols[1].as_str() {
|
||||||
"action" => Ok(Some("$expr$".to_string())),
|
"action" => Ok(Some("$expr$".into())),
|
||||||
"hello" => Ok(Some("world".to_string())),
|
"hello" => Ok(Some("world".into())),
|
||||||
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".to_string())),
|
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".into())),
|
||||||
"cleanup" => Ok(None),
|
"cleanup" => Ok(None),
|
||||||
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
||||||
LexError::ImproperSymbol(format!("Improper command: {}", cmd))
|
LexError::ImproperSymbol(format!("Improper command: {}", cmd))
|
||||||
)), Position::NONE)),
|
)), Position::NONE)),
|
||||||
},
|
},
|
||||||
// perform command arg ...
|
// perform command arg ...
|
||||||
3 => match (stream[1].as_str(), stream[2].as_str()) {
|
3 => match (symbols[1].as_str(), symbols[2].as_str()) {
|
||||||
("action", _) => Ok(None),
|
("action", _) => Ok(None),
|
||||||
("hello", "world") => Ok(None),
|
("hello", "world") => Ok(None),
|
||||||
("update", arg) if arg == "system" => Ok(None),
|
("update", arg) if arg == "system" => Ok(None),
|
||||||
@ -315,7 +353,9 @@ engine.register_custom_syntax_raw(
|
|||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
0, // the number of new variables declared within this custom syntax
|
// Number of new variables declared by this custom syntax
|
||||||
|
0,
|
||||||
|
// Implementation function
|
||||||
implementation_func
|
implementation_func
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
@ -324,20 +364,24 @@ engine.register_custom_syntax_raw(
|
|||||||
|
|
||||||
The custom syntax parser has the following signature:
|
The custom syntax parser has the following signature:
|
||||||
|
|
||||||
> `Fn(stream: &[String]) -> Result<Option<String>, ParseError>`
|
> `Fn(symbols: &[ImmutableString], look_ahead: &str) -> Result<Option<ImmutableString>, ParseError>`
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | :---------: | -------------------------------------------------------------------------------------------------- |
|
| ------------ | :------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `stream` | `&[String]` | a slice of symbols that have been parsed so far, possibly containing `"$expr$"` and/or `"$block$"` |
|
| `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
|
### Return Value
|
||||||
|
|
||||||
The return value is `Result<Option<String>, ParseError>` where:
|
The return value is `Result<Option<ImmutableString>, ParseError>` where:
|
||||||
|
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `Ok(None)` | parsing complete and there are no more symbols to match |
|
| `Ok(None)` | parsing complete and there are no more symbols to match |
|
||||||
| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` |
|
| `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`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate that there is a syntax error, but it can be any `ParseError`. |
|
| `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`. |
|
||||||
|
@ -17,7 +17,7 @@ To add more functionalities to a _raw_ `Engine`, load [packages] into it.
|
|||||||
Built-in Operators
|
Built-in Operators
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
| Operators | Assignment operators | Supported for types (see [standard types]) |
|
| Operators | Assignment operators | Supported for types<br/>(see [standard types]) |
|
||||||
| ------------------------- | ---------------------------- | ----------------------------------------------------------------------------- |
|
| ------------------------- | ---------------------------- | ----------------------------------------------------------------------------- |
|
||||||
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `char`, `ImmutableString` |
|
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `char`, `ImmutableString` |
|
||||||
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
|
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
|
||||||
|
@ -24,7 +24,7 @@ engine.on_var(|name, index, context| {
|
|||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
||||||
)),
|
)),
|
||||||
// Silently maps 'chameleon' into 'innocent'.
|
// Silently maps 'chameleon' into 'innocent'.
|
||||||
"chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new(
|
"chameleon" => context.scope().get_value("innocent").map(Some).ok_or_else(|| Box::new(
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
||||||
)),
|
)),
|
||||||
// Return Ok(None) to continue with the normal variable resolution process.
|
// Return Ok(None) to continue with the normal variable resolution process.
|
||||||
@ -67,17 +67,17 @@ The function signature passed to `Engine::on_var` takes the following form:
|
|||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| ----------------------------- | :-----------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------------- | :-----------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `name` | `&str` | variable name |
|
| `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. |
|
| `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_ |
|
| `context` | `&EvalContext` | reference to the current evaluation _context_ |
|
||||||
| - `context.scope` | `&Scope` | reference to the current [`Scope`] containing all variables up to the current evaluation position |
|
| • `scope()` | `&Scope` | reference to the current [`Scope`] |
|
||||||
| - `context.engine()` | `&Engine` | reference to the current [`Engine`] |
|
| • `engine()` | `&Engine` | reference to the current [`Engine`] |
|
||||||
| - `context.imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements |
|
| • `imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements |
|
||||||
| - `context.iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
| • `iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
||||||
| - `context.this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
|
| • `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
|
||||||
| - `context.call_level()` | `usize` | the current nesting level of function calls |
|
| • `call_level()` | `usize` | the current nesting level of function calls |
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ r"
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Constants Can be Modified via Rust
|
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 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
|
a registered Rust function that takes a first `&mut` parameter - because there is no way for
|
||||||
@ -76,9 +76,15 @@ x.increment(); // call 'increment' defined in Rust with '&mut' first parame
|
|||||||
x == 43; // value of 'x' is changed!
|
x == 43; // value of 'x' is changed!
|
||||||
|
|
||||||
fn double() {
|
fn double() {
|
||||||
this *= 2; // function squares 'this'
|
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.double(); // <- error: cannot modify constant 'this'
|
||||||
|
|
||||||
x == 43; // value of 'x' is unchanged by script
|
x == 43; // value of 'x' is unchanged by script
|
||||||
|
@ -15,7 +15,7 @@ it is usually used to perform type-specific actions based on the actual value's
|
|||||||
```c
|
```c
|
||||||
let mystery = get_some_dynamic_value();
|
let mystery = get_some_dynamic_value();
|
||||||
|
|
||||||
switch mystery {
|
switch type_of(mystery) {
|
||||||
"i64" => print("Hey, I got an integer here!"),
|
"i64" => print("Hey, I got an integer here!"),
|
||||||
"f64" => print("Hey, I got a float here!"),
|
"f64" => print("Hey, I got a float here!"),
|
||||||
"string" => print("Hey, I got a string here!"),
|
"string" => print("Hey, I got a string here!"),
|
||||||
|
@ -134,29 +134,33 @@ impl Handler {
|
|||||||
// Say there are three events: 'start', 'end', 'update'.
|
// Say there are three events: 'start', 'end', 'update'.
|
||||||
// In a real application you'd be handling errors...
|
// In a real application you'd be handling errors...
|
||||||
pub fn on_event(&mut self, event_name: &str, event_data: i64) -> Result<(), Error> {
|
pub fn on_event(&mut self, event_name: &str, event_data: i64) -> Result<(), Error> {
|
||||||
|
let engine = &self.engine;
|
||||||
|
let scope = &mut self.scope;
|
||||||
|
let ast = &self.ast;
|
||||||
|
|
||||||
match event_name {
|
match event_name {
|
||||||
// The 'start' event maps to function 'start'.
|
// The 'start' event maps to function 'start'.
|
||||||
// In a real application you'd be handling errors...
|
// In a real application you'd be handling errors...
|
||||||
"start" => self.engine.call_fn(&mut self.scope, &self.ast, "start", (event_data,))?,
|
"start" => engine.call_fn(scope, ast, "start", (event_data,))?,
|
||||||
|
|
||||||
// The 'end' event maps to function 'end'.
|
// The 'end' event maps to function 'end'.
|
||||||
// In a real application you'd be handling errors...
|
// In a real application you'd be handling errors...
|
||||||
"end" => self.engine.call_fn(&mut self.scope, &self.ast, "end", (event_data,))?,
|
"end" => engine.call_fn(scope, ast, "end", (event_data,))?,
|
||||||
|
|
||||||
// The 'update' event maps to function 'update'.
|
// The 'update' event maps to function 'update'.
|
||||||
// This event provides a default implementation when the scripted function
|
// This event provides a default implementation when the scripted function
|
||||||
// is not found.
|
// is not found.
|
||||||
"update" => self.engine
|
"update" =>
|
||||||
.call_fn(&mut self.scope, &self.ast, "update", (event_data,))
|
engine.call_fn(scope, ast, "update", (event_data,))
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "update" => {
|
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "update" => {
|
||||||
// Default implementation of 'update' event handler
|
// Default implementation of 'update' event handler
|
||||||
self.scope.set_value("state2", SomeType::new(42));
|
self.scope.set_value("state2", SomeType::new(42));
|
||||||
// Turn function-not-found into a success
|
// Turn function-not-found into a success
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
_ => Err(err.into())
|
_ => Err(err.into())
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,9 +271,9 @@ use rhai::plugin::*; // a "prelude" import for macros
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod my_module {
|
mod my_module {
|
||||||
// This is the '+' operator for 'MyType'.
|
// This is the '+' operator for 'TestStruct'.
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add(obj: &mut MyType, value: i64) {
|
pub fn add(obj: &mut TestStruct, value: i64) {
|
||||||
obj.prop += value;
|
obj.prop += value;
|
||||||
}
|
}
|
||||||
// This function is 'calc (i64)'.
|
// This function is 'calc (i64)'.
|
||||||
@ -305,24 +305,24 @@ mod my_module {
|
|||||||
pub fn greet(name: &str) -> String {
|
pub fn greet(name: &str) -> String {
|
||||||
format!("hello, {}!", name)
|
format!("hello, {}!", name)
|
||||||
}
|
}
|
||||||
// This is a getter for 'MyType::prop'.
|
// This is a getter for 'TestStruct::prop'.
|
||||||
#[rhai_fn(get = "prop")]
|
#[rhai_fn(get = "prop")]
|
||||||
pub fn get_prop(obj: &mut MyType) -> i64 {
|
pub fn get_prop(obj: &mut TestStruct) -> i64 {
|
||||||
obj.prop
|
obj.prop
|
||||||
}
|
}
|
||||||
// This is a setter for 'MyType::prop'.
|
// This is a setter for 'TestStruct::prop'.
|
||||||
#[rhai_fn(set = "prop")]
|
#[rhai_fn(set = "prop")]
|
||||||
pub fn set_prop(obj: &mut MyType, value: i64) {
|
pub fn set_prop(obj: &mut TestStruct, value: i64) {
|
||||||
obj.prop = value;
|
obj.prop = value;
|
||||||
}
|
}
|
||||||
// This is an index getter for 'MyType'.
|
// This is an index getter for 'TestStruct'.
|
||||||
#[rhai_fn(index_get)]
|
#[rhai_fn(index_get)]
|
||||||
pub fn get_index(obj: &mut MyType, index: i64) -> bool {
|
pub fn get_index(obj: &mut TestStruct, index: i64) -> bool {
|
||||||
obj.list[index]
|
obj.list[index]
|
||||||
}
|
}
|
||||||
// This is an index setter for 'MyType'.
|
// This is an index setter for 'TestStruct'.
|
||||||
#[rhai_fn(index_set)]
|
#[rhai_fn(index_set)]
|
||||||
pub fn get_index(obj: &mut MyType, index: i64, state: bool) {
|
pub fn get_index(obj: &mut TestStruct, index: i64, state: bool) {
|
||||||
obj.list[index] = state;
|
obj.list[index] = state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,7 +344,7 @@ use rhai::plugin::*; // a "prelude" import for macros
|
|||||||
mod my_module {
|
mod my_module {
|
||||||
// This function can be called in five ways
|
// This function can be called in five ways
|
||||||
#[rhai_fn(name = "get_prop_value", name = "prop", name = "+", set = "prop", index_get)]
|
#[rhai_fn(name = "get_prop_value", name = "prop", name = "+", set = "prop", index_get)]
|
||||||
pub fn prop_function(obj: &mut MyType, index: i64) -> i64 {
|
pub fn prop_function(obj: &mut TestStruct, index: i64) -> i64 {
|
||||||
obj.prop[index]
|
obj.prop[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,14 +140,14 @@ with a special "pretty-print" name, [`type_of()`] will return that name instead.
|
|||||||
engine
|
engine
|
||||||
.register_type::<TestStruct1>()
|
.register_type::<TestStruct1>()
|
||||||
.register_fn("new_ts1", TestStruct1::new)
|
.register_fn("new_ts1", TestStruct1::new)
|
||||||
.register_type_with_name::<TestStruct2>("MyType")
|
.register_type_with_name::<TestStruct2>("TestStruct")
|
||||||
.register_fn("new_ts2", TestStruct2::new);
|
.register_fn("new_ts2", TestStruct2::new);
|
||||||
|
|
||||||
let ts1_type = engine.eval::<String>(r#"let x = new_ts1(); x.type_of()"#)?;
|
let ts1_type = engine.eval::<String>(r#"let x = new_ts1(); x.type_of()"#)?;
|
||||||
let ts2_type = engine.eval::<String>(r#"let x = new_ts2(); x.type_of()"#)?;
|
let ts2_type = engine.eval::<String>(r#"let x = new_ts2(); x.type_of()"#)?;
|
||||||
|
|
||||||
println!("{}", ts1_type); // prints 'path::to::TestStruct'
|
println!("{}", ts1_type); // prints 'path::to::TestStruct'
|
||||||
println!("{}", ts1_type); // prints 'MyType'
|
println!("{}", ts1_type); // prints 'TestStruct'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ which contains only one function: `resolve`.
|
|||||||
When Rhai prepares to load a module, `ModuleResolver::resolve` is called with the name
|
When Rhai prepares to load a module, `ModuleResolver::resolve` is called with the name
|
||||||
of the _module path_ (i.e. the path specified in the [`import`] statement).
|
of the _module path_ (i.e. the path specified in the [`import`] statement).
|
||||||
|
|
||||||
* Upon success, it should return an [`Rc<Module>`][module] (or `Arc<Module>` under [`sync`]).
|
* Upon success, it should return an [`Rc<Module>`][module] (or [`Arc<Module>`][module] under [`sync`]).
|
||||||
|
|
||||||
The module should call `Module::build_index` on the target module before returning.
|
The module should call `Module::build_index` on the target module before returning.
|
||||||
This method flattens the entire module tree and _indexes_ it for fast function name resolution.
|
This method flattens the entire module tree and _indexes_ it for fast function name resolution.
|
||||||
@ -66,7 +66,7 @@ engine.set_module_resolver(Some(MyModuleResolver {}));
|
|||||||
|
|
||||||
engine.consume(r#"
|
engine.consume(r#"
|
||||||
import "hello" as foo; // this 'import' statement will call
|
import "hello" as foo; // this 'import' statement will call
|
||||||
// 'MyModuleResolver::resolve' with "hello" as `path`
|
// 'MyModuleResolver::resolve' with "hello" as 'path'
|
||||||
foo:bar();
|
foo:bar();
|
||||||
"#)?;
|
"#)?;
|
||||||
```
|
```
|
||||||
|
@ -65,14 +65,14 @@ The function signature passed to `Engine::register_raw_fn` takes the following f
|
|||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| ----------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| -------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `T` | `impl Clone` | return type of the function |
|
| `T` | `impl Clone` | return type of the function |
|
||||||
| `context` | `NativeCallContext` | the current _native call context_ |
|
| `context` | `NativeCallContext` | the current _native call context_ |
|
||||||
| - `context.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]. |
|
| • `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]. |
|
||||||
| - `context.imports()` | `Option<&Imports>` | reference to the current stack of [modules] imported via `import` statements (if any) |
|
| • `imports()` | `Option<&Imports>` | reference to the current stack of [modules] imported via `import` statements (if any) |
|
||||||
| - `context.iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
| • `iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
|
||||||
| `args` | `&mut [&mut Dynamic]` | a slice containing `&mut` references to [`Dynamic`] values.<br/>The slice is guaranteed to contain enough arguments _of the correct types_. |
|
| `args` | `&mut [&mut Dynamic]` | a slice containing `&mut` references to [`Dynamic`] values.<br/>The slice is guaranteed to contain enough arguments _of the correct types_. |
|
||||||
|
|
||||||
### Return value
|
### Return value
|
||||||
|
|
||||||
|
@ -46,5 +46,7 @@ Operations Count vs. Progress Percentage
|
|||||||
|
|
||||||
Notice that the _operations count_ value passed into the closure does not indicate the _percentage_ of work
|
Notice that the _operations count_ value passed into the closure does not indicate the _percentage_ of work
|
||||||
already done by the script (and thus it is not real _progress_ tracking), because it is impossible to determine
|
already done by the script (and thus it is not real _progress_ tracking), because it is impossible to determine
|
||||||
how long a script may run. It is possible, however, to calculate this percentage based on an estimated
|
how long a script may run.
|
||||||
total number of operations for a typical run.
|
|
||||||
|
It is possible, however, to calculate this percentage based on an estimated total number of operations
|
||||||
|
for a typical run.
|
||||||
|
@ -195,7 +195,7 @@ fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyType = Rc<RefCell<INT>>;
|
type TestStruct = Rc<RefCell<INT>>;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -203,18 +203,18 @@ type MyType = Rc<RefCell<INT>>;
|
|||||||
fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
|
fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
// Register API on MyType
|
// Register API on TestStruct
|
||||||
engine
|
engine
|
||||||
.register_type_with_name::<MyType>("MyType")
|
.register_type_with_name::<TestStruct>("TestStruct")
|
||||||
.register_get_set(
|
.register_get_set(
|
||||||
"data",
|
"data",
|
||||||
|p: &mut MyType| *p.borrow(),
|
|p: &mut TestStruct| *p.borrow(),
|
||||||
|p: &mut MyType, value: INT| *p.borrow_mut() = value,
|
|p: &mut TestStruct, value: INT| *p.borrow_mut() = value,
|
||||||
)
|
)
|
||||||
.register_fn("+=", |p1: &mut MyType, p2: MyType| {
|
.register_fn("+=", |p1: &mut TestStruct, p2: TestStruct| {
|
||||||
*p1.borrow_mut() += *p2.borrow()
|
*p1.borrow_mut() += *p2.borrow()
|
||||||
})
|
})
|
||||||
.register_fn("-=", |p1: &mut MyType, p2: MyType| {
|
.register_fn("-=", |p1: &mut TestStruct, p2: TestStruct| {
|
||||||
*p1.borrow_mut() -= *p2.borrow()
|
*p1.borrow_mut() -= *p2.borrow()
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let res = engine.eval_ast::<Map>(&ast)?;
|
let res = engine.eval_ast::<Map>(&ast)?;
|
||||||
|
|
||||||
// Make closure
|
// Make closure
|
||||||
let f = move |p1: MyType, p2: MyType| -> Result<(), Box<EvalAltResult>> {
|
let f = move |p1: TestStruct, p2: TestStruct| -> Result<(), Box<EvalAltResult>> {
|
||||||
let action_ptr = res["action"].clone().cast::<FnPtr>();
|
let action_ptr = res["action"].clone().cast::<FnPtr>();
|
||||||
let name = action_ptr.fn_name();
|
let name = action_ptr.fn_name();
|
||||||
engine.call_fn::<_, ()>(&mut Scope::new(), &ast, name, (p1, p2))
|
engine.call_fn::<_, ()>(&mut Scope::new(), &ast, name, (p1, p2))
|
||||||
|
Loading…
Reference in New Issue
Block a user