6.2 KiB
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.14natively;(+ 1.5 2.5) → 4.0;strformats with%g(compact, no trailing zeros).floor/ceil/round/truncateall exist inspec/primitives.sx. - Regex —
regexp-match,regexp-match-all,regexp-replace,regexp-replace-all,regexp-splitare registered OCaml primitives usingRe.Pcre(hosts/ocaml/lib/sx_primitives.ml). call/ccmulti-shot — works.set!on closed-over vars works. Fibers are implementable as a pure SX library.performuser-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.
| Status | Work | Effort | Unlocks in Tcl |
|---|---|---|---|
| [x] | 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 |
| [x] | regexp pattern str and regsub pattern str repl wrapping existing SX primitives |
few hours | pattern matching, text processing |
| [x] | 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:
; 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:
(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
- Phase 1 — immediate Tcl wins, zero risk, proves the approach
- Phase 2 (
lib/fiber.sx) — the interesting SX work, benefits all hosted languages - Phase 3 (OCaml primitives) — quick practical completions
- 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.
Progress log
Newest first.
- 2026-05-06: Phase 1 apply —
tcl-cmd-applywrapstcl-call-proc, parses{args body}funcList, full frame isolation; 329/329 tests green - 2026-05-06: Phase 1 regexp/regsub —
tcl-cmd-regexp/tcl-cmd-regsubwrappingmake-regexp/regexp-match/regexp-match-all/regexp-replace/regexp-replace-all; -nocase/-all/-inline/-all flags; matchVar + subgroup capture; 329/329 tests green - 2026-05-06: Phase 1 float expr —
tcl-num-float?,tcl-parse-num, float-awaretcl-apply-binop/tcl-apply-func/unary-minus/**;sqrt/floor/ceil/round/sin/cos/tan/pow/exp/logall float-native; 329/329 tests green
What stays out of scope
package requireof binary loadables- Full
clock formatlocale support - Tk / GUI
- Threads (mapped to coroutines only, as planned)
- Full POSIX file I/O (seek/tell/async) — stubs are fine