Five new guest-language plans mirroring the js-on-sx / hs-loop pattern, each with a phased roadmap (Progress log + Blockers), a self-contained agent briefing for respawning a long-lived loop, and a shared restore-all.sh that snapshots state across all seven language loops. Briefings bake in the lessons from today's stall debugging: never call sx_build (600s watchdog), only touch lib/<lang>/** + own plan file, commit every feature, update Progress log on each commit, route shared-file issues to Blockers rather than fixing them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.4 KiB
5.4 KiB
Erlang-on-SX: actors on delimited continuations
The headline showcase for the SX runtime. Erlang is built around the one primitive — lightweight processes with mailboxes and selective receive — that delimited continuations implement natively. Most Erlang implementations ship a whole VM (BEAM) for this; on SX each process is a pair of continuations and the scheduler is ~50 lines of SX.
End-state goal: spawn a million processes, run the classic ring benchmark, plus a mini gen_server OTP subset and a test corpus of ~150 programs.
Scope decisions (defaults — override by editing before we spawn)
- Syntax: Erlang/OTP 26 subset. No preprocessor, no parse transforms.
- Conformance: not BEAM-compat. "Looks like Erlang, runs like Erlang, not byte-compatible." We care about semantics, not BEAM bug-for-bug.
- Test corpus: custom — ring, ping-pong, fibonacci-server, bank-account-server, echo-server, plus ~100 hand-written tests for patterns/guards/BIFs. No ISO Common Test.
- Binaries: basic bytes-lists only; full binary pattern matching deferred.
- Hot code reload, distribution, NIFs: out of scope entirely.
Ground rules
- Scope: only touch
lib/erlang/**andplans/erlang-on-sx.md. Don't editspec/,hosts/,shared/,lib/js/**,lib/hyperscript/**,lib/lua/**,lib/prolog/**,lib/forth/**,lib/haskell/**,lib/stdlib.sx, orlib/root. Erlang primitives go inlib/erlang/runtime.sx. - SX files: use
sx-treeMCP tools only. - Commits: one feature per commit. Keep
## Progress logupdated and tick roadmap boxes.
Architecture sketch
Erlang source
│
▼
lib/erlang/tokenizer.sx — atoms, vars, tuples, lists, binaries, operators
│
▼
lib/erlang/parser.sx — AST: modules, functions with clauses, patterns, guards
│
▼
lib/erlang/transpile.sx — AST → SX AST (entry: erlang-eval-ast)
│
▼
lib/erlang/runtime.sx — scheduler, processes, mailboxes, BIFs
Core mapping:
- Process = pair of delimited continuations (
on-receive,on-resume) + mailbox list + pid + links - Scheduler = round-robin list of runnable processes; cooperative yield on
receive spawn= push a new process record, return its pidsend= append to target mailbox; if target is blocked on receive, resume its continuationreceive= selective — scan mailbox for first matching clause; if none,performa suspend with the receive pattern; scheduler resumes when a matching message arrives- Pattern matching = SX
caseon tagged values; vars bind on match - Guards = side-effect-free predicate evaluated after unification
- Immutable data = native
- Links / monitors / exit signals = additional process-record fields, scheduler fires exit signals on death
Roadmap
Phase 1 — tokenizer + parser
- Tokenizer: atoms (bare + single-quoted), variables (Uppercase/
_-prefixed), numbers (int, float,16#HEX), strings"...", chars$c, punct( ) { } [ ] , ; . : :: -> - Parser: module declarations,
-module/-export/-importattributes, function clauses with head patterns + guards + body - Expressions: literals, vars, calls, tuples
{...}, lists[...|...], binaries<<...>>,if,case,receive,fun,try/catch, operators - Unit tests in
lib/erlang/tests/parse.sx
Phase 2 — sequential eval + pattern matching + BIFs
erlang-eval-ast: evaluate sequential expressions- Pattern matching (atoms, numbers, vars, tuples, lists,
[H|T], underscore, bound-var re-match) - Guards:
is_integer,is_atom,is_list,is_tuple, comparisons, arithmetic - BIFs:
length/1,hd/1,tl/1,element/2,tuple_size/1,atom_to_list/1,list_to_atom/1,lists:map/2,lists:foldl/3,lists:reverse/1,io:format/1-2 - 30+ tests in
lib/erlang/tests/eval.sx
Phase 3 — processes + mailboxes + receive (THE SHOWCASE)
- Scheduler in
runtime.sx: runnable queue, pid counter, per-process state record spawn/1,spawn/3,self/0!(send),receive ... endwith selective pattern matchingreceive ... after Ms -> ...timeout clause (use SX timer primitive)exit/1, basic process termination- Classic programs in
lib/erlang/tests/programs/:ring.erl— N processes in a ring, pass a token around M timesping_pong.erl— two processes exchanging messagesbank.erl— account server (deposit/withdraw/balance)echo.erl— minimal serverfib_server.erl— compute fib on request
lib/erlang/conformance.sh+ runner,scoreboard.json+scoreboard.md- Target: 5/5 classic programs + 1M-process ring benchmark runs
Phase 4 — links, monitors, exit signals
link/1,unlink/1,monitor/2,demonitor/1- Exit-signal propagation; trap_exit flag
try/catch/of/end
Phase 5 — modules + OTP-lite
-module(M).loading,M:F(...)calls across modulesgen_serverbehaviour (the big OTP win)supervisor(simple one-for-one)- Registered processes:
register/2,whereis/1
Phase 6 — the rest
- List comprehensions
[X*2 || X <- L] - Binary pattern matching
<<A:8, B:16>> - ETS-lite (in-memory tables via SX dicts)
- More BIFs — target 200+ test corpus green
Progress log
Newest first.
- (not started)
Blockers
- (none yet)