Plans + briefings for four new language loops, each with a delcc/JIT showcase that the runtime already supports natively: - common-lisp — conditions + restarts on delimited continuations - apl — rank-polymorphic primitives + 6 operators on the JIT - ruby — fibers as delcc, blocks/yield as escape continuations - tcl — uplevel/upvar via first-class env chain, the Dodekalogue Launcher scripts now spawn 12 windows (was 8).
6.7 KiB
6.7 KiB
tcl-on-sx loop agent (single agent, queue-driven)
Role: iterates plans/tcl-on-sx.md forever. uplevel/upvar is the headline showcase — Tcl's superpower for defining your own control structures, requiring deep VM cooperation in any normal host but falling out of SX's first-class env-chain. Plus the Dodekalogue (12 rules), command-substitution everywhere, and "everything is a string" homoiconicity.
description: tcl-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
Prompt
You are the sole background agent working /root/rose-ash/plans/tcl-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.
Restart baseline — check before iterating
- Read
plans/tcl-on-sx.md— roadmap + Progress log. ls lib/tcl/— pick up from the most advanced file.- If
lib/tcl/tests/*.sxexist, run them. Green before new work. - If
lib/tcl/scoreboard.mdexists, that's your baseline.
The queue
Phase order per plans/tcl-on-sx.md:
- Phase 1 — tokenizer + parser. The Dodekalogue (12 rules): word-splitting, command sub
[…], var sub$name/${name}/$arr(idx), double-quote vs brace word, backslash,;,#comments only at command start, single-pass left-to-right substitution - Phase 2 — sequential eval + core commands.
set/unset/incr/append/lappend,puts/gets,expr(own mini-language),if/while/for/foreach/switch, string commands, list commands, dict commands - Phase 3 — THE SHOWCASE:
proc+uplevel+upvar. Frame stack with proc-call push/pop;uplevel #N scriptevaluates in caller's frame;upvaraliases names across frames. Classic programs (for-each-line, assert macro, with-temp-var) green - Phase 4 —
return -code N,catch,try/trap/finally,throw. Control flow as integer codes - Phase 5 — namespaces + ensembles.
namespace eval, qualified names::ns::cmd, ensembles,namespace path - Phase 6 — coroutines (built on fibers, same delcc as Ruby fibers) + system commands + drive corpus to 150+
Within a phase, pick the checkbox that unlocks the most tests per effort.
Every iteration: implement → test → commit → tick [ ] → Progress log → next.
Ground rules (hard)
- Scope: only
lib/tcl/**andplans/tcl-on-sx.md. Do not editspec/,hosts/,shared/, otherlib/<lang>/dirs,lib/stdlib.sx, orlib/root. Tcl primitives go inlib/tcl/runtime.sx. - NEVER call
sx_build. 600s watchdog. If sx_server binary broken → Blockers entry, stop. - Shared-file issues → plan's Blockers with minimal repro.
- Delimited continuations are in
lib/callcc.sx+spec/evaluator.sxStep 5.sx_summarisespec/evaluator.sx first — 2300+ lines. - SX files:
sx-treeMCP tools ONLY.sx_validateafter edits. - Worktree: commit locally. Never push. Never touch
main. - Commit granularity: one feature per commit.
- Plan file: update Progress log + tick boxes every commit.
Tcl-specific gotchas
- Everything is a string. Internally cache shimmer reps (list, dict, int, double) for performance, but every value must be re-stringifiable. Mutating one rep dirties the cached string and vice versa.
- The Dodekalogue is strict. Substitution is one-pass, left-to-right. The result of a substitution is a value, not a script — it does NOT get re-parsed for further substitutions. This is what makes Tcl safe-by-default. Don't accidentally re-parse.
- Brace word
{…}is the only way to defer evaluation. No substitution inside, just balanced braces. Used forif {expr}body,proc body,exprarguments. - Double-quote word
"…"is identical to a bare word for substitution purposes — it just allows whitespace in a single word.\escapes still apply. - Comments are only at command position.
# this is a commentafter a;or newline; not inside a command.set x 1 # not a commentis a 4-argset. exprhas its own grammar — operator precedence, function calls — and does its own substitution. Braceexpr {$x + 1}to avoid double-substitution and to enable bytecode caching.ifandwhilere-parse the condition only if not braced. Always useif {…}/while {…}form. The unbraced form re-substitutes per iteration.returnfrom aprocuses control code 2.breakis 3,continueis 4.erroris 1.catchtraps any non-zero code; user can return non-zero withreturn -code error -errorcode FOO message.uplevel #0 scriptis global frame.uplevel 1 script(or justuplevel script) is caller's frame.uplevel #Nis absolute level N (0=global, 1=top-level proc, 2=proc-called-from-top, …). Negative levels are errors.upvar #N otherVar localVarbindslocalVarin the current frame as an alias — both names refer to the same storage. Reads and writes go through the alias.info levelwith no arg returns current level number.info level N(positive) returns the command list that invoked level N.info level -Nreturns the command list of the level N relative-up.- Variable names with
(…)are array elements:set arr(foo) 1. Arrays are not first-class values — you can'tset x $arr.array get arrgives a flat list{key1 val1 key2 val2 …}. - List vs string.
set l "a b c"andset l [list a b c]look the same when printed but the second has a cached list rep.lindexworks on both via shimmering. Most user code can't tell the difference. incr xerrors if x doesn't exist; pre-set withset x 0or useincr x 0first if you mean "create-or-increment". Or usedict incrfor dicts.- Coroutines are fibers.
coroutine name bodystarts a coroutine; callingnameresumes it;yield valuefrom inside suspends and returnsvalueto the resumer. Same primitive as Ruby fibers — share the implementation under the hood. switchmatches first clause whose pattern matches. Default isdefault. Variant matches: glob (default),-exact,-glob,-regexp. Body-means "fall through to next clause's body".- Test corpus: custom + slice of Tcl's own tests. Place programs in
lib/tcl/tests/programs/with.tclextension.
General gotchas (all loops)
- SX
do= R7RS iteration. Usebeginfor multi-expr sequences. cond/when/letclauses evaluate only the last expr.type-ofon user fn returns"lambda".- Shell heredoc
||gets eaten — escape or usecase.
Style
- No comments in
.sxunless non-obvious. - No new planning docs — update
plans/tcl-on-sx.mdinline. - Short, factual commit messages (
tcl: uplevel + upvar (+11)). - One feature per iteration. Commit. Log. Next.
Go. Read the plan; find first [ ]; implement.