Modifying a generator follows the same builder pattern as modifying a parser,
but using GeneratorBuilder and GeneratorRule from @traqula/core.
As with all builders, start by creating a copy of the generator builder you're extending:
import { GeneratorBuilder } from '@traqula/core';
const myGeneratorBuilder = GeneratorBuilder.create(existingGeneratorBuilder);
The GeneratorBuilder exposes the same manipulation methods as the ParserBuilder:
getRule(name): Retrieve an existing generator rule by name for inspection or wrapping.patchRule(rule): Replace the implementation of an existing rule.addRule(rule): Add a new generator rule.addMany(rule1, rule2, ...): Add multiple rules at once (see guidelines).deleteRule(name) / deleteMany(...names): Remove rules by name.typePatch<{...}>(): Update type signatures without changing implementations.widenContext(): Narrow the context type parameter to a more specific subtype.merge(otherBuilder, overrides): Merge with another GeneratorBuilder.Suppose you've extended a parser with a new path rule that returns a narrower type.
The corresponding generator rule needs to handle this changed AST structure:
import type { GeneratorRule } from '@traqula/core';
// Retrieve the original to understand its signature
const originalPath = existingGeneratorBuilder.getRule('path');
// Create a replacement that handles the new AST shape
const patchedPath: GeneratorRule<MyContext, 'path', MyNewPathType> = <const> {
name: 'path',
gImpl: ({ PRINT, SUBRULE }) => (ast, context) => {
context.astFactory.printFilter(ast, () => {
// Generate using the new path structure
PRINT(ast.value);
});
},
};
const myGeneratorBuilder = GeneratorBuilder.create(existingGeneratorBuilder)
.patchRule(patchedPath);
When your extended grammar introduces new AST node types, add corresponding generator rules:
const myNewNodeGen: GeneratorRule<MyContext, 'myNewNode', MyNewNodeAST> = <const> {
name: 'myNewNode',
gImpl: ({ PRINT, SUBRULE, PRINT_WORD }) => (ast, { astFactory: F }) => {
F.printFilter(ast, () => {
PRINT_WORD('MY_KEYWORD');
SUBRULE(someExistingRule, ast.child);
});
},
};
const myGeneratorBuilder = GeneratorBuilder.create(existingGeneratorBuilder)
.addRule(myNewNodeGen);
When modifying generator rules, keep these constraints in mind to preserve round-tripping support:
printFilter: Always wrap your PRINT calls with astFactory.printFilter(ast, () => ...) to respect the node's source location. This ensures round-tripping works correctly when the node is sourced from the original string rather than auto-generated.For more details on round-tripping, see the generator creation docs and the AST structure docs.