parserator today covers string → AST. The other direction — AST → formatted string — doesn't exist, which means we can't round-trip and can't easily prove parsers correct beyond hand-written cases.
A Wadler/Prettier-style layout algebra would fill this:
type Doc =
| { kind: "text"; text: string }
| { kind: "line"; flat: string } // softline: space when flat, newline when broken
| { kind: "group"; doc: Doc }
| { kind: "nest"; indent: number; doc: Doc }
| { kind: "concat"; docs: Doc[] }
| { kind: "fill"; docs: Doc[] }
const group = (doc: Doc): Doc => ({ kind: "group", doc })
const nest = (indent: number, doc: Doc): Doc => ({ kind: "nest", indent, doc })
const softline: Doc = { kind: "line", flat: " " }
function render(doc: Doc, width: number): string { ... }
The immediate payoff is round-trip property tests: for every example parser we already ship (json, ini, scheme, toyml), assert parse(print(ast)) produces an equal AST. That turns each example into a test oracle for free and catches regressions the current single test file can't.
Open questions:
- Should
Doc carry Spans for source-map-aware emission, or stay purely textual and layer spans on top?
- Does the monadic generator idiom (
yield*) apply to Doc the way it does to Parser, or is the algebraic constructor style enough?
- Does this live in core, or is it the first real sibling package?
Non-goals:
- Full source-map / WASM codegen — that's a separate codegen toolkit later.
- Language-specific pretty-printers — just the algebra + a generic layout engine.
Pairs with the diagnostics work: spans from #11 thread through here for source-map-aware printing.
parserator today covers
string → AST. The other direction —AST → formatted string— doesn't exist, which means we can't round-trip and can't easily prove parsers correct beyond hand-written cases.A Wadler/Prettier-style layout algebra would fill this:
The immediate payoff is round-trip property tests: for every example parser we already ship (json, ini, scheme, toyml), assert
parse(print(ast))produces an equal AST. That turns each example into a test oracle for free and catches regressions the current single test file can't.Open questions:
DoccarrySpans for source-map-aware emission, or stay purely textual and layer spans on top?yield*) apply toDocthe way it does toParser, or is the algebraic constructor style enough?Non-goals:
Pairs with the diagnostics work: spans from #11 thread through here for source-map-aware printing.