diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index 046a2b25..925caf7c 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -135,6 +135,43 @@ and tightens loose ends. on error switches to the trap branch. Define `apl-throw` and a small set of error codes; use `try`/`catch` from the host. +### Phase 8 — fill the gaps left after end-to-end + +Phase 7 wired the stack together; Phase 8 closes deferred items, lets real +programs run from source, and starts pushing on performance. + +- [ ] **Quick-wins bundle** (one iteration) — three small fixes that each unblock + real programs: + - decimal literals: `read-digits!` consumes one trailing `.` plus more digits + so `3.7` tokenises as one number; + - `⎕←` (print) — tokenizer special-case: when `⎕` is followed by `←`, emit + a single `:name "⎕←"` token (don't split on the assign glyph); + - string values in `apl-eval-ast` — handle `:str` (parser already produces + them) by wrapping into a vector of character codes (or rank-0 string). +- [ ] **Named function definitions** — `f ← {⍺+⍵} ⋄ 1 f 2` and `2 f 3`. + - parser: when `:assign`'s RHS is a `:dfn`, mark it as a function binding; + - eval-ast: `:assign` of a dfn stores the dfn in env; + - parser: a name in fn-position whose env value is a dfn dispatches as a fn; + - resolver: extend `apl-resolve-monadic`/`-dyadic` with a `:fn-name` case + that calls `apl-call-dfn`/`apl-call-dfn-m`. +- [ ] **Multi-axis bracket indexing** — `A[I;J]` and `A[;J]` and `A[I;]`. + - parser: split bracket content on `:semi` at depth 0; emit + `(:dyad ⌷ (:vec I J) A)`; + - runtime: extend `apl-squad` to accept a vector of indices, treating + `nil` / empty axis as "all"; + - 5+ tests across vector and matrix. +- [ ] **`.apl` files as actual tests** — `lib/apl/tests/programs/*.apl` are + currently documentation. Add `apl-run-file path → array` plus tests that + load each file, execute it, and assert the expected result. Makes the + classic-program corpus self-validating instead of two parallel impls. +- [ ] **Train/fork notation** — `(f g h) ⍵ ↔ (f ⍵) g (h ⍵)` (3-train); + `(g h) ⍵ ↔ g (h ⍵)` (2-train atop). Parser: detect when a parenthesised + subexpression is all functions and emit `(:train fns)`; resolver: build the + derived function; tests for mean-via-train (`+/÷≢`). +- [ ] **Performance pass** — n-queens(8) currently ~30 s/iter (tight on the + 300 s timeout). Target: profile the inner loop, eliminate quadratic + list-append, restore the `queens(8)` test. + ## SX primitive baseline Use vectors for arrays; numeric tower + rationals for numbers; ADTs for tagged data; @@ -149,6 +186,7 @@ data; format for string templating. _Newest first._ +- 2026-05-07: Phase 8 added — quick-wins bundle (decimals + ⎕← + strings), named functions, multi-axis bracket, .apl-files-as-tests, trains, perf - 2026-05-07: Phase 7 step 6 — :Trap exception machinery via R7RS guard; apl-throw raises tagged error, apl-trap-matches? checks codes (0=catch-all), :trap clause in apl-tradfn-eval-stmt wraps try-block with guard; :throw AST for testing; **Phase 7 complete, all unchecked plan items done**; +5 tests; 450/450 - 2026-05-07: Phase 7 step 5 — idiom corpus 34→64 (+30 source-string idioms via apl-run); also fixed tokenizer + parser to recognize ≢ and ≡ glyphs (were silently skipped); 445/445 - 2026-05-07: Phase 7 step 4 — bracket indexing `A[I]` desugared to `(:dyad ⌷ I A)` via maybe-bracket helper, wired into :name + :lparen branches of collect-segments-loop; multi-axis (A[I;J]) deferred (semicolon split); +7 tests; 415/415