waxwing

Compiler architecture

Five layers, each named for what would make it change (ADR 004, design in docs/plans/2026-10-07-five-layers-design.md). Imports run only to the same or an earlier layer; scripts/structure.mjs enforces it from purs graph.

Layer Modules (src/) Role
Domain Syntax, Syntax.Core, Type, Type.{Core,Compare,Parts,Marks,Record}, Resolved, Problem, Host, Checked.Internal, IR.Internal syntax trees, spans, Ty v, resolved syntax, checked IR, monomorphic IR, Problem data, capability-port types
Features Resolve, Resolve.{Expression,Types,TypeRef,TypeArguments,Occurrences,Sorts,RowSettle,SortCheck,SortConflict,EffectSorts,RecordFields,Record,Pattern,Repeated,Fresh,Lambda,Name,Builtin,Qualified,Imports,Unbound,Scope}, Check, Check.{TypeText,Infer,Call,Apply,Lambda,Pipe,Use,Context,Hint,Functional,Arms,Match,Require,Scheme,Walk,Comparable,Instantiation,Nested,Components,Unify,UnifyParts,UnifyFailure,UnifyRecord,FlatRecord,RecordTail,Lacks,Implied,Seed,Record,Compose,Tables,Inhabited,Signature,Matrix,Usefulness,Missing,Coverage,Search,MarkStore,UnifyMark,MarkUse,RowFinish,ResultClauses,Walked,Settle,SettleEntry,Feed,FeedOrder,Cycle,Restricted,Watch,TailWatch,Handle,Sync,Filing,Groups,SyncCycle,Clauses,ClauseBody,TailPass,Install,Lend,Frames,Mark,MarkReport,MarkNotes,Site,Strip}, Specialize, Specialize.{Seeds,Keys,State,Lower,Body,Copy,Intern,Values,Ground,Printable,Layouts,Record} resolution, checking, coverage, specialization; report Problem data, never text
Format Lex, Lex.{Token,Scan,Text,Origin,Escape,Chars,Run,Walk,Block,Dedent}, Parse, Parse.*, Stack, Go, Go.{Layout,Data,Lowered,Expression,Match,Capture,Value,Apply,Lambda,Pipe,Stage,Entry,Compare,Show,TextShow,Usage,Families,Record,RecordNames,RecordHelpers,RecordUse}, Diagnostic, Diagnostic.{Code,Qualify}, Wire, Arguments text in (tokens, parser, reserved words, uppercase rule); Go out; diagnostic text, E_* names, wire records, usage text
Runtime Node (+ Node.js) port implementations, argv/stdout/stderr/exit, JSON; the only FFI
Program Compile, Command, Main pure compile, commands over any Host, entry point

Domain, Features and Format are pure (core library allowlist, no Effect).

Pipeline

String -> Lex/Parse -> Syntax -> Resolve -> Resolved
       -> Check: typing per function (unification, comparison groundness,
          inferred-type depth bound) -> checked IR
          -> instantiation rule -> coverage
       -> Specialize -> monomorphic IR -> Go text

Every arrow is Either Diagnostic. Phase list (P001 final; ADR 007): Parse, Resolve, Check (typing, instantiation rule, coverage), Specialize, Go.

Program.Compile composes the phases; Program.Command runs emit/build/run over the Domain.Host ports (records over an abstract monad), so test/program.test.mjs substitutes fakes. Expected source errors are Either Diagnostic; the first error in phase/traversal order wins. Host failures (IoFailure, ToolFailure) are separate values rendered by Format.Wire.

Phases

Only Features.Check* and Features.Specialize* import Domain.Checked.Internal, and only Features.Specialize* and Format.Go* import Domain.IR.Internal. These are enforced project boundaries, not PureScript privacy.

Verified Go properties

Names are mangled from IDs; no source spelling, timestamp or absolute path enters output. Emission is byte-deterministic (bootstrap/answer.go, bootstrap/shapes.go, bootstrap/tree.go snapshots). Int is int32 with wrapping addition. Generated code never dereferences nil unguarded (ADR 003). Generated Go is canonical emitter output; gofmt copies are presentation only.

Lexing and declaration parsing are index-based tailRecM loops (Control.Monad.Rec.Class is allowlisted), so long declaration sequences and whitespace do not overflow the stack. Since G001, long comma, constructor and match-arm lists inside one declaration do not either, and nesting deeper than 128 levels is E_NESTING (ADR 006). Every later phase still recurses over the tree, which is why the limit exists (heap-based phases are BACKLOG H001; iterative operator chains O001). Format.Go computes a constructor layout (Format.Go.Layout) once, so Go emission never searches the type tables per constructor.

Future stages (proposed, not implemented)

Syntax -> Resolved -> Kinded -> Checked constraints -> coverage/instances -> Elaborated IR -> lowered Go IR -> emitted Go. A target-neutral Features.Lower is deferred until decision-tree match compilation gives it a target-neutral job (BACKLOG A002). Foreign values (FFI) need full-value validation of tag and payload consistency before they become closed sums (I001). No fake stages for absent features. ADR 002 set the polymorphism strategy; ADR 007 records the implemented specialization; ADR 008 the function values and their staged lowering; ADR 010 the effect rows, handlers, cleanup and their erased lowering; ADR 013 the records, whose rows enter specialization keys.

Known leak

Diagnostic offsets are UTF-16 code units, a hosting artifact of JavaScript strings (ADR 004).