# 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/**` and `plans/go-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, or other `lib//`. - **Shared-file issues** go under "Blockers" below with a minimal repro; do not fix here. - **SX files:** use `sx-tree` MCP 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 log` updated 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) → `perform` that suspends until receiver ready; scheduler picks next goroutine - `v := <-ch` (receive) → `perform` that suspends until sender ready - `select { case ... }` → scheduler checks all channel readiness, picks first ready - `defer fn()` → push onto a per-goroutine defer stack; run on return/panic - `panic(v)` → `raise` the value; `recover()` catches it in deferred function - `interface{}` → any SX value (duck typed) - `struct { ... }` → SX hash table with field names as keys - `slice` → SX vector with length + capacity metadata - `map[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 assertions `v.(T)`, method calls `v.Method(args)`, goroutine `go`, channel ops `<-ch`, `ch <- v`, `defer`, `select` - [ ] Tests in `lib/go/tests/parse.sx` ### Phase 2 — transpile: basic Go (no goroutines) - [ ] `go-eval-ast` entry - [ ] Arithmetic, string ops, comparison, boolean - [ ] Variables, short decl, assignment, multiple assignment - [ ] `if`/`else if`/`else` - [ ] `for` (C-style), `for range` over slice/map/string - [ ] Functions: named + anonymous, multiple return values (SX multiple values, Phase 8) - [ ] Structs → SX hash tables; field access `.field`; struct literals `T{f: v}` - [ ] Slices → SX vectors; `len`, `cap`, `append`, `copy`, slice expressions `s[a:b]` - [ ] Maps → SX hash tables; `make(map[K]V)`, `m[k]`, `m[k] = v`, `delete(m, k)`, comma-ok `v, ok := m[k]` - [ ] Pointers — modelled as single-element mutable vectors; `&x` creates wrapper, `*p` dereferences - [ ] `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 - [ ] `defer` statement pushes thunk; transpiler wraps function body in try/finally equivalent - [ ] `panic(v)` → `raise` with Go panic wrapper - [ ] `recover()` → 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 + `false` - [ ] `select` — scheduler inspects all cases, picks a ready one (random if multiple), blocks if none ready until at least one becomes ready - [ ] `go fn(args)` — spawns new goroutine on run queue - [ ] `time.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 `:type` field, 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`, `Stringer` dispatch - [ ] `strings` — `Contains`, `HasPrefix`, `HasSuffix`, `Split`, `Join`, `TrimSpace`, `ToUpper`, `ToLower`, `Replace`, `Index`, `Count`, `Repeat` - [ ] `strconv` — `Itoa`, `Atoi`, `FormatFloat`, `ParseFloat`, `ParseInt`, `FormatInt` - [ ] `math` — full surface via SX math primitives (Phase 15) - [ ] `sort` — `sort.Slice`, `sort.Ints`, `sort.Strings` - [ ] `errors` — `errors.New`, `errors.Is`, `errors.As` - [ ] `sync` — `sync.Mutex` (cooperative — just a boolean flag + goroutine queue), `sync.WaitGroup`, `sync.Once` - [ ] `io` — `io.Reader`/`io.Writer` interfaces; `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)_