diff --git a/lib/apl/runtime.sx b/lib/apl/runtime.sx index a1957d5f..e96fb0a5 100644 --- a/lib/apl/runtime.sx +++ b/lib/apl/runtime.sx @@ -827,6 +827,31 @@ ((new-ravel (reduce (fn (acc r) (append acc (map (fn (j) (nth ravel (+ (* r cols) j))) (range 0 cols)))) (list) kept-rows))) (make-array (cons (len kept-rows) (rest shape)) new-ravel)))))))) +(define + apl-where + (fn + (arr) + (let + ((ravel (get arr :ravel)) (io (disclose (apl-quad-io)))) + (let + ((indices (filter (fn (i) (not (= (nth ravel i) 0))) (range 0 (len ravel))))) + (apl-vector (map (fn (i) (+ i io)) indices)))))) + +(define + apl-interval-index + (fn + (breaks vals) + (let + ((b-ravel (get breaks :ravel)) + (v-ravel + (if (scalar? vals) (list (disclose vals)) (get vals :ravel)))) + (let + ((result (map (fn (y) (len (filter (fn (b) (<= b y)) b-ravel))) v-ravel))) + (if + (scalar? vals) + (apl-scalar (first result)) + (make-array (get vals :shape) result)))))) + (define apl-primes (fn diff --git a/lib/apl/tests/pipeline.sx b/lib/apl/tests/pipeline.sx index 2d21bfb6..1e857498 100644 --- a/lib/apl/tests/pipeline.sx +++ b/lib/apl/tests/pipeline.sx @@ -455,3 +455,45 @@ (list 1 2 3)) (apl-test "⍕ 42 → \"42\" (alias for ⎕FMT)" (apl-run "⍕ 42") "42") + +(begin + (apl-test + "⍸ where: indices of truthy cells" + (mkrv (apl-run "⍸ 0 1 0 1 1")) + (list 2 4 5)) + (apl-test + "⍸ where: leading truthy" + (mkrv (apl-run "⍸ 1 0 0 1 1")) + (list 1 4 5)) + (apl-test + "⍸ where: all-zero → empty" + (mkrv (apl-run "⍸ 0 0 0")) + (list)) + (apl-test + "⍸ where: all-truthy" + (mkrv (apl-run "⍸ 1 1 1")) + (list 1 2 3)) + (apl-test + "⍸ where: ⎕IO=1 (1-based)" + (mkrv (apl-run "⍸ (⍳5)=3")) + (list 3)) + (apl-test + "⍸ interval-index: 2 4 6 ⍸ 5 → 2" + (mkrv (apl-run "2 4 6 ⍸ 5")) + (list 2)) + (apl-test + "⍸ interval-index: 2 4 6 ⍸ 1 3 5 6 7 → 0 1 2 3 3" + (mkrv (apl-run "2 4 6 ⍸ 1 3 5 6 7")) + (list 0 1 2 3 3)) + (apl-test + "⍸ interval-index: ⍳5 ⍸ 3 → 3" + (mkrv (apl-run "(⍳5) ⍸ 3")) + (list 3)) + (apl-test + "⍸ interval-index: y below all → 0" + (mkrv (apl-run "10 20 30 ⍸ 5")) + (list 0)) + (apl-test + "⍸ interval-index: y above all → len breaks" + (mkrv (apl-run "10 20 30 ⍸ 100")) + (list 3))) diff --git a/lib/apl/transpile.sx b/lib/apl/transpile.sx index d5b50148..ce7575cd 100644 --- a/lib/apl/transpile.sx +++ b/lib/apl/transpile.sx @@ -46,6 +46,7 @@ ((= g "⍕") apl-quad-fmt) ((= g "⎕FMT") apl-quad-fmt) ((= g "⎕←") apl-quad-print) + ((= g "⍸") apl-where) (else (error "no monadic fn for glyph"))))) (define @@ -90,6 +91,7 @@ ((= g "⍉") apl-transpose-dyadic) ((= g "⊢") (fn (a b) b)) ((= g "⊣") (fn (a b) a)) + ((= g "⍸") apl-interval-index) (else (error "no dyadic fn for glyph"))))) (define diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index c1113610..be407957 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -233,7 +233,7 @@ Phase 9 left seven glyphs that the parser recognises but the runtime cannot evaluate, and two source files (`life.apl`, `quicksort.apl`) that still need work to run as-written. Phase 10 closes both. -- [ ] **`⍸` where** — monadic `⍸ B` returns the indices of the truthy +- [x] **`⍸` where** — monadic `⍸ B` returns the indices of the truthy cells (1-based per `⎕IO`). Dyadic `X ⍸ Y` is interval index (find the largest `i` such that `X[i] ≤ Y`). Add `apl-where` + dyadic `apl-interval-index`; wire both into `apl-monadic-fn` / `apl-dyadic-fn`. @@ -288,6 +288,7 @@ data; format for string templating. _Newest first._ +- 2026-05-08: Phase 10 step 1 — `⍸` where. apl-where (monadic, indices of truthy cells, ⎕IO-respecting) + apl-interval-index (dyadic, count of breaks ≤ y; broadcasts over Y vector or scalar). Wired into apl-monadic-fn / apl-dyadic-fn (cond clauses inserted as proper siblings via sx_insert_child after sx_insert_near silently wrapped multi-form sources in `(begin …)`). +10 tests; pipeline 109/109 - 2026-05-08: Phase 10 added — fill runtime gaps (⍸ ∪ ∩ ⊥ ⊤ ⊆ ⍎) + life.apl and quicksort.apl as-written - 2026-05-07: Phase 9 step 6 — glyph audit. Wired ⍉ → apl-transpose/apl-transpose-dyadic, ⊢ → monadic+dyadic identity-right, ⊣ → identity-left, ⍕ → apl-quad-fmt. +6 tests; **Phase 9 complete, all unchecked items ticked**; pipeline 99/99 - 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