Skip to content

Make puffscript self-hosting - #1

Open
andrewkchan wants to merge 7 commits into
mainfrom
cursor/self-hosting-puffscript-498e
Open

Make puffscript self-hosting#1
andrewkchan wants to merge 7 commits into
mainfrom
cursor/self-hosting-puffscript-498e

Conversation

@andrewkchan

@andrewkchan andrewkchan commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Summary

Puffscript is now self-hosting: selfhost/ contains a complete puffscript compiler written in puffscript (~5,600 lines) that compiles its own source and reaches a bootstrap fixpoint. Its WAT output is byte-identical to the TypeScript reference compiler's on every tested input, and its error messages match too.

npm install
npm test           # 101 tests, including bootstrap fixpoint verification
npm run bootstrap  # stage1 -> stage2 -> stage3 fixpoint check

Language extensions (TypeScript reference compiler)

  • import def name(params) type; — host functions imported from WASM module env; the CLI runner provides getchar/putchar/puterr/exit wired to stdin/stdout/stderr (file I/O)
  • export def — export functions from the compiled module
  • int↔pointer casts and pointer-to-struct cast syntax (Node~(p)) — enables user-space heap allocators
  • __heap_end__() / __grow_heap__(n) builtins (wrap memory.size / memory.grow) — dynamic memory
  • address-of on struct members and dereferences (&p~.member)
  • __memcpy__ builtin declaration fixed to match its 3-arg WASM implementation
  • float literals are emitted from source lexemes (host-float-formatting independent), the in-memory stack region grew from 512KB to 4MB, WAT debug comments are off, and a latent infinite recursion in eager debug-comment generation on self-referential struct pointer types is fixed

Self-hosted compiler (selfhost/)

Faithful port of the reference pipeline: util.puff (bump allocator over grown WASM memory, vectors, buffers, strings), scanner.puff, ast.puff, sexpr.puff, parser.puff (panic-flag error recovery mirroring exception unwinding), resolver.puff (out-of-order global resolution, cycle detection, liveness), backend.puff, main.puff (stdin→WAT-on-stdout driver).

Verification

  • All prior tests pass; new tests cover the added language features
  • Differential testing: token streams, AST s-expressions, error output + exit codes, and WAT output all match the reference compiler over a fixture corpus extracted from test.ts (tools/extract-fixtures.ts, tools/compare-selfhost.sh)
  • test.ts self-hosting suite:
    • bootstrap fixpoint: stage1 (compiled by TS) compiles its own source byte-identically to the TS compiler; stage2 (self-compiled) reproduces the same output
    • self-hosted compiler output equivalence and error-message equivalence on sample programs
    • a program compiled by the self-hosted compiler executes with correct output

Tooling

tools/puffc.ts (reference CLI), tools/run.ts (WASM runner with host env), tools/wat2wasm.ts, tools/dump.ts (debug dumps), tools/build-selfhost.sh, tools/bootstrap.sh (npm run bootstrap).

Open in Web Open in Cursor 

cursoragent and others added 7 commits August 13, 2026 06:57
…nter casts, heap builtins

- 'import def name(params) type;' declares a host function imported from WASM module 'env'
- 'export def' exports a function from the compiled module
- int<->pointer casts allow user code to implement heap allocators
- pointer-to-struct cast syntax: 'Token~(expr)'
- __heap_end__/__grow_heap__ builtins wrap memory.size/memory.grow
- address-of now supports struct members and dereferences (&p~.member)
- CLI tools: puffc (compile), wat2wasm (assemble), run (execute with env imports)
- turn off WAT debug comments to keep output manageable for large programs

Co-authored-by: Andrew Chan <andrewkchan@users.noreply.github.com>
… 4MB

Both changes make WAT output reproducible by a self-hosted compiler:
- float constants no longer depend on JS shortest-round-trip double formatting
- the larger in-memory stack accommodates compiler workloads (string temporaries)

Co-authored-by: Andrew Chan <andrewkchan@users.noreply.github.com>
- selfhost/util.puff: bump allocator over grown WASM memory, Vec/Buf/Str helpers
- selfhost/scanner.puff: token definitions and scanner, differentially tested
  against the reference scanner (token streams and error messages match)
- fix __memcpy__ builtin declaration to match its 3-arg WASM implementation
- tools: dump.ts (reference-compiler debug dumps), build-selfhost.sh

Co-authored-by: Andrew Chan <andrewkchan@users.noreply.github.com>
Differentially tested against the reference parser: token streams,
s-expressions, parse errors, and exit codes match over the fixture corpus
(extracted from test.ts) and the selfhost sources themselves.

Also fixes a latent reference-compiler bug where emitDebugComments computed
s-expressions eagerly (infinite recursion on self-referential struct pointer
types) even with debug comments disabled.

Co-authored-by: Andrew Chan <andrewkchan@users.noreply.github.com>
Full port of type resolution/checking including out-of-order global
resolution, symbol cycle detection, liveness analysis, and coercions.
Error output matches the reference resolver over all 116 fixtures
(parser/resolver error fixtures, e2e programs, and selfhost sources).

Co-authored-by: Andrew Chan <andrewkchan@users.noreply.github.com>
The puffscript compiler written in puffscript (selfhost/) now:
- emits WAT byte-identical to the reference compiler on all fixtures
- compiles its own source byte-identically to the reference compiler
- reaches a bootstrap fixpoint: stage2 (self-compiled) output == stage1 output

Co-authored-by: Andrew Chan <andrewkchan@users.noreply.github.com>
- test.ts: bootstrap fixpoint test (stage1==stage2==stage3), output-equivalence
  tests vs the reference compiler, and an end-to-end run of a program compiled
  by the self-hosted compiler
- tools/bootstrap.sh + 'npm run bootstrap'
- README: self-hosting instructions, import/export, pointer casts, heap builtins
- regenerate demo.js bundle

Co-authored-by: Andrew Chan <andrewkchan@users.noreply.github.com>
@andrewkchan
andrewkchan marked this pull request as ready for review August 13, 2026 07:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants