Change Option<usize> to usize for variable resolver index.

This commit is contained in:
Stephen Chung 2020-10-11 22:41:26 +08:00
parent fd5a932611
commit e343bcfa8f
8 changed files with 68 additions and 29 deletions

View File

@ -121,6 +121,7 @@ The Rhai Scripting Language
5. [Multi-Layer Functions](patterns/multi-layer.md) 5. [Multi-Layer Functions](patterns/multi-layer.md)
6. [One Engine Instance Per Call](patterns/parallel.md) 6. [One Engine Instance Per Call](patterns/parallel.md)
7. [Scriptable Event Handler with State](patterns/events.md) 7. [Scriptable Event Handler with State](patterns/events.md)
8. [Dynamic Constants Provider](patterns/dynamic-const.md)
9. [Advanced Topics](advanced.md) 9. [Advanced Topics](advanced.md)
1. [Capture Scope for Function Call](language/fn-capture.md) 1. [Capture Scope for Function Call](language/fn-capture.md)
2. [Low-Level API](rust/register-raw.md) 2. [Low-Level API](rust/register-raw.md)

View File

@ -3,14 +3,4 @@ Advanced Topics
{{#include links.md}} {{#include links.md}}
This section covers advanced features such as: This section covers advanced features of the Rhai [`Engine`].
* [Advanced patterns]({{rootUrl}}/patterns/index.md) in using Rhai.
* [Capture the calling scope]({{rootUrl}}/language/fn-capture.md) in a function call.
* [`serde`] integration.
* Low-level [function registration API]({{rootUrl}}/rust/register-raw.md)
* [Domain-Specific Languages][DSL].

View File

@ -122,7 +122,7 @@ where:
* `context: &mut EvalContext` - mutable reference to the current evaluation _context_ (**do not touch**) which exposes the following fields: * `context: &mut EvalContext` - mutable reference to the current evaluation _context_ (**do not touch**) which exposes the following fields:
* `context.engine(): &Engine` - reference to the current [`Engine`]. * `context.engine(): &Engine` - reference to the current [`Engine`].
* `context.namespace(): &Module` - reference to the current _global namespace_ containing all script-defined functions. * `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions.
* `context.call_level(): usize` - the current nesting level of function calls. * `context.call_level(): usize` - the current nesting level of function calls.
* `inputs: &[Expression]` - a list of input expression trees. * `inputs: &[Expression]` - a list of input expression trees.

View File

@ -16,7 +16,7 @@ To do so, provide a closure to the [`Engine`] via the [`Engine::on_var`] method:
let mut engine = Engine::new(); let mut engine = Engine::new();
// Register a variable resolver. // Register a variable resolver.
engine.on_var(|name, index, engine, scope, lib| { engine.on_var(|name, index, scope, context| {
match name { match name {
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())), "MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
// Override a variable - make it not found even if it exists! // Override a variable - make it not found even if it exists!
@ -50,26 +50,23 @@ Function Signature
The function signature passed to `Engine::on_var` takes the following form: The function signature passed to `Engine::on_var` takes the following form:
> `Fn(name: &str, index: Option<usize>, scope: &Scope, context: &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static` > `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static`
where: where:
* `name: &str` - variable name. * `name: &str` - variable name.
* `index: Option<usize>` - an offset from the bottom of the current [`Scope`] that the variable is supposed to reside. * `index: usize` - an offset from the bottom of the current [`Scope`] that the variable is supposed to reside.
Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`. Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`.
Notice that, if there was an [`eval`] statement before the current statement, new variables may have been introduced and this index may be incorrect.
Therefore, this index is for reference only. It should not be relied upon.
If `index` is `None`, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed.
* `scope : &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position. * `scope : &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position.
* `context: &EvalContext` - reference to the current evaluation _context_, which exposes the following fields: * `context: &EvalContext` - reference to the current evaluation _context_, which exposes the following fields:
* `context.engine(): &Engine` - reference to the current [`Engine`]. * `context.engine(): &Engine` - reference to the current [`Engine`].
* `context.namespace(): &Module` - reference to the current _global namespace_ containing all script-defined functions. * `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions.
* `context.call_level(): usize` - the current nesting level of function calls. * `context.call_level(): usize` - the current nesting level of function calls.
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where `Ok(None)` indicates that the normal The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where `Ok(None)` indicates that the normal
variable resolution process should continue. variable resolution process should continue.

View File

@ -0,0 +1,56 @@
Dynamic Constants Provider
=========================
{{#include ../links.md}}
Usage Scenario
--------------
* A system has a _large_ number of constants, but only a minor set will be used by any script.
* The system constants are expensive to load.
* The system constants set is too massive to push into a custom [`Scope`].
* The values of system constants are volatile and call-dependent.
Key Concepts
------------
* Use a [variable resolver] to intercept variable access.
* Only load a variable when it is being used.
* Perform a lookup based on variable name, and provide the correct data value.
* May even perform back-end network access or look up the latest value from a database.
Implementation
--------------
```rust
let mut engine = Engine::new();
// Create shared data provider.
// Assume that Provider::get(&str) -> Option<value> gets a system constant.
let provider: Arc<Provider> = get_data_provider();
// Clone the shared provider
let db = provider.clone();
// Register a variable resolver.
// Move the shared provider into the closure.
engine.on_var(move |name, _, _, _| Ok(db.get(name).map(Dynamic::from)));
```
Values are Constants
--------------------
All values provided by a [variable resolver] are _constants_ due to their dynamic nature.
They cannot be assigned to.
In order to change values in an external system, register a dedicated API for that purpose.

View File

@ -1721,12 +1721,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn on_var( pub fn on_var(
&mut self, &mut self,
callback: impl Fn( callback: impl Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
&str,
Option<usize>,
&Scope,
&EvalContext,
) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> &mut Self { ) -> &mut Self {

View File

@ -745,7 +745,7 @@ impl Engine {
this_ptr, this_ptr,
level: 0, level: 0,
}; };
if let Some(result) = resolve_var(name, index.map(|v| v.get()), scope, &context) if let Some(result) = resolve_var(name, index.map_or(0, |v| v.get()), scope, &context)
.map_err(|err| err.fill_position(*pos))? .map_err(|err| err.fill_position(*pos))?
{ {
return Ok((result.into(), name, ScopeEntryType::Constant, *pos)); return Ok((result.into(), name, ScopeEntryType::Constant, *pos));

View File

@ -224,13 +224,13 @@ pub type Callback<T, R> = Box<dyn Fn(&T) -> R + Send + Sync + 'static>;
/// A standard callback function. /// A standard callback function.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub type OnVarCallback = Box< pub type OnVarCallback = Box<
dyn Fn(&str, Option<usize>, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> dyn Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ 'static, + 'static,
>; >;
/// A standard callback function. /// A standard callback function.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub type OnVarCallback = Box< pub type OnVarCallback = Box<
dyn Fn(&str, Option<usize>, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> dyn Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,