diff --git a/plans/tcl-sx-completion.md b/plans/tcl-sx-completion.md new file mode 100644 index 00000000..39f633bd --- /dev/null +++ b/plans/tcl-sx-completion.md @@ -0,0 +1,146 @@ +# Tcl-on-SX completion plan — SX capabilities first + +Tcl phases 1–6 are complete (329/329 tests). This plan covers the remaining +limitations, ordered by the SX work needed to enable them. + +## Key audit findings + +Several apparent gaps are already solved in SX: + +- **Floats** — SX parses `3.14` natively; `(+ 1.5 2.5) → 4.0`; `str` formats + with `%g` (compact, no trailing zeros). `floor`/`ceil`/`round`/`truncate` + all exist in `spec/primitives.sx`. +- **Regex** — `regexp-match`, `regexp-match-all`, `regexp-replace`, + `regexp-replace-all`, `regexp-split` are registered OCaml primitives using + `Re.Pcre` (`hosts/ocaml/lib/sx_primitives.ml`). +- **`call/cc` multi-shot** — works. `set!` on closed-over vars works. Fibers + are implementable as a pure SX library. +- **`perform` user-accessible** — `(perform :foo 42)` from user code suspends + the evaluator and emits an IO request. The algebraic effects model is + already half-built. +- **No `file-read`/`clock-seconds`** — not yet registered as OCaml primitives. + Only string ports exist. Would need small OCaml additions. +- **No `env-as-value`** — environments are internal OCaml values, not + inspectable from SX user code. + +--- + +## Phase 1 — Zero-cost wins (no SX changes, only `lib/tcl/`) + +Everything here is pure Tcl implementation work. + +| Work | Effort | Unlocks in Tcl | +|---|---|---| +| Float in `expr` — detect `.` in number tokens, route through float ops instead of `parse-int` | half day | `expr {3.14 * 2}`, `expr {sqrt(2.0)}`, float comparisons | +| `regexp pattern str` and `regsub pattern str repl` wrapping existing SX primitives | few hours | pattern matching, text processing | +| `apply {args body} ?arg…?` — anonymous proc call | 1 hour | higher-order functions, `lmap` idiom | +| `array get/set/names/size/exists/unset` commands | half day | array variables (tokenizer already parses `$arr(key)`) | + +**Total: ~2 days. Zero SX changes.** + +--- + +## Phase 2 — `lib/fiber.sx` (pure SX library, no OCaml) + +`call/cc` is multi-shot and `set!` on closed-over vars both work. Fibers are +implementable as a pure SX library using symmetric continuation swapping: + +```scheme +; lib/fiber.sx — canonical fiber primitive for all hosted languages +(define make-fiber + (fn (thunk) + (define slot-k nil) + (define slot-caller nil) + (define slot-done false) + (fn (resume-val) + (call/cc (fn (caller-k) + (set! slot-caller caller-k) + (if (nil? slot-k) + (begin (thunk resume-val) (set! slot-done true) (caller-k nil)) + (slot-k resume-val))))))) + +(define fiber-yield + (fn (val) + (call/cc (fn (k) + (set! slot-k k) + (slot-caller val))))) +``` + +Each coroutine becomes a fiber. `yield` swaps to the caller; calling the +coroutine name swaps back. True suspension, not eager pre-execution. + +**Broader value:** Ruby fibers, Python generators, Lua coroutines, async event +loops, cooperative schedulers all sit on top of the same library. + +**Alternatively:** `perform` is user-accessible. A Tcl scheduler living outside +the SX evaluator (the OCaml host or an SX event loop) could catch +`(perform :fiber-yield val)` and dispatch it — the algebraic effects model, +already half-built. + +**Total: 2–3 days. Produces `lib/fiber.sx` as a lasting SX contribution.** +Tcl coroutines then rewrite using `make-fiber` for true suspension. + +--- + +## Phase 3 — Small OCaml additions (`sx_primitives.ml`) + +Each is ~10–20 lines of OCaml. All are useful across the whole platform, not +just Tcl. + +| Primitive | OCaml effort | Unlocks | +|---|---|---| +| `(file-read path)` → string | tiny | Tcl `open`/`read`, SX scripts reading files | +| `(file-write path str)` → nil | tiny | Tcl `open`/`puts` to files | +| `(file-exists? path)` → bool | tiny | Tcl `file exists` | +| `(file-glob pattern)` → list | small | Tcl `glob` | +| `(clock-seconds)` → int | tiny | Tcl `clock seconds` | +| `(clock-format n fmt)` → string | small (wraps `strftime`) | Tcl `clock format` | + +**Total: 1 day. One focused afternoon of OCaml.** + +--- + +## Phase 4 — Optional: env-as-value (architectural) + +`uplevel`/`upvar` required an explicit frame stack because SX environments +aren't inspectable from user code. Adding: + +```scheme +(current-env) ; → env value +(eval-in-env env expr) ; → result +(env-lookup env key) ; → value or nil +(env-extend env key val) ; → new env (non-mutating) +``` + +...would let `uplevel N` be literally "look up env N levels up, eval in it." +The Tcl frame stack (hundreds of lines) collapses to ~10 lines. + +Also benefits: metacircular evaluators, REPL tooling, live debugging (inspect +any scope), the sx_docs server's eval endpoint. + +More invasive — touches `sx_types.ml` and `sx_server.ml` — but a meaningful +architectural improvement worth doing when the moment is right. + +**Total: 2–3 days. High architectural value, not urgent.** + +--- + +## Suggested order + +1. **Phase 1** — immediate Tcl wins, zero risk, proves the approach +2. **Phase 2** (`lib/fiber.sx`) — the interesting SX work, benefits all hosted languages +3. **Phase 3** (OCaml primitives) — quick practical completions +4. **Phase 4** — architectural cleanup when it's worth the invasiveness + +Phases 1+2+3 ≈ one focused week. Tcl is genuinely complete, and `lib/fiber.sx` +becomes a lasting SX contribution used by every future hosted language. + +--- + +## What stays out of scope + +- `package require` of binary loadables +- Full `clock format` locale support +- Tk / GUI +- Threads (mapped to coroutines only, as planned) +- Full POSIX file I/O (seek/tell/async) — stubs are fine