12. Adding a new target languageΒΆ
wrenfold can be extended to target new languages by customizing the code generation step. For a complete demonstration, refer to the c_generation example. This section provides a brief overview.
To begin with, one should subclass the wrenfold.code_generation.BaseGenerator type in python
and implement format_<type name> methods for each of the
syntax tree types.
Most nodes in the syntax tree will have children - for example wrenfold.ast.Add has a
member args that enumerates the operands of an N-ary addition. To facilitate recursive
formatting, the BaseGenerator type provides an overloaded
wrenfold.code_generation.BaseGenerator.format() method that automatically delegates to the
appropriately named formatter on your subclass.
For example, when printing ast.Add we can recurse on add.args:
from wrenfold import ast
from wrenfold.code_generation import BaseGenerator
class CustomGenerator(BaseGenerator):
    """A generator for a new language."""
    def format_add(self, add: ast.Add) -> str:
        # Calling `format(...)` will automatically delegate to the appropriate overload.
        # If the argument is an `ast.Add`, it will recurse back into this method.
        return ' + '.join(self.format(x) for x in add.args)
    # ... more overloads ...
    def format_compare(self, compare: ast.Compare) -> str:
        if compare.operation == RelationalOperation.LessThan:
            op = '<'
        elif compare.operation == RelationalOperation.LessThanOrEqual:
            op = '<='
        elif compare.operation == RelationalOperation.Equal:
            op = '=='
        else:
            raise NotImplementedError(f"Unknown operation: {compare.operation}")
        return f'{self.format(compare.left)} {op} {self.format(compare.right)}'
Note
There is no hard requirement that you use the BaseGenerator infrastructure to implement
code-generation. wrenfold.code_generation.transpile() directly returns the top-level
FunctionDefinition object, which can be traversed by alternative means if desired.
With a BaseGenerator subclass in hand, we can generate code:
def some_symbolic_func(x: type_annotations.FloatScalar):
    return x * 2
# Here we replace the built-in generator with `CustomGenerator`:
code = code_generation.generate_function(some_symbolic_func, generator=CustomGenerator())