This documentation details the creation of a generator and thereby assumes you have read how to create a parser and Traqula's expected AST structure.
Traqula provides a framework to create a generator through @traqula/core, further facilitating round tripping generation when following the assumptions we put on the AST and the correct invocation of printFilter.
A generator is constructed similarly to a parser by defining may GeneratorRules and linking them together through a builder.
A generatorRule is an object having a name and an implementation gImpl.
When you want to create both a parser and a generator, we advise you to create objects that contain both impl (from ParserRule) and gImpl (from GeneratorRule) such as done for our SPARQL parser here rules.
gImpl is a function that gets helper functions (e.g. SUBRULE) and returns a function that receives:
The helper functions exposed are:
@traqula/cores traqulaIndentation and traqulaNewlineAlternative.Using these types one can create a rule like:
import {GeneratorRule, AstCoreFactory} from "@traqula/core";
export const var_: GeneratorRule<{ astfactory: AstCoreFactory }, 'var', { value: string, child: object }> = <const> {
name: 'var',
gImpl: ({PRINT, SUBRULE}) => (ast) => {
// Print the provided string
PRINT(`?${ast.value}`);
// Example subrule call:
SUBRULE(someOtherRule, ast.child)
},
};
The most important thing about round tripping is done in the parser and while constructing the AST types. To do this correctly, read the documentation on AST structure carefully. To support round tripping from the generators side is as simple as checking whether you should actually print.
Taking the example above, making it safe for round tripping is as easy as wrapping the PRINT in printFiler.
The printFilter only executes it's provided callback in case the provided AST node is one that should print:
import {GeneratorRule, AstCoreFactory} from "@traqula/core";
export const var_: GeneratorRule<{ astfactory: AstCoreFactory }, 'var', { value: string, child: object }> = <const> {
name: 'var',
gImpl: ({PRINT, SUBRULE}) => (ast, {astFactory: F}) => {
// Print the provided string only if `loc` in `ast` says that's required.
F.printFilter(ast, () => PRINT(`?${ast.value}`));
// Example subrule call:
SUBRULE(someOtherRule, ast.child)
},
};
The actual round tripping is handled through the DynamicGenerator which is constructed by the GeneratorBuilder. It uses two function also exposed to rules, but they should rarely be called manually.
HANDLE_LOC can help generatorRules that generate many AST nodes at once without calling SUBRULE on them.
The construction of the generator works exactly the same as the construction of a parser.
import { GeneratorBuilder } from '@traqula/core';
const generatorBuilder = GeneratorBuilder.create(<const> [var_, someOtherRule]);
const myGenerator = generatorBuilder.build();
const generated = myGenerator.var_(varAst, myContext);