Transformers¶
Transformers are optional components used to modify the output trees generated by the generator before they are serialized. They allow for various modifications to be performed, such as semantic improvements, variable resolution, checksum generation, and more. Multiple transformers can be defined, and they are typically used to enhance the generated output.
A transformer is implemented as a function that traverses the tree and performs the necessary operations on the nodes. The function takes a node parameter, which represents the root of the tree being processed, and it returns the modified root as the result.
The following snippet shows an example transformer that normalizes function
names in trees generated by an hypothetical code generator. In this tree,
funcName nodes represent function declarations, while funcRef nodes
should refer to existing function names.
import random
def func_name_transformer(root):
def _walk(node):
nonlocal idx
if node.name == 'funcName':
node.src = f'func_{idx}'
idx += 1
elif node.name == 'funcRef':
node.src = f'func_{random.randint(0, idx)}'
for child in node.children:
_walk(child)
idx = 0
_walk(root)
return root
Transformers can be applied in two scenarios:
When creating new test cases using grammarinator-generate, transformers can be used to postprocess the generated output.
When creating an initial population using grammarinator-parse, transformers can be used to postprocess the items of the seed.
Both scripts expect transformers to be specified using the --transformer
argument in the form of a fully qualified name.
When using Grammarinator from the API, transformers can be defined in the
constructor of grammarinator.tool.GeneratorTool or
grammarinator.tool.ParserTool.