Skip to content

Add a pretty-printer / Doc algebra (parsing's inverse) #16

Description

@saiashirwad

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions