a compiler-compiler for syntax you write
Compiler-compiler is literal: write a reader — one function from source text to AST — and lang wraps it around a shared compiler kernel. You provide syntax and lowering. lang provides the type system and native/wasm backends. The result is a compiler for your language.
C, Lisp, flow, Forth, and minipy ship as source you can steal, not a closed language list. The polyglot demo shows reader boundaries disappear in one wasm module — no VM, FFI, or glue.
write a reader in your browser →
These are the last four lines of the C reader. This is all the kernel sees:
reader c(text *u8) *u8 { var t *Tokenizer = tok_new(text); var prog *PNode = parse_c_program(t); return emit_program(prog); }
When lang sees #c{ ... }, it runs this reader at compile time
and splices in the AST. After that, C is just code. The kernel never needs
to know C exists.
The reader guide starts with 20 lines. The small calculator adds a real expression grammar. Then steal the closest shipped example.
answer 42 becomes a native program. Copy this first.
A tiny recursive-descent parser for arithmetic. Grow into this second.
A substantial C subset. Start here for a conventional grammar and desugaring pass.
Closures, lists, quote, and a boxed runtime. Start here when your language needs its own value model.
Generators lower to algebraic effects. Start here for a focused DSL. Native-only: core wasm has no stack switching.
The reader turns a compile-time data stack into expression trees. Start here when your syntax looks nothing like the AST.
Its reader reconstructs INDENT/DEDENT from token offsets. Start here for layout or custom lexing.
Reuse lang’s tokenizer, #parser{}, and AST builders, or
replace any of them. Then wrap your reader around the kernel:
$ ./out/lang compiler mine my_reader.lang -o mine
mine is now a standalone compiler for whole files in your
syntax. lang itself is built the same way: kernel + lang reader, verified to
a fixed point.