Manage AST’s
When compiling a Rhai script to an AST, the following data are packaged together as a single unit:
| Data | Type | Description | Requires feature | Access API |
|---|---|---|---|---|
| Source name | ImmutableString | optional text name to identify the source of the script | source(&self),clone_source(&self),set_source(&mut self, source),clear_source(&mut self) | |
| Module documentation | Vec<SmartString> | documentation of the script | metadata | doc(&self),clear_doc(&mut self) |
| Statements | Vec<Stmt> | list of script statements at global level | internals | statements(&self),statements_mut(&mut self) |
| Functions | Shared<Module> | functions defined in the script | internals,not no_function | shared_lib(&self) |
| Embedded module resolver | StaticModuleResolver | embedded module resolver for self-contained AST | internals,not no_module | resolver(&self) |
Most of the AST API is available only under the internals feature.
Use the source name to identify the source script in errors – useful when multiple modules are imported recursively.
For the complete AST API, refer to the documentation online.
Extract Only Functions
The following methods, not available under no_function, allow manipulation of the functions
encapsulated within an AST:
| Method | Description |
|---|---|
clone_functions_only(&self) | clone the AST into a new AST with only functions, excluding statements |
clone_functions_only_filtered(&self, filter) | clone the AST into a new AST with only functions that pass the filter predicate, excluding statements |
retain_functions(&mut self, filter) | remove all functions in the AST that do not pass a particular predicate filter; statements are untouched |
iter_functions(&self) | return an iterator on all the functions in the AST |
clear_functions(&mut self) | remove all functions from the AST, leaving only statements |
Extract Only Statements
The following methods allow manipulation of the statements in an AST:
| Method | Description |
|---|---|
clone_statements_only(&self) | clone the AST into a new AST with only the statements, excluding functions |
clear_statements(&mut self) | remove all statements from the AST, leaving only functions |
iter_literal_variables(&self, constants, variables) | return an iterator on all top-level literal constant and/or variable definitions in the AST |
Merge and Combine AST’s
The following methods merge one AST with another:
| Method | Description |
|---|---|
merge(&self, &ast),+ operator | append the second AST to this AST, yielding a new AST that is a combination of the two; statements are simply appended, functions in the second AST of the same name and arity override similar functions in this AST |
merge_filtered(&self, &ast, filter) | append the second AST (but only functions that pass the predicate filter) to this AST, yielding a new AST that is a combination of the two; statements are simply appended, functions in the second AST of the same name and arity override similar functions in this AST |
combine(&mut self, ast),+= operator | append the second AST to this AST; statements are simply appended, functions in the second AST of the same name and arity override similar functions in this AST |
combine_filtered(&mut self, ast, filter) | append the second AST (but only functions that pass the predicate filter) to this AST; statements are simply appended, functions in the second AST of the same name and arity override similar functions in this AST |
When statements are appended, beware that this may change the semantics of the script.
// First script
let ast1 = engine.compile(
"
fn foo(x) { 42 + x }
foo(1)
")?;
// Second script
let ast2 = engine.compile(
"
fn foo(n) { `hello${n}` }
foo("!")
")?;
// Merge them
let merged = ast1.merge(&ast2);
// Notice that using the '+' operator also works:
let merged = &ast1 + &ast2;
merged in the above example essentially contains the following script program:
fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten
foo(1) // <- notice this will be "hello1" instead of 43,
// but it is no longer the return value
foo("!") // <- returns "hello!"
Walk an AST
The internals feature allows access to internal Rhai data structures, particularly the nodes
that make up the AST.
AST node types
There are a few useful types when walking an AST:
| Type | Description |
|---|---|
ASTNode | an enum with two variants: Expr or Stmt |
Expr | an expression |
Stmt | a statement |
BinaryExpr | a sub-type containing the LHS and RHS of a binary expression |
FnCallExpr | a sub-type containing information on a function call |
CustomExpr | a sub-type containing information on a custom syntax expression |
The AST::walk method takes a callback function and recursively walks the AST in depth-first
manner, with the parent node visited before its children.
Callback function signature
The signature of the callback function takes the following form.
FnMut(&[ASTNode]) -> bool
The single argument passed to the method contains a slice of ASTNode types representing the path
from the current node to the root of the AST.
Return true to continue walking the AST, or false to terminate.
Children visit order
The order of visits to the children of each node type:
| Node type | Children visit order |
|---|---|
if statement |
|
switch statement |
|
while, do, loop statement |
|
for statement |
|
return statement | return value expression |
throw statement | exception value expression |
try … catch statement |
|
import statement | path expression |
| Array literal | each of the element expressions, in order |
| Object map literal | each of the element expressions, in order |
| Interpolated string | each of the string/expression segments, in order |
| Indexing |
|
| Field access/method call |
|
&&, ||, ?? |
|
| Function call, operator expression | each of the argument expressions, in order |
let, const statement | value expression |
| Assignment statement |
|
| Statements block | each of the statements, in order |
| Custom syntax expression | each of the inputs stream, in order |
| All others | single child (if any) |