commit
852dd5c777
@ -15,28 +15,51 @@ allow combining all functions in one [`AST`] into another, forming a new, unifie
|
|||||||
|
|
||||||
In general, there are two types of _namespaces_ where functions are looked up:
|
In general, there are two types of _namespaces_ where functions are looked up:
|
||||||
|
|
||||||
| Namespace | Source | Lookup method | Sub-modules? | Variables? |
|
| Namespace | How Many | Source | Lookup method | Sub-modules? | Variables? |
|
||||||
| --------- | ------------------------------------------------------------------------------------- | --------------------------------- | :----------: | :--------: |
|
| --------- | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | :----------: | :--------: |
|
||||||
| Global | 1) `Engine::register_XXX` API<br/>2) [`AST`] being evaluated<br/>3) [packages] loaded | simple function name | ignored | ignored |
|
| Global | One | 1) [`AST`] being evaluated<br/>2) `Engine::register_XXX` API<br/>3) [packages] loaded<br/>4) functions in [modules] loaded via `Engine::register_module` and marked _global_ | simple function name | ignored | ignored |
|
||||||
| Module | [`Module`] | namespace-qualified function name | yes | yes |
|
| Module | Many | [`Module`] | namespace-qualified function name | yes | yes |
|
||||||
|
|
||||||
|
|
||||||
|
Module Namespace
|
||||||
|
----------------
|
||||||
|
|
||||||
|
There can be multiple module namespaces at any time during a script evaluation, loaded via the
|
||||||
|
[`import`] statement.
|
||||||
|
|
||||||
|
Functions and variables in module namespaces are isolated and encapsulated within their own environments.
|
||||||
|
|
||||||
|
They must be called or accessed in a _namespace-qualified_ manner.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
import "my_module" as m; // new module namespace 'm' created via 'import'
|
||||||
|
|
||||||
|
let x = m::calc_result(); // namespace-qualified function call
|
||||||
|
|
||||||
|
let y = m::MY_NUMBER; // namespace-qualified variable (constant) access
|
||||||
|
|
||||||
|
let x = calc_result(); // <- error: function 'calc_result' not found
|
||||||
|
// in global namespace!
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Global Namespace
|
Global Namespace
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
There is one _global_ namespace for every [`Engine`], which includes:
|
There is one _global_ namespace for every [`Engine`], which includes (in the following search order):
|
||||||
|
|
||||||
* All the native Rust functions registered via the `Engine::register_XXX` API.
|
* All functions defined in the [`AST`] currently being evaluated.
|
||||||
|
|
||||||
* All the Rust functions defined in [packages] that are loaded into the [`Engine`].
|
* All native Rust functions and iterators registered via the `Engine::register_XXX` API.
|
||||||
|
|
||||||
* All the [modules] imported via [`import`] statements.
|
* All functions and iterators defined in [packages] that are loaded into the [`Engine`].
|
||||||
|
|
||||||
In addition, during evaluation of an [`AST`], all script-defined functions bundled together within
|
* Functions defined in [modules] loaded via `Engine::register_module` that are specifically marked
|
||||||
the [`AST`] are added to the global namespace and override any existing registered functions of
|
for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]).
|
||||||
the same names and number of parameters.
|
|
||||||
|
Anywhere in a Rhai script, when a function call is made, the function is searched within the
|
||||||
|
global namespace, in the above search order.
|
||||||
|
|
||||||
Anywhere in a Rhai script, when a function call is made, it is searched within the global namespace.
|
|
||||||
Therefore, function calls in Rhai are _late_ bound - meaning that the function called cannot be
|
Therefore, function calls in Rhai are _late_ bound - meaning that the function called cannot be
|
||||||
determined or guaranteed and there is no way to _lock down_ the function being called.
|
determined or guaranteed and there is no way to _lock down_ the function being called.
|
||||||
This aspect is very similar to JavaScript before ES6 modules.
|
This aspect is very similar to JavaScript before ES6 modules.
|
||||||
|
@ -514,6 +514,18 @@ impl Module {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the namespace of a registered function.
|
||||||
|
///
|
||||||
|
/// The [`u64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or
|
||||||
|
/// the function [`crate::calc_script_fn_hash`].
|
||||||
|
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
||||||
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
|
f.namespace = namespace;
|
||||||
|
}
|
||||||
|
self.indexed = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a Rust function into the module, returning a hash key.
|
/// Set a Rust function into the module, returning a hash key.
|
||||||
///
|
///
|
||||||
/// If there is an existing Rust function of the same hash, it is replaced.
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
|
@ -172,8 +172,7 @@ mod print_debug_functions {
|
|||||||
let len = map.len();
|
let len = map.len();
|
||||||
|
|
||||||
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
||||||
result.push_str(k);
|
result.push_str(&format!("{:?}: ", k));
|
||||||
result.push_str(": ");
|
|
||||||
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, v));
|
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, v));
|
||||||
if i < len - 1 {
|
if i < len - 1 {
|
||||||
result.push_str(", ");
|
result.push_str(", ");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
|
use rhai::{Engine, EvalAltResult, FnNamespace, Module, ParseErrorType, RegisterFn, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -51,6 +51,31 @@ fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[test]
|
||||||
|
fn test_functions_namespaces() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
{
|
||||||
|
let mut m = Module::new();
|
||||||
|
let hash = m.set_fn_0("test", || Ok(999 as INT));
|
||||||
|
m.update_fn_namespace(hash, FnNamespace::Global);
|
||||||
|
|
||||||
|
engine.register_module("hello", m);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("test()")?, 999);
|
||||||
|
assert_eq!(engine.eval::<INT>("fn test() { 123 } test()")?, 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.register_fn("test", || 42 as INT);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("test()")?, 42);
|
||||||
|
assert_eq!(engine.eval::<INT>("fn test() { 123 } test()")?, 123);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
|
fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
@ -73,6 +73,6 @@ fn test_print_custom_type() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x.to_string()
|
x.to_string()
|
||||||
"#
|
"#
|
||||||
)?
|
)?
|
||||||
.contains("e: hello: 42"));
|
.contains(r#""e": hello: 42"#));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user