Traqula docs
    Preparing search index...

    Modify Generator

    Modifying a generator follows the same builder pattern as modifying a parser, but using GeneratorBuilder and GeneratorRule from @traqula/core.

    Note

    Minor versions of Traqula keep the name, signature and behavior of existing rules, but can add rules or change how existing rules are implemented, such as which rules they call. This only affects projects that patch, delete or individually register rules. See versioning.

    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:

    1. Preserve SUBRULE generation order: Generate subnodes in the same order they appear in the source string. If node A appears before node B in the source, generate A before B.
    2. Preserve range nesting: Descendant nodes must represent subranges of their parent's range.
    3. Use 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.