Hyperscript compiler/runtime:
- query target support in set/fire/put commands
- hs-set-prolog-hook! / hs-prolog-hook / hs-prolog in runtime
- runtime log-capture cleanup
Scripts: sx-loops-up/down, sx-hs-e-up/down, sx-primitives-down
Plans: datalog, elixir, elm, go, koka, minikanren, ocaml, hs-bucket-f,
designs (breakpoint, null-safety, step-limit, tell, cookies, eval,
plugin-system)
lib/prolog/hs-bridge.sx: initial hook-based bridge draft
lib/common-lisp/tests/runtime.sx: CL runtime tests
WASM: regenerate sx_browser.bc.js from updated hs sources
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.5 KiB
7.5 KiB
Go-on-SX: Go on the CEK/VM
Compile Go source to SX AST; the existing CEK evaluator runs it. The unique angle: Go's
goroutines and channels map cleanly onto SX's IO suspension machinery (perform/cek-resume)
— a goroutine is a cek-step-loop running in a cooperative scheduler, a channel send/receive
is a perform that suspends until the other end is ready.
End-state goal: core Go programs running, including goroutines, channels, defer/panic/recover, interfaces, and structs. Not a full Go compiler — no generics, no CGo, no full stdlib — but a faithful runtime for idiomatic Go concurrent programs.
Ground rules
- Scope: only touch
lib/go/**andplans/go-on-sx.md. Do not editspec/,hosts/,shared/, or otherlib/<lang>/. - Shared-file issues go under "Blockers" below with a minimal repro; do not fix here.
- SX files: use
sx-treeMCP tools only. - Architecture: Go source → Go AST → SX AST. No standalone Go evaluator.
- Concurrency model: cooperative, not preemptive. Goroutines yield at channel ops and
time.Sleep. A round-robin scheduler in SX drives them. - Commits: one feature per commit. Keep
## Progress logupdated and tick boxes.
Architecture sketch
Go source text
│
▼
lib/go/tokenizer.sx — Go tokens: keywords, idents, string/rune/number literals,
│ operators, semicolon insertion rules
▼
lib/go/parser.sx — Go AST: package, import, var, const, type, func, struct,
│ interface, goroutine, channel ops, defer, select, for range
▼
lib/go/transpile.sx — Go AST → SX AST
│
▼
lib/go/runtime.sx — goroutine scheduler, channel primitives, defer stack,
│ panic/recover, interface dispatch, slice/map ops
▼
CEK / VM
Key semantic mappings:
go fn()→ spawn new coroutine (SX coroutine primitive, Phase 4 of primitives)ch <- v(send) →performthat suspends until receiver ready; scheduler picks next goroutinev := <-ch(receive) →performthat suspends until sender readyselect { case ... }→ scheduler checks all channel readiness, picks first readydefer fn()→ push onto a per-goroutine defer stack; run on return/panicpanic(v)→raisethe value;recover()catches it in deferred functioninterface{}→ any SX value (duck typed)struct { ... }→ SX hash table with field names as keysslice→ SX vector with length + capacity metadatamap[K]V→ SX mutable hash table (Phase 10 of primitives)
Roadmap
Phase 1 — tokenizer + parser
- Tokenizer: keywords (
package,import,func,var,const,type,struct,interface,go,chan,select,defer,return,if,else,for,range,switch,case,default,break,continue,goto,fallthrough,map,make,new,nil,true,false), automatic semicolon insertion, string literals (interpreted + raw`...`), rune literals'a', number literals (int, float, hex, octal, binary, complex), operators, slices[:] - Parser: package clause, imports, top-level
func/var/const/type; function bodies: short variable decl:=, assignments,if/else,for/range,switch,return, struct literals, slice literals, map literals, composite literals, type assertionsv.(T), method callsv.Method(args), goroutinego, channel ops<-ch,ch <- v,defer,select - Tests in
lib/go/tests/parse.sx
Phase 2 — transpile: basic Go (no goroutines)
go-eval-astentry- Arithmetic, string ops, comparison, boolean
- Variables, short decl, assignment, multiple assignment
if/else if/elsefor(C-style),for rangeover slice/map/string- Functions: named + anonymous, multiple return values (SX multiple values, Phase 8)
- Structs → SX hash tables; field access
.field; struct literalsT{f: v} - Slices → SX vectors;
len,cap,append,copy, slice expressionss[a:b] - Maps → SX hash tables;
make(map[K]V),m[k],m[k] = v,delete(m, k), comma-okv, ok := m[k] - Pointers — modelled as single-element mutable vectors;
&xcreates wrapper,*pdereferences fmt.Println/fmt.Printf/fmt.Sprintf→ SX IO perform (print)- 40+ eval tests in
lib/go/tests/eval.sx
Phase 3 — defer / panic / recover
- Defer stack per function frame — SX list of thunks, run LIFO on return
deferstatement pushes thunk; transpiler wraps function body in try/finally equivalentpanic(v)→raisewith Go panic wrapperrecover()→ catches panic value inside a deferred function; returns nil otherwise- Panic propagation across call stack until recovered or fatal
- Tests: defer ordering, panic/recover, panic in goroutine without recover
Phase 4 — goroutines + channels
- Coroutine-based goroutine type using SX coroutine primitive (Phase 4 of primitives)
- Round-robin scheduler in
lib/go/runtime.sx: maintains run queue, steps each goroutine one turn at a time, suspends at channel ops - Unbuffered channels:
make(chan T)→ rendezvous point; send suspends until receive and vice versa. Implemented as a pair of waiting queues +cek-resume. - Buffered channels:
make(chan T, n)→ circular buffer; send only blocks when full, receive only blocks when empty close(ch)— mark channel closed; receivers drain then get zero value +falseselect— scheduler inspects all cases, picks a ready one (random if multiple), blocks if none ready until at least one becomes readygo fn(args)— spawns new goroutine on run queuetime.Sleep(d)— yields current goroutine, re-queues after d milliseconds (simulated with IO perform timer)- Tests: ping-pong, fan-out, fan-in, select with default, range over channel
Phase 5 — interfaces
- Interface type → SX dict
{:type "T" :methods {...}}dispatch table interface{}/any→ any SX value (already implicit)- Type assertion
v.(T)→ check:typefield, panic if mismatch - Type switch
switch v.(type) { case T: ... }→ dispatches on:type - Method sets — structs implement interfaces implicitly if they have the right methods
- Value vs pointer receivers — pointer receiver gets the mutable vector wrapper
- Built-in interfaces:
error(Error() string),Stringer(String() string) - Tests: interface satisfaction, type assertion, type switch, error interface
Phase 6 — standard library subset
fmt—Println,Printf,Sprintf,Fprintf,Errorf,Stringerdispatchstrings—Contains,HasPrefix,HasSuffix,Split,Join,TrimSpace,ToUpper,ToLower,Replace,Index,Count,Repeatstrconv—Itoa,Atoi,FormatFloat,ParseFloat,ParseInt,FormatIntmath— full surface via SX math primitives (Phase 15)sort—sort.Slice,sort.Ints,sort.Stringserrors—errors.New,errors.Is,errors.Assync—sync.Mutex(cooperative — just a boolean flag + goroutine queue),sync.WaitGroup,sync.Onceio—io.Reader/io.Writerinterfaces;io.ReadAll;strings.NewReader
Phase 7 — full conformance target
- Vendor a Go test suite or hand-build 100+ program tests in
lib/go/tests/programs/ - Drive scoreboard
Blockers
(none yet)
Progress log
Newest first.
(awaiting phase 1)