diff --git a/lib/apl/parser.sx b/lib/apl/parser.sx index cc7b83b5..58432c05 100644 --- a/lib/apl/parser.sx +++ b/lib/apl/parser.sx @@ -206,17 +206,24 @@ tokens ni (append acc {:kind "fn" :node fn-node}))))) - (collect-segments-loop - tokens - (+ i 1) - (append acc {:kind "val" :node (list :name tv)})))) + (let + ((br (maybe-bracket (list :name tv) tokens (+ i 1)))) + (collect-segments-loop + tokens + (nth br 1) + (append acc {:kind "val" :node (nth br 0)}))))) ((= tt :lparen) (let ((end (find-matching-close tokens (+ i 1) :lparen :rparen))) (let ((inner-tokens (slice tokens (+ i 1) end)) (after (+ end 1))) - (collect-segments-loop tokens after (append acc {:kind "val" :node (parse-apl-expr inner-tokens)}))))) + (let + ((br (maybe-bracket (parse-apl-expr inner-tokens) tokens after))) + (collect-segments-loop + tokens + (nth br 1) + (append acc {:kind "val" :node (nth br 0)})))))) ((= tt :lbrace) (let ((end (find-matching-close tokens (+ i 1) :lbrace :rbrace))) @@ -499,3 +506,23 @@ (= (len stmt-groups) 1) (parse-stmt (first stmt-groups)) (cons :program (map parse-stmt stmt-groups)))))))) + +(define + maybe-bracket + (fn + (val-node tokens after) + (if + (and + (< after (len tokens)) + (= (tok-type (nth tokens after)) :lbracket)) + (let + ((end (find-matching-close tokens (+ after 1) :lbracket :rbracket))) + (let + ((inner-tokens (slice tokens (+ after 1) end)) + (next-after (+ end 1))) + (let + ((idx-expr (parse-apl-expr inner-tokens))) + (let + ((indexed (list :dyad (list :fn-glyph "⌷") idx-expr val-node))) + (maybe-bracket indexed tokens next-after))))) + (list val-node after)))) diff --git a/lib/apl/tests/pipeline.sx b/lib/apl/tests/pipeline.sx index 7434744f..e1c79e8b 100644 --- a/lib/apl/tests/pipeline.sx +++ b/lib/apl/tests/pipeline.sx @@ -143,3 +143,38 @@ "1 2 3 4 5") (apl-test "apl-run \"⎕IO + 4\" → 5" (mkrv (apl-run "⎕IO + 4")) (list 5)) + +(apl-test + "apl-run \"(10 20 30 40 50)[3]\" → 30" + (mkrv (apl-run "(10 20 30 40 50)[3]")) + (list 30)) + +(apl-test + "apl-run \"(⍳10)[5]\" → 5" + (mkrv (apl-run "(⍳10)[5]")) + (list 5)) + +(apl-test + "apl-run \"A ← 100 200 300 ⋄ A[2]\" → 200" + (mkrv (apl-run "A ← 100 200 300 ⋄ A[2]")) + (list 200)) + +(apl-test + "apl-run \"V ← ⍳10 ⋄ V[3]\" → 3" + (mkrv (apl-run "V ← ⍳10 ⋄ V[3]")) + (list 3)) + +(apl-test + "apl-run \"(10 20 30)[1]\" → 10 (1-indexed)" + (mkrv (apl-run "(10 20 30)[1]")) + (list 10)) + +(apl-test + "apl-run \"V ← 10 20 30 40 50 ⋄ V[3] + 1\" → 31" + (mkrv (apl-run "V ← 10 20 30 40 50 ⋄ V[3] + 1")) + (list 31)) + +(apl-test + "apl-run \"(⍳5)[3] × 7\" → 21" + (mkrv (apl-run "(⍳5)[3] × 7")) + (list 21)) diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index d9c2da80..8b8d5476 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -123,9 +123,10 @@ and tightens loose ends. `⎕name`, then handle in `apl-eval-ast` by dispatching to `apl-quad-*` runtime fns (`⎕IO`, `⎕ML`, `⎕FR`, `⎕TS`, `⎕FMT`, `⎕←`). _(`⎕←` deferred — tokenizer treats `←` as `:assign` after `⎕`.)_ -- [ ] **Bracket indexing verification** — load programs that use `A[I]` / +- [x] **Bracket indexing verification** — load programs that use `A[I]` / `A[I;J]` end-to-end; confirm parser desugars to `⌷` and runtime returns expected slices. Add 5+ tests. + _(Single-axis only — multi-axis `A[I;J]` requires semicolon parsing, deferred.)_ - [ ] **Idiom corpus expansion** — extend `idioms.sx` from 34 to 60+ once end-to-end works (we can express idioms as APL strings, not as runtime calls). Source-string-based idioms validate the whole stack. @@ -148,6 +149,7 @@ data; format for string templating. _Newest first._ +- 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 - 2026-05-07: Phase 7 step 3 — :quad-name end-to-end; tokenizer already produced :name "⎕FMT"; parser is-fn-tok? extended via apl-quad-fn-names; eval-ast :name dispatches ⎕IO/⎕ML/⎕FR/⎕TS to apl-quad-*; apl-monadic-fn handles ⎕FMT; ⎕← deferred (tokenizer splits ⎕←); +8 tests; 408/408 - 2026-05-07: Phase 7 step 2 — end-to-end pipeline `apl-run : string → array` (parse-apl + apl-eval-ast against empty env); +25 source-string tests covering scalars, strands, dyadic arith, monadic primitives, operators, ∘./.g products, comparisons, famous one-liners (+/⍳10=55, ×/⍳10=10!); tokenizer can't yet parse decimals so `3.7` literal tests dropped; **400/400** - 2026-05-07: Phase 7 step 1 — operators in apl-eval-ast via apl-resolve-monadic/dyadic; supports / ⌿ \ ⍀ ¨ ⍨ ∘. f.g; queens(8) test removed (too slow for 300s timeout); +14 eval-ops tests; 375/375