Expand description
This module provides functionality related to building user defined operations.
The main functionality is provided by the OperationBuilder type.
This should be used as the primary backend used by frontends that want to allow end-users to create their own operations.
The main method of communication is through atomic “instructions” sent to the builder.
These are flat instructions: any nesting in the resulting user defined operation is a result
of explicit “nesting” instructions, such as OperationBuilder::start_query.
You can think of these instructions as the HIR (high-level intermediate representation) of grabapl, which the builder compiles into bytecode (i.e., the final user defined operation) for the interpreter.
See the OperationBuilder documentation for the available instructions.
§Example
Assume we want to build a text-based frontend that allows users to create their own operations.
We may want to support syntax such as the following:
fn mark_children_as_visited(parent: int) {
if shape [child: int, parent -> child: *] {
mark_node<"visited">(child);
// we found a child, hence we should recurse to find more children
mark_children_as_visited(parent);
}
}If we leverage this builder, all our frontend would need to do in order to get a finished user defined operation, is turn the above syntax example into the following sequence of instructions:
expect_parameter_node("parent", NodeType::Int)- the parameter definitionstart_shape_query("<generated name>")- the start of the shape queryexpect_shape_node("child", NodeType::Int)- the shape query expects a child node of type intexpect_shape_edge("parent", "child", EdgeType::Wildcard)- the shape query expects an edge from parent to childenter_true_branch()- we enter the true branch of the shape query- Note how this is a flat instruction: We don’t pass the entire true branch as argument to the method. Instead, we change the context to indicate the following instructions are part of the true branch.
add_operation(LibBuiltinOperation::MarkNode("visited"), vec!["child"])- we add an operation that marks the child node as visitedadd_operation(Recurse, vec!["parent"])- we add an operation that recurses to find more childrenend_query()- we end the shape query
After sending these instructions to the builder we can call OperationBuilder::build()
to get the final user defined operation that can then be added to a OperationContext and
executed by the interpreter via run_from_concrete.
§Example Frontends
See grabapl_syntax for a text-based syntax
frontend that compiles parsed ASTs into instructions for this builder.
This implements our example from above.
See example_clients/simple_semantics/{simple_semantics_ffi, www} for a basic visual editor that
uses commands from the user to convert into instructions for this builder, and takes the builder’s
intermediate state to give visual feedback to the user.