Γοητεία: The black art of commanding what lies beneath.
A self-hosting Scheme for the WebAssembly GC era.
goeteia.wasm is Goeteia compiled by itself: it reads
Scheme and writes WebAssembly, right here — no server, no JS
compiler. The value is the last expression; Ctrl/⌘-Enter runs.
The compiler is written in the Scheme subset it compiles. The self-hosted build recompiles itself and the output is byte-identical — the fixpoint is checked in CI fashion on every change, and every test runs through both stages.
Fixnums are unboxed i31refs, pairs and records
are GC structs, eq? is one ref.eq.
No shadow heap in JavaScript: the host supplies two byte-stream
imports and nothing else.
syntax-rules and procedural
syntax-case with fenders, nested ellipses and
datum->syntax, running in a compile-time
interpreter with hygiene by renaming.
Typed function references with a fast per-arity entry and a
generic entry per closure — variadic procedures and
apply are cheap, and every tail call is a
return_call. A 100M-iteration loop runs in
constant stack, in ~150ms.
Fixnums promote to bignums on overflow (checked inline, two bit-compares), flonums have contagion, and float literals are encoded to IEEE-754 by pure Scheme — so both hosts of the compiler emit identical bytes.
Escape continuations ride the Wasm exception-handling proposal: capture is O(1), the normal path costs one try block, and winders unwind inner-to-outer on the way out.
define-record-type compiles to GC structs with
an identity slot — a record predicate is one type test plus one
ref.eq. Hashtables are pure Scheme over vectors,
and dead code elimination keeps programs lean.
R6RS-style (library ...) files with
(import (math utils)) resolution, dependencies
first; exports are advisory because unused code is pruned
anyway.
$ git clone https://github.com/guenchi/Goeteia
$ cd Goeteia
$ ./run-tests.sh # every test, both compiler stages
$ ./build-self.sh # rebuild the compiler with itself
$ echo '(define (fact n) (if (zero? n) 1 (* n (fact (- n 1)))))
(fact 20)' > fact.ss
$ node rt/compile.mjs goeteia.wasm fact.ss fact.wasm
$ node rt/run.mjs fact.wasm
2432902008176640000
Compiled modules run on any engine with Wasm GC and tail calls: Node 22+, current Chrome / Firefox / Safari, wasmtime. Bootstrapping from source needs Chez Scheme; the checked-in compiler wasm works without it.