161 lines
4.2 KiB
Markdown
161 lines
4.2 KiB
Markdown
Compile a Script (to AST)
|
|
=========================
|
|
|
|
{{#include ../links.md}}
|
|
|
|
To repeatedly evaluate a script, _compile_ it first with `Engine::compile` into an `AST`
|
|
(**A**bstract **S**yntax **T**ree) form.
|
|
|
|
`Engine::eval_ast_XXX` and `Engine::run_ast_XXX` evaluate a pre-compiled `AST`.
|
|
|
|
```rust
|
|
// Compile to an AST and store it for later evaluations
|
|
let ast = engine.compile("40 + 2")?;
|
|
|
|
for _ in 0..42 {
|
|
let result: i64 = engine.eval_ast(&ast)?;
|
|
|
|
println!("Answer #{i}: {result}"); // prints 42
|
|
}
|
|
```
|
|
|
|
~~~admonish tip.small "Tip: Compile script file"
|
|
|
|
Compiling script files is also supported via `Engine::compile_file`
|
|
(not available for [`no_std`] or [WASM] builds).
|
|
|
|
```rust
|
|
let ast = engine.compile_file("hello_world.rhai".into())?;
|
|
```
|
|
~~~
|
|
|
|
~~~admonish info.small "See also: `AST` manipulation API"
|
|
|
|
Advanced users who may want to manipulate an `AST`, especially the functions contained within,
|
|
should see the section on [_Manage AST's_](ast.md) for more details.
|
|
~~~
|
|
|
|
|
|
Practical Use – Header Template Scripts
|
|
---------------------------------------------
|
|
|
|
Sometimes it is desirable to include a standardized _header template_ in a script that contains
|
|
pre-defined [functions], [constants] and [imported][`import`] [modules].
|
|
|
|
```rust
|
|
// START OF THE HEADER TEMPLATE
|
|
// The following should run before every script...
|
|
|
|
import "hello" as h;
|
|
import "world" as w;
|
|
|
|
// Standard constants
|
|
|
|
const GLOBAL_CONSTANT = 42;
|
|
const SCALE_FACTOR = 1.2;
|
|
|
|
// Standard functions
|
|
|
|
fn foo(x, y) { ... }
|
|
|
|
fn bar() { ... }
|
|
|
|
fn baz() { ... }
|
|
|
|
// END OF THE HEADER TEMPLATE
|
|
|
|
// Everything below changes from run to run
|
|
|
|
foo(bar() + GLOBAL_CONSTANT, baz() * SCALE_FACTOR)
|
|
```
|
|
|
|
### Option 1 – The easy way
|
|
|
|
Prepend the script header template onto independent scripts and run them as a whole.
|
|
|
|
> **Pros:** Easy!
|
|
>
|
|
> **Cons:** If the header template is long, work is duplicated every time to parse it.
|
|
|
|
```rust
|
|
let header_template = "..... // scripts... .....";
|
|
|
|
for index in 0..10000 {
|
|
let user_script = db.get_script(index);
|
|
|
|
// Just merge the two scripts...
|
|
let combined_script = format!("{header_template}\n{user_script}\n");
|
|
|
|
// Run away!
|
|
let result = engine.eval::<i64>(combined_script)?;
|
|
|
|
println!("{result}");
|
|
}
|
|
```
|
|
|
|
### Option 2 – The hard way
|
|
|
|
Option 1 requires the script header template to be recompiled every time. This can be expensive if
|
|
the header is very long.
|
|
|
|
This option compiles both the script header template and independent scripts as separate `AST`'s
|
|
which are then joined together to form a combined `AST`.
|
|
|
|
> **Pros:** No need to recompile the header template!
|
|
>
|
|
> **Cons:** More work...
|
|
|
|
```rust
|
|
let header_template = "..... // scripts... .....";
|
|
|
|
let mut template_ast = engine.compile(header_template)?;
|
|
|
|
// If you don't want to run the template, only keep the functions
|
|
// defined inside (e.g. closures), clear out the statements.
|
|
template_ast.clear_statements();
|
|
|
|
for index in 0..10000 {
|
|
let user_script = db.get_script(index);
|
|
|
|
let user_ast = engine.compile(user_script)?;
|
|
|
|
// Merge the two AST's
|
|
let combined_ast = template_ast + user_ast;
|
|
|
|
// Run away!
|
|
let result = engine.eval_ast::<i64>(combined_ast)?;
|
|
|
|
println!("{result}");
|
|
```
|
|
|
|
### Option 3 – The not-so-hard way
|
|
|
|
Option 1 does repeated work, option 2 requires manipulating `AST`'s...
|
|
|
|
This option makes the scripted [functions] (not [imported][`import`] [modules] nor [constants]
|
|
however) available globally by first making it a [module] (via [`Module::eval_ast_as_new`](modules/ast.md))
|
|
and then loading it into the [`Engine`] via `Engine::register_global_module`.
|
|
|
|
> **Pros:** No need to recompile the header template!
|
|
>
|
|
> **Cons:** No [imported][`import`] [modules] nor [constants]; if the header template is changed, a new [`Engine`] must be created.
|
|
|
|
```rust
|
|
let header_template = "..... // scripts... .....";
|
|
|
|
let template_ast = engine.compile(header_template)?;
|
|
|
|
let template_module = Module::eval_ast_as_new(Scope::new(), &template_ast, &engine)?;
|
|
|
|
engine.register_global_module(template_module.into());
|
|
|
|
for index in 0..10000 {
|
|
let user_script = db.get_script(index);
|
|
|
|
// Run away!
|
|
let result = engine.eval::<i64>(user_script)?;
|
|
|
|
println!("{result}");
|
|
}
|
|
```
|