Modeling Language Implementation
Parsing @gen
functions
Gen's built-in modeling languages are designed to preserve Julia's syntax as far as possible, apart from the Tilde syntax for calling generative functions, and the restrictions imposed on the Static Modeling Language. In order to preserve that syntax, including the use of non-Gen macros within @gen
functions, we relegate as much of the parsing of @gen
functions as possible to Julia's macro-expander and parser.
In particular, we adopt an implementation strategy that enforces a separation between the surface syntax associated with Gen-specific macros (i.e., @trace
and @param
) and their corresponding implementations, which differ across the Dynamic Modeling Language (DML) and the Static Modeling Language (SML). We do this by introducing the custom expressions Expr(:gentrace, call, addr)
and Expr(:genparam, name, type)
, which serve as intermediate representations in the macro-expanded abstract syntax tree.
Each modeling language can then handle these custom expressions in their own manner, either by parsing them to nodes in the Static Computation Graph (for the SML), or by substituting them with their implementations (for the DML). This effectively allows the SML and DML to have separate implementations of @trace
and @param
.
For clarity, below is a procedural description of how the @gen
macro processes Julia function syntax:
macroexpand
the entire function body with respect to the calling module. This expands any (properly-scoped)@trace
calls toExpr(:gentrace, ...)
expressions, and any (properly-scoped)@param
calls toExpr(:genparam, ...)
expressions, while also expanding non-Gen macros.- Desugar any tilde expressions
x ~ gen_fn()
, including those that may have been generated by macros, toExpr(:gentrace, ...)
expressions. - Pass the macro-expanded and de-sugared function body on to
make_static_gen_function
ormake_dynamic_gen_function
accordingly. - For static
@gen
functions, match:gentrace
expressions when adding address nodes to the static computation graph, and match:genparam
expressions when adding parameter nodes to the static computation graph. AStaticIRGenerativeFunction
is then compiled from the static computation graph. - For dynamic
@gen
functions, rewrite any:gentrace
expression with its implementationdynamic_trace_impl
, and rewrite any:genparam
expression with its implementationdynamic_param_impl
. The rewritten syntax tree is then evaluated as a standard Julia function, which serves as the implementation of the constructedDynamicDSLFunction
.