Showcase: blocks with non-local return on captured method-return continuation. ANSI-ish Smalltalk-80 subset, SUnit + Pharo Kernel-Tests slice, 7 phases. Worktree: /root/rose-ash-loops/smalltalk on branch loops/smalltalk.
5.0 KiB
5.0 KiB
smalltalk-on-sx loop agent (single agent, queue-driven)
Role: iterates plans/smalltalk-on-sx.md forever. Message-passing OO + blocks with non-local return on delimited continuations. Non-local return is the headline showcase — every other Smalltalk reinvents it on the host stack; on SX it falls out of the captured method-return continuation.
description: smalltalk-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/smalltalk-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.
Restart baseline — check before iterating
- Read
plans/smalltalk-on-sx.md— roadmap + Progress log. ls lib/smalltalk/— pick up from the most advanced file.- If
lib/smalltalk/tests/*.sxexist, run them. Green before new work. - If
lib/smalltalk/scoreboard.mdexists, that's your baseline.
The queue
Phase order per plans/smalltalk-on-sx.md:
- Phase 1 — tokenizer + parser (chunk format, identifiers, keywords
foo:, binary selectors,#sym,#(…),$c, blocks[:a | …], cascades, message precedence) - Phase 2 — object model + sequential eval (class table bootstrap, message dispatch,
super,doesNotUnderstand:, instance variables) - Phase 3 — THE SHOWCASE: blocks with non-local return via captured method-return continuation.
whileTrue:/ifTrue:ifFalse:as block sends. 5 classic programs (eight-queens, quicksort, mandelbrot, life, fibonacci) green. - Phase 4 — reflection + MOP:
perform:,respondsTo:, runtime method addition,becomeForward:,Exception/on:do:/ensure:on top ofhandler-bind/raise - Phase 5 — collections + numeric tower + streams
- Phase 6 — port SUnit, vendor Pharo Kernel-Tests slice, drive corpus to 200+
- Phase 7 — speed (optional): inline caching, block intrinsification
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/smalltalk/**andplans/smalltalk-on-sx.md. Do not editspec/,hosts/,shared/, otherlib/<lang>/dirs,lib/stdlib.sx, orlib/root. Smalltalk primitives go inlib/smalltalk/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.
Smalltalk-specific gotchas
- Method invocation captures
^k— the return continuation. Bind it as the block's escape token.^exprfrom inside any nested block invokes that captured^k. Escape past method return raisesBlockContext>>cannotReturn:. - Blocks are lambdas + escape token, not bare lambdas.
value/value:/… invoke the lambda;^invokes the escape. ifTrue:/ifFalse:/whileTrue:are ordinary block sends — no special form. The runtime intrinsifies them in the JIT path (Tier 1 of bytecode expansion already covers this pattern).- Cascade
r m1; m2; m3desugars to(let ((tmp r)) (st-send tmp 'm1 ()) (st-send tmp 'm2 ()) (st-send tmp 'm3 ())). Result is the cascade's last send (or first, depending on parser variant — pick one and document). supersend looks up starting from the defining class's superclass, not the receiver class. Stash the defining class on the method record.- Selectors are interned symbols. Use SX symbols.
- Receiver dispatch: tagged ints / floats / strings / symbols /
nil/true/falsearen't boxed. Their classes (SmallInteger,Float,String,Symbol,UndefinedObject,True,False) are looked up by SX type-of, not by an:classfield. - Method precedence: unary > binary > keyword.
3 + 4 factorialis3 + (4 factorial).a foo: b barisa foo: (b bar)(keyword absorbs trailing unary). - Image / fileIn / become: between sessions = out of scope. One-way
becomeForward:only. - Test corpus: ~200 hand-written + a slice of Pharo Kernel-Tests. Place programs in
lib/smalltalk/tests/programs/.
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/smalltalk-on-sx.mdinline. - Short, factual commit messages (
smalltalk: tokenizer + 56 tests). - One feature per iteration. Commit. Log. Next.
Go. Read the plan; find first [ ]; implement.