Update docs.

This commit is contained in:
Stephen Chung 2020-12-16 14:57:28 +08:00
parent f8c14ba1c4
commit 95b8dcc623
15 changed files with 169 additions and 109 deletions

View File

@ -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]

View File

@ -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 |

View File

@ -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())?;

View File

@ -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 | | &bull; `scope()` | `&Scope` | reference to the current [`Scope`] |
| - `context.engine()` | `&Engine` | reference to the current [`Engine`] | | &bull; `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 | | &bull; `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 | | &bull; `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 | | &bull; `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 | | &bull; `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
| `inputs` | `&[Expression]` | a list of input expression trees | | &bull; `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`. |

View File

@ -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`]) |

View File

@ -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 | | &bull; `scope()` | `&Scope` | reference to the current [`Scope`] |
| - `context.engine()` | `&Engine` | reference to the current [`Engine`] | | &bull; `engine()` | `&Engine` | reference to the current [`Engine`] |
| - `context.imports()` | `&Imports` | reference to the current stack of [modules] imported via `import` statements | | &bull; `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 | | &bull; `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 | | &bull; `this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
| - `context.call_level()` | `usize` | the current nesting level of function calls | | &bull; `call_level()` | `usize` | the current nesting level of function calls |
### Return Value ### Return Value

View File

@ -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

View File

@ -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!"),

View File

@ -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())
})? })?
} }
} }
} }

View File

@ -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]
} }
} }

View File

@ -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'
``` ```

View File

@ -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();
"#)?; "#)?;
``` ```

View File

@ -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]. | | &bull; `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) | | &bull; `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 | | &bull; `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

View File

@ -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.

View File

@ -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))