169 lines
4.0 KiB
Markdown
169 lines
4.0 KiB
Markdown
Global Constants
|
|
================
|
|
|
|
{{#include ../links.md}}
|
|
|
|
|
|
```admonish info "Usage scenario"
|
|
|
|
* Script has a lot of duplicated [constants] used inside [functions].
|
|
|
|
* For easier management, [constants] are declared at the top of the script.
|
|
|
|
* As Rhai [functions] are pure, they cannot access [constants] declared at global level
|
|
except through [`global`].
|
|
|
|
* Sprinkling large number of [`global::CONSTANT`][`global`] throughout the script makes
|
|
it slow and cumbersome.
|
|
|
|
* Using [`global`] or a [variable resolver] defeats
|
|
[constants propagation]({{rootUrl}}/engine/optimize/constants.md) in [script optimization].
|
|
```
|
|
|
|
```admonish abstract "Key concepts"
|
|
|
|
* The key to global [constants] is to use them to [optimize][script optimization] a script.
|
|
Otherwise, it would be just as simple to pass the constants into a custom [`Scope`] instead.
|
|
|
|
* The script is first compiled into an [`AST`], and all [constants] are extracted.
|
|
|
|
* The [constants] are then supplied to [re-optimize][script optimization] the [`AST`].
|
|
|
|
* This pattern also works under [_Strict Variables Mode_][strict variables].
|
|
```
|
|
|
|
|
|
Example
|
|
-------
|
|
|
|
Assume that the following Rhai script needs to work (but it doesn't).
|
|
|
|
```rust
|
|
// These are constants
|
|
|
|
const FOO = 1;
|
|
const BAR = 123;
|
|
const MAGIC_NUMBER = 42;
|
|
|
|
fn get_magic() {
|
|
MAGIC_NUMBER // <- oops! 'MAGIC_NUMBER' not found!
|
|
}
|
|
|
|
fn calc_foo(x) {
|
|
x * global::FOO // <- works but cumbersome; not desirable!
|
|
}
|
|
|
|
let magic = get_magic() * BAR;
|
|
|
|
let x = calc_foo(magic);
|
|
|
|
print(x);
|
|
```
|
|
|
|
|
|
Step 1 – Compile Script into `AST`
|
|
----------------------------------------
|
|
|
|
Compile the script into [`AST`] form.
|
|
|
|
Normally, it is useful to disable [optimizations][script optimization] at this stage since
|
|
the [`AST`] will be re-optimized later.
|
|
|
|
[_Strict Variables Mode_][strict variables] must be OFF for this to work.
|
|
|
|
```rust
|
|
// Turn Strict Variables Mode OFF (if necessary)
|
|
engine.set_strict_variables(false);
|
|
|
|
// Turn optimizations OFF
|
|
engine.set_optimization_level(OptimizationLevel::None);
|
|
|
|
let ast = engine.compile("...")?;
|
|
```
|
|
|
|
|
|
Step 2 – Extract Constants
|
|
--------------------------------
|
|
|
|
Use [`AST::iter_literal_variables`](https://docs.rs/rhai/{{version}}/rhai/struct.AST.html#method.iter_literal_variables)
|
|
to extract top-level [constants] from the [`AST`].
|
|
|
|
```rust
|
|
let mut scope = Scope::new();
|
|
|
|
// Extract all top-level constants without running the script
|
|
ast.iter_literal_variables(true, false).for_each(|(name, _, value)|
|
|
scope.push_constant(name, value);
|
|
);
|
|
|
|
// 'scope' now contains: FOO, BAR, MAGIC_NUMBER
|
|
```
|
|
|
|
|
|
Step 3a – Propagate Constants
|
|
-----------------------------------
|
|
|
|
[Re-optimize][script optimization] the [`AST`] using the new constants.
|
|
|
|
```rust
|
|
// Turn optimization back ON
|
|
engine.set_optimization_level(OptimizationLevel::Simple);
|
|
|
|
let ast = engine.optimize_ast(&scope, ast, engine.optimization_level());
|
|
```
|
|
|
|
|
|
Step 3b – Recompile Script (Alternative)
|
|
----------------------------------------------
|
|
|
|
If [_Strict Variables Mode_][strict variables] is used, however, it is necessary to re-compile the
|
|
script in order to detect undefined [variable] usages.
|
|
|
|
```rust
|
|
// Turn Strict Variables Mode back ON
|
|
engine.set_strict_variables(true);
|
|
|
|
// Turn optimization back ON
|
|
engine.set_optimization_level(OptimizationLevel::Simple);
|
|
|
|
// Re-compile the script using constants in 'scope'
|
|
let ast = engine.compile_with_scope(&scope, "...")?;
|
|
```
|
|
|
|
|
|
Step 4 – Run the Script
|
|
-----------------------------
|
|
|
|
At this step, the [`AST`] is now optimized with constants propagated into all access sites.
|
|
|
|
The script essentially becomes:
|
|
|
|
```rust
|
|
// These are constants
|
|
|
|
const FOO = 1;
|
|
const BAR = 123;
|
|
const MAGIC_NUMBER = 42;
|
|
|
|
fn get_magic() {
|
|
42 // <- constant replaced by value
|
|
}
|
|
|
|
fn calc_foo(x) {
|
|
x * global::FOO
|
|
}
|
|
|
|
let magic = get_magic() * 123; // <- constant replaced by value
|
|
|
|
let x = calc_foo(magic);
|
|
|
|
print(x);
|
|
```
|
|
|
|
Run it via `Engine::run_ast` or `Engine::eval_ast`.
|
|
|
|
```rust
|
|
// The 'scope' is no longer necessary
|
|
engine.run_ast(&ast)?;
|
|
```
|