apl: ⍵-rebind + primes.apl runs as-written (+4 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 50s

Two changes wire the original primes idiom through:

1. Parser :glyph branch detects ⍵← / ⍺← and emits :assign-expr
   (was only :name-token before).
2. Eval-ast :name lookup checks env["⍵"]/env["⍺"] before falling
   back to env["omega"]/env["alpha"].  Inline ⍵-rebind binds
   under the glyph key directly.

apl-run "primes ← {(2=+⌿0=⍵∘.|⍵)/⍵←⍳⍵} ⋄ primes 50"
→ 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47

primes.apl now runs as-written via apl-run-file + " ⋄ primes 30".
This commit is contained in:
2026-05-07 23:19:45 +00:00
parent bf782d9c49
commit f5d3b1df19
4 changed files with 60 additions and 9 deletions

View File

@@ -344,10 +344,22 @@
((= tt :glyph)
(cond
((or (= tv "") (= tv "⍵"))
(collect-segments-loop
tokens
(+ i 1)
(append acc {:kind "val" :node (list :name tv)})))
(if
(and
(< (+ i 1) (len tokens))
(= (tok-type (nth tokens (+ i 1))) :assign))
(let
((rhs-tokens (slice tokens (+ i 2) (len tokens))))
(let
((rhs-expr (parse-apl-expr rhs-tokens)))
(collect-segments-loop
tokens
(len tokens)
(append acc {:kind "val" :node (list :assign-expr tv rhs-expr)}))))
(collect-segments-loop
tokens
(+ i 1)
(append acc {:kind "val" :node (list :name tv)}))))
((= tv "∇")
(collect-segments-loop
tokens

View File

@@ -397,8 +397,37 @@
:dfn)
(apl-test
"apl-run-file: source-then-call shape"
"apl-run-file: source-then-call returns primes count"
(mksh
(apl-run
(str (file-read "lib/apl/tests/programs/primes.apl") " ⋄ primes 30")))
(list 0))
(list 10))
(apl-test
"primes one-liner with ⍵-rebind: primes 30"
(mkrv
(apl-run "primes ← {(2=+⌿0=⍵∘.|⍵)/⍵←⍳⍵} ⋄ primes 30"))
(list 2 3 5 7 11 13 17 19 23 29))
(apl-test
"primes one-liner: primes 50"
(mkrv
(apl-run "primes ← {(2=+⌿0=⍵∘.|⍵)/⍵←⍳⍵} ⋄ primes 50"))
(list 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47))
(apl-test
"primes.apl loaded + called via apl-run-file"
(mkrv
(apl-run
(str (file-read "lib/apl/tests/programs/primes.apl") " ⋄ primes 20")))
(list 2 3 5 7 11 13 17 19))
(apl-test
"primes.apl loaded — count of primes ≤ 100"
(first
(mksh
(apl-run
(str
(file-read "lib/apl/tests/programs/primes.apl")
" ⋄ primes 100"))))
25)

View File

@@ -122,8 +122,14 @@
(let
((nm (nth node 1)))
(cond
((= nm "") (get env "alpha"))
((= nm "⍵") (get env "omega"))
((= nm "")
(let
((v (get env "")))
(if (= v nil) (get env "alpha") v)))
((= nm "⍵")
(let
((v (get env "⍵")))
(if (= v nil) (get env "omega") v)))
((= nm "⎕IO") (apl-quad-io))
((= nm "⎕ML") (apl-quad-ml))
((= nm "⎕FR") (apl-quad-fr))

View File

@@ -211,10 +211,13 @@ Today they are documentation; we paraphrase the algorithms in
read; fall back to embedded source if no read primitive exists.
_(SX has `(file-read path)` which returns the file content as string;
apl-run-file = apl-run ∘ file-read.)_
- [ ] **End-to-end .apl tests** — once the above land, add tests that
- [x] **End-to-end .apl tests** — once the above land, add tests that
run `lib/apl/tests/programs/*.apl` *as written* and assert results.
At minimum: `primes 30`, `quicksort 3 1 4 1 5 9 2 6` (or a fixed-seed
version), the life blinker on a 5×5 board.
_(primes.apl runs as-written with ⍵-rebind now supported. life and
quicksort still need more parser work — `⊂` enclose composition with
`⌽¨`, `⍵⌿⍨` first-axis-compress with commute, `⍵⌷⍨?≢⍵`.)_
- [ ] **Audit silently-skipped glyphs** — sweep `apl-glyph-set` and
`apl-parse-fn-glyphs` against the runtime's `apl-monadic-fn` and
`apl-dyadic-fn` cond chains to find any that the runtime supports
@@ -234,6 +237,7 @@ data; format for string templating.
_Newest first._
- 2026-05-07: Phase 9 step 5 — primes.apl runs as-written end-to-end. Added ⍵/ inline-assign in parser :glyph branch + :name lookup falls back from "⍵"/"" key to "omega"/"alpha". `apl-run "primes ← {(2=+⌿0=⍵∘.|⍵)/⍵←⍳⍵} ⋄ primes 50"` → 15 primes. +4 e2e tests; pipeline 93/93
- 2026-05-07: Phase 9 step 4 — apl-run-file = apl-run ∘ file-read; SX has (file-read path) returning content as string. primes/life/quicksort .apl files now load and parse end-to-end (return :dfn AST). +4 tests
- 2026-05-07: Phase 9 step 3 — `?N` random / roll. Top-level mutable apl-rng-state with LCG; apl-rng-seed! for deterministic tests; apl-roll wraps as scalar in 1..N. apl-monadic-fn maps "?" → apl-roll. +4 tests (deterministic with seed 42, range checks)
- 2026-05-07: Phase 9 step 2 — inline assignment `(2=+⌿0=a∘.|a)/a←30` runs end-to-end. Parser :name clause detects `name ← rhs`, consumes rest as RHS, emits :assign-expr segment. Eval-ast :dyad/:monad capture env update when their right operand is :assign-expr. +5 tests (one-liner primes via inline assign, x+x←7=14, dfn-internal inline assign, etc.)