AST Math IR · simply · 22 Apr 2026

One expression. Nine runtimes.

MathIR takes a single mathematical expression, parses it into an abstract-syntax tree, and emits idiomatic code in nine target languages. Change the tree — all nine outputs update in lockstep.

The problem

Every pricing library eventually faces the same issue: the math needs to run in several places at once. A C# server. A Rust core. A JavaScript harness in the browser. A CUDA kernel. An Excel LAMBDA on a trader's desk. Hand-writing each version is the obvious path and the wrong one — every bug has to be caught nine times, every refinement applied nine times, and the nine implementations drift apart the moment anyone is tired on a Friday afternoon.

The escape hatch is to express the math once as a data structure, then let a family of emitters generate each target language from that single source. That data structure is an abstract-syntax tree, and in this project it's called MathIR.

A worked example

Take the first equation everyone learns in physics — distance under constant acceleration:

s = u·t + ½ a t2

Where s is distance travelled, u is initial velocity, t is time, and a is (constant) acceleration. Four variables and a square. Enough structure to show real tree depth without being intimidating.

Parsed as a tree

MathIR parses that single expression into a tree of Binary (operator), Var (variable), and LitF64 (literal) nodes. The square t2 is represented as t * t — the IR does not ship a dedicated Pow node because every target language inlines small integer powers as repeated multiplication anyway.

AST for s = u*t + 0.5*a*t*t Abstract-syntax tree with five levels. Root is an Add of two Mul subtrees. The left subtree is u times t. The right subtree is 0.5 times a times t times t, right-associated. + Add × Mul × Mul u Var t Var 0.5 Lit × Mul a Var × Mul t Var t Var
Operator — Binary("+", "*") Variable — Var("u") Literal — LitF64(0.5)

Read it top-down: the root is the + at the top combining two products. The left product is simply u * t. The right product is 0.5 * (a * (t * t)) — the outer multiplier 0.5, then the acceleration, then t squared written explicitly as t * t.

That's the entire structure. No hidden state, no implicit coercions, no operator-precedence magic — the tree is the precedence.

Nine emissions, same tree

Feed that AST through nine emitters and out come nine idiomatic functions. Each one produces what a competent developer in that language would write by hand — no LLM signatures, no foreign syntax tells, no redundant parentheses. Use the language pill above to pick one; the matching card lifts so it's easy to compare against the tree.

Rust icon
Rust
pub fn suvat(u: f64, t: f64, a: f64) -> f64 {
    u * t + 0.5 * a * t * t
}
C++ icon
C++
double suvat(double u, double t, double a) {
    return u * t + 0.5 * a * t * t;
}
C# icon
C#
public static double Suvat(
    double u, double t, double a)
    => u * t + 0.5 * a * t * t;
Go icon
Go
func suvat(u, t, a float64) float64 {
    return u*t + 0.5*a*t*t
}
JavaScript icon
JavaScript
function suvat(u, t, a) {
    return u * t + 0.5 * a * t * t;
}
TypeScript icon
TypeScript
function suvat(
    u: number, t: number, a: number,
): number {
    return u * t + 0.5 * a * t * t;
}
Python icon
Python
def suvat(u: float, t: float, a: float) -> float:
    return u * t + 0.5 * a * t * t
CUDA icon
CUDA
__device__ double suvat(
    double u, double t, double a) {
    return u * t + 0.5 * a * t * t;
}
Excel LAMBDA icon
Excel LAMBDA
SUVAT = LAMBDA(u, t, a,
    u*t + 0.5*a*t*t)

That's it

Every snippet above was generated by an EmitterLang class walking the same AST. No snippet was hand-written; no snippet was hand-edited. If the source expression changes — a new coefficient, a new variable, a different operator tree — all nine files regenerate together, and any downstream test that compares the lanes catches discrepancies automatically.

The reflexlibs.ai pricing catalogue — Black-76, SABR, Heston Normal — is built the same way. A harder tree, but the same machinery: one author writes the math; a family of emitters produces the production code; a triumvirate harness runs the same vectors through every lane and demands agreement to 1e-12.

SUVAT is the smallest possible version of that story. Pricing Greeks are just a deeper tree.