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.
78 lines
5.0 KiB
Markdown
78 lines
5.0 KiB
Markdown
# 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
|
|
|
|
1. Read `plans/smalltalk-on-sx.md` — roadmap + Progress log.
|
|
2. `ls lib/smalltalk/` — pick up from the most advanced file.
|
|
3. If `lib/smalltalk/tests/*.sx` exist, run them. Green before new work.
|
|
4. If `lib/smalltalk/scoreboard.md` exists, 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 of `handler-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/**` and `plans/smalltalk-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, other `lib/<lang>/` dirs, `lib/stdlib.sx`, or `lib/` root. Smalltalk primitives go in `lib/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.sx` Step 5. `sx_summarise` spec/evaluator.sx first — 2300+ lines.
|
|
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after 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. `^expr` from inside any nested block invokes that captured `^k`. Escape past method return raises `BlockContext>>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; m3` desugars 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).
|
|
- **`super` send** 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` / `false` aren't boxed. Their classes (`SmallInteger`, `Float`, `String`, `Symbol`, `UndefinedObject`, `True`, `False`) are looked up by SX type-of, not by an `:class` field.
|
|
- **Method precedence:** unary > binary > keyword. `3 + 4 factorial` is `3 + (4 factorial)`. `a foo: b bar` is `a 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. Use `begin` for multi-expr sequences.
|
|
- `cond`/`when`/`let` clauses evaluate only the last expr.
|
|
- `type-of` on user fn returns `"lambda"`.
|
|
- Shell heredoc `||` gets eaten — escape or use `case`.
|
|
|
|
## Style
|
|
|
|
- No comments in `.sx` unless non-obvious.
|
|
- No new planning docs — update `plans/smalltalk-on-sx.md` inline.
|
|
- Short, factual commit messages (`smalltalk: tokenizer + 56 tests`).
|
|
- One feature per iteration. Commit. Log. Next.
|
|
|
|
Go. Read the plan; find first `[ ]`; implement.
|