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.3 KiB
6.3 KiB
common-lisp-on-sx loop agent (single agent, queue-driven)
Role: iterates plans/common-lisp-on-sx.md forever. Conditions + restarts on delimited continuations is the headline showcase — every other Lisp reinvents resumable exceptions on the host stack. On SX signal/invoke-restart is just a captured continuation. Plus CLOS, the LOOP macro, packages.
description: common-lisp-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/common-lisp-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.
Restart baseline — check before iterating
- Read
plans/common-lisp-on-sx.md— roadmap + Progress log. ls lib/common-lisp/— pick up from the most advanced file.- If
lib/common-lisp/tests/*.sxexist, run them. Green before new work. - If
lib/common-lisp/scoreboard.mdexists, that's your baseline.
The queue
Phase order per plans/common-lisp-on-sx.md:
- Phase 1 — reader + parser (read macros
#''`,,@#( … )#:#\char#xFF#b1010, ratios, dispatch chars, lambda lists with&optional/&rest/&key/&aux) - Phase 2 — sequential eval + special forms (
let/let*/flet/labels,block/return-from,tagbody/go,unwind-protect, multiple values,setfsubset, dynamic variables) - Phase 3 — THE SHOWCASE: condition system + restarts.
define-condition,signal/error/cerror/warn,handler-bind(non-unwinding),handler-case(unwinding),restart-case,restart-bind,find-restart/invoke-restart/compute-restarts,with-condition-restarts. Classic programs (restart-demo, parse-recover, interactive-debugger) green. - Phase 4 — CLOS:
defclass,defgeneric,defmethodwith:before/:after/:around,call-next-method, multiple dispatch - Phase 5 — macros + LOOP macro + reader macros
- Phase 6 — packages + stdlib (sequence functions, FORMAT directives, drive corpus to 200+)
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/common-lisp/**andplans/common-lisp-on-sx.md. Do not editspec/,hosts/,shared/, otherlib/<lang>/dirs,lib/stdlib.sx, orlib/root. CL primitives go inlib/common-lisp/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.
Common-Lisp-specific gotchas
handler-bindis non-unwinding — handlers can decline by returning normally, in which casesignalkeeps walking the chain.handler-caseis unwinding — picking a handler aborts the protected form via a captured continuation. Don't conflate them.- Restarts are not handlers.
restart-caseestablishes named resumption points;signalruns handler code with restarts visible; the handler chooses a restart by callinginvoke-restart, which abandons handler stack and resumes at the restart point. Two stacks: handlers walk down, restarts wait to be invoked. block/return-fromis lexical.block name … (return-from name v) …captures^konce at entry;return-frominvokes it.return-fromto a name not in scope is an error (don't fall back to outer block).tagbody/go— each tag in tagbody is a continuation;go taginvokes it. Tags are lexical, can only target tagbodies in scope.unwind-protectruns cleanup on any non-local exit (return-from, throw, condition unwind). Implement as a scope frame fired by the cleanup machinery.- Multiple values: primary-value-only contexts (function args,
iftest, etc.) drop extras silently.valuesproduces multiple.multiple-value-bind/multiple-value-callconsume them. Don't auto-list. - CLOS dispatch: sort applicable methods by argument-list specificity (
subclasspper arg, left-to-right); standard method combination calls primary methods most-specific-first viacall-next-methodchain.:beforeruns all before primaries;:afterruns all after, in reverse-specificity.:aroundwraps everything. call-next-methodis a continuation available only inside a method body. Implement as a thunk stored in a dynamic-extent variable.- Generalised reference (
setf):(setf (foo x) v)↦(setf-foo v x). Look up the setf-expander, not just a writer fn.define-setf-expanderis mandatory for non-trivial places. Start with the symbolic / list / aref / slot-value cases. - Dynamic variables (specials):
defvar/defparametermark a symbol as special.letover a special name rebinds in dynamic extent (use parameterize-style scope), not lexical. - Symbols are package-qualified. Reader resolves
cl:car,mypkg::internal, barefoo(current package). Internal vs external matters for:(one colon) reads. nilis also()is also the empty list. Same object.nilis also false. CL has no distinct unit value.- LOOP macro is huge. Build incrementally — start with
for/in,for/from,collect,sum,count,repeat. Add conditional clauses (when,if,else) once iteration drivers stable.namedblocks +return-from namedlast. - Test corpus: custom + curated
ansi-testslice. Place programs inlib/common-lisp/tests/programs/with.lispextension.
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/common-lisp-on-sx.mdinline. - Short, factual commit messages (
common-lisp: handler-bind + 12 tests). - One feature per iteration. Commit. Log. Next.
Go. Read the plan; find first [ ]; implement.