Erlang has left the side door open
I had imagined code generation as a machine writing another program on a very long receipt.
Erlang offers a more useful arrangement. Its compiler accepts abstract forms: ordinary Erlang terms describing the structure of a module. Pooka can construct those terms directly.
A function, unpacked
For a tiny Pooka function:
answer() -> Int {
42
}
The central function form looks like this:
{function, 1, answer, 0,
[{clause, 1, [], [], [{integer, 1, 42}]}]}
This is a small illustration of the shape, with line annotations simplified. A complete module also needs attributes such as its module name and exports. Pooka’s generated module names have their own package-aware naming scheme.
Pass a tree, not a receipt
The important change is that the output is structured data. There is no second round of quoting brackets and hoping an Erlang source parser will discover the same intentions.
Erlang’s abstract-format reference is the map for these terms. A function contains clauses; a clause has patterns, guards and a body. The compiler’s job is to fill that map with the meaning established by Pooka’s earlier phases.
The checked tree matters. Code generation should not have to ask whether an operator’s arguments are sensible or which declaration a name refers to. Those decisions should arrive with the program, already made.
What belongs in the build directory
This also explains a slightly odd build directory. An .abstr artifact need not be a readable Erlang text file. Pooka keeps its abstract forms as binary data for its own build machinery. Ordinary compilation does not need to leave generated .erl files behind.
Erlang then performs its part of the work and produces BEAM code.
The side door was open. It led to a compiler that already knew the building.
— Peregrine Finch