Global Constants

Example

Assume that the following Rhai script needs to work (but it doesn’t).

// 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 at this stage since the AST will be re-optimized later.

Strict Variables Mode must be OFF for this to work.

// 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 to extract top-level constants from the AST.

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 the AST using the new constants.

// 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 is used, however, it is necessary to re-compile the script in order to detect undefined variable usages.

// 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:

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

// The 'scope' is no longer necessary
engine.run_ast(&ast)?;