apl: named function definitions f ← {…} (+7 tests, 467/467)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Parser: apl-collect-fn-bindings pre-scans stmt-groups for
`name ← { ... }` patterns and populates apl-known-fn-names.
is-fn-tok? consults this list; collect-segments-loop emits
(:fn-name nm) for known names so they parse as functions.
Resolver: apl-resolve-{monadic,dyadic} handle :fn-name by
looking up env, asserting the binding is a dfn, returning
a closure that dispatches to apl-call-dfn{-m,}.
Recursion still works: `fact ← {0=⍵:1 ⋄ ⍵×∇⍵-1} ⋄ fact 5` → 120.
This commit is contained in:
@@ -84,20 +84,46 @@
|
|||||||
|
|
||||||
(define apl-quad-fn-names (list "⎕FMT" "⎕←"))
|
(define apl-quad-fn-names (list "⎕FMT" "⎕←"))
|
||||||
|
|
||||||
(define
|
(define apl-known-fn-names (list))
|
||||||
apl-parse-op-glyph?
|
|
||||||
(fn (v) (some (fn (g) (= g v)) apl-parse-op-glyphs)))
|
|
||||||
|
|
||||||
; ============================================================
|
; ============================================================
|
||||||
; Token accessors
|
; Token accessors
|
||||||
; ============================================================
|
; ============================================================
|
||||||
|
|
||||||
|
(define
|
||||||
|
apl-collect-fn-bindings
|
||||||
|
(fn
|
||||||
|
(stmt-groups)
|
||||||
|
(set! apl-known-fn-names (list))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
(toks)
|
||||||
|
(when
|
||||||
|
(and
|
||||||
|
(>= (len toks) 3)
|
||||||
|
(= (tok-type (nth toks 0)) :name)
|
||||||
|
(= (tok-type (nth toks 1)) :assign)
|
||||||
|
(= (tok-type (nth toks 2)) :lbrace))
|
||||||
|
(set!
|
||||||
|
apl-known-fn-names
|
||||||
|
(cons (tok-val (nth toks 0)) apl-known-fn-names))))
|
||||||
|
stmt-groups)))
|
||||||
|
|
||||||
|
(define
|
||||||
|
apl-parse-op-glyph?
|
||||||
|
(fn (v) (some (fn (g) (= g v)) apl-parse-op-glyphs)))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
apl-parse-fn-glyph?
|
apl-parse-fn-glyph?
|
||||||
(fn (v) (some (fn (g) (= g v)) apl-parse-fn-glyphs)))
|
(fn (v) (some (fn (g) (= g v)) apl-parse-fn-glyphs)))
|
||||||
|
|
||||||
(define tok-type (fn (tok) (get tok :type)))
|
(define tok-type (fn (tok) (get tok :type)))
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; Collect trailing operators starting at index i
|
||||||
|
; Returns {:ops (op ...) :end new-i}
|
||||||
|
; ============================================================
|
||||||
|
|
||||||
(define tok-val (fn (tok) (get tok :value)))
|
(define tok-val (fn (tok) (get tok :value)))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -107,8 +133,8 @@
|
|||||||
(and (= (tok-type tok) :glyph) (apl-parse-op-glyph? (tok-val tok)))))
|
(and (= (tok-type tok) :glyph) (apl-parse-op-glyph? (tok-val tok)))))
|
||||||
|
|
||||||
; ============================================================
|
; ============================================================
|
||||||
; Collect trailing operators starting at index i
|
; Build a derived-fn node by chaining operators left-to-right
|
||||||
; Returns {:ops (op ...) :end new-i}
|
; (+/¨ → (:derived-fn "¨" (:derived-fn "/" (:fn-glyph "+"))))
|
||||||
; ============================================================
|
; ============================================================
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -119,15 +145,17 @@
|
|||||||
(and (= (tok-type tok) :glyph) (apl-parse-fn-glyph? (tok-val tok)))
|
(and (= (tok-type tok) :glyph) (apl-parse-fn-glyph? (tok-val tok)))
|
||||||
(and
|
(and
|
||||||
(= (tok-type tok) :name)
|
(= (tok-type tok) :name)
|
||||||
(some (fn (q) (= q (tok-val tok))) apl-quad-fn-names)))))
|
(or
|
||||||
|
(some (fn (q) (= q (tok-val tok))) apl-quad-fn-names)
|
||||||
|
(some (fn (q) (= q (tok-val tok))) apl-known-fn-names))))))
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; Find matching close bracket/paren/brace
|
||||||
|
; Returns the index of the matching close token
|
||||||
|
; ============================================================
|
||||||
|
|
||||||
(define collect-ops (fn (tokens i) (collect-ops-loop tokens i (list))))
|
(define collect-ops (fn (tokens i) (collect-ops-loop tokens i (list))))
|
||||||
|
|
||||||
; ============================================================
|
|
||||||
; Build a derived-fn node by chaining operators left-to-right
|
|
||||||
; (+/¨ → (:derived-fn "¨" (:derived-fn "/" (:fn-glyph "+"))))
|
|
||||||
; ============================================================
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
collect-ops-loop
|
collect-ops-loop
|
||||||
(fn
|
(fn
|
||||||
@@ -143,8 +171,10 @@
|
|||||||
{:end i :ops acc})))))
|
{:end i :ops acc})))))
|
||||||
|
|
||||||
; ============================================================
|
; ============================================================
|
||||||
; Find matching close bracket/paren/brace
|
; Segment collection: scan tokens left-to-right, building
|
||||||
; Returns the index of the matching close token
|
; a list of {:kind "val"/"fn" :node ast} segments.
|
||||||
|
; Operators following function glyphs are merged into
|
||||||
|
; derived-fn nodes during this pass.
|
||||||
; ============================================================
|
; ============================================================
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -163,12 +193,20 @@
|
|||||||
(find-matching-close-loop tokens start open-type close-type 1)))
|
(find-matching-close-loop tokens start open-type close-type 1)))
|
||||||
|
|
||||||
; ============================================================
|
; ============================================================
|
||||||
; Segment collection: scan tokens left-to-right, building
|
; Build tree from segment list
|
||||||
; a list of {:kind "val"/"fn" :node ast} segments.
|
;
|
||||||
; Operators following function glyphs are merged into
|
; The segments are in left-to-right order.
|
||||||
; derived-fn nodes during this pass.
|
; APL evaluates right-to-left, so the LEFTMOST function is
|
||||||
|
; the outermost (last-evaluated) node.
|
||||||
|
;
|
||||||
|
; Patterns:
|
||||||
|
; [val] → val node
|
||||||
|
; [fn val ...] → (:monad fn (build-tree rest))
|
||||||
|
; [val fn val ...] → (:dyad fn val (build-tree rest))
|
||||||
|
; [val val ...] → (:vec val1 val2 ...) — strand
|
||||||
; ============================================================
|
; ============================================================
|
||||||
|
|
||||||
|
; Find the index of the first function segment (returns -1 if none)
|
||||||
(define
|
(define
|
||||||
find-matching-close-loop
|
find-matching-close-loop
|
||||||
(fn
|
(fn
|
||||||
@@ -208,21 +246,9 @@
|
|||||||
collect-segments
|
collect-segments
|
||||||
(fn (tokens) (collect-segments-loop tokens 0 (list))))
|
(fn (tokens) (collect-segments-loop tokens 0 (list))))
|
||||||
|
|
||||||
; ============================================================
|
; Build an array node from 0..n value segments
|
||||||
; Build tree from segment list
|
; If n=1 → return that segment's node
|
||||||
;
|
; If n>1 → return (:vec node1 node2 ...)
|
||||||
; The segments are in left-to-right order.
|
|
||||||
; APL evaluates right-to-left, so the LEFTMOST function is
|
|
||||||
; the outermost (last-evaluated) node.
|
|
||||||
;
|
|
||||||
; Patterns:
|
|
||||||
; [val] → val node
|
|
||||||
; [fn val ...] → (:monad fn (build-tree rest))
|
|
||||||
; [val fn val ...] → (:dyad fn val (build-tree rest))
|
|
||||||
; [val val ...] → (:vec val1 val2 ...) — strand
|
|
||||||
; ============================================================
|
|
||||||
|
|
||||||
; Find the index of the first function segment (returns -1 if none)
|
|
||||||
(define
|
(define
|
||||||
collect-segments-loop
|
collect-segments-loop
|
||||||
(fn
|
(fn
|
||||||
@@ -242,24 +268,38 @@
|
|||||||
((= tt :str)
|
((= tt :str)
|
||||||
(collect-segments-loop tokens (+ i 1) (append acc {:kind "val" :node (list :str tv)})))
|
(collect-segments-loop tokens (+ i 1) (append acc {:kind "val" :node (list :str tv)})))
|
||||||
((= tt :name)
|
((= tt :name)
|
||||||
(if
|
(cond
|
||||||
(some (fn (q) (= q tv)) apl-quad-fn-names)
|
((some (fn (q) (= q tv)) apl-quad-fn-names)
|
||||||
(let
|
|
||||||
((op-result (collect-ops tokens (+ i 1))))
|
|
||||||
(let
|
(let
|
||||||
((ops (get op-result :ops)) (ni (get op-result :end)))
|
((op-result (collect-ops tokens (+ i 1))))
|
||||||
(let
|
(let
|
||||||
((fn-node (build-derived-fn (list :fn-glyph tv) ops)))
|
((ops (get op-result :ops))
|
||||||
(collect-segments-loop
|
(ni (get op-result :end)))
|
||||||
tokens
|
(let
|
||||||
ni
|
((fn-node (build-derived-fn (list :fn-glyph tv) ops)))
|
||||||
(append acc {:kind "fn" :node fn-node})))))
|
(collect-segments-loop
|
||||||
(let
|
tokens
|
||||||
((br (maybe-bracket (list :name tv) tokens (+ i 1))))
|
ni
|
||||||
(collect-segments-loop
|
(append acc {:kind "fn" :node fn-node}))))))
|
||||||
tokens
|
((some (fn (q) (= q tv)) apl-known-fn-names)
|
||||||
(nth br 1)
|
(let
|
||||||
(append acc {:kind "val" :node (nth br 0)})))))
|
((op-result (collect-ops tokens (+ i 1))))
|
||||||
|
(let
|
||||||
|
((ops (get op-result :ops))
|
||||||
|
(ni (get op-result :end)))
|
||||||
|
(let
|
||||||
|
((fn-node (build-derived-fn (list :fn-name tv) ops)))
|
||||||
|
(collect-segments-loop
|
||||||
|
tokens
|
||||||
|
ni
|
||||||
|
(append acc {:kind "fn" :node fn-node}))))))
|
||||||
|
(else
|
||||||
|
(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)
|
((= tt :lparen)
|
||||||
(let
|
(let
|
||||||
((end (find-matching-close tokens (+ i 1) :lparen :rparen)))
|
((end (find-matching-close tokens (+ i 1) :lparen :rparen)))
|
||||||
@@ -346,9 +386,12 @@
|
|||||||
|
|
||||||
(define find-first-fn (fn (segs) (find-first-fn-loop segs 0)))
|
(define find-first-fn (fn (segs) (find-first-fn-loop segs 0)))
|
||||||
|
|
||||||
; Build an array node from 0..n value segments
|
|
||||||
; If n=1 → return that segment's node
|
; ============================================================
|
||||||
; If n>1 → return (:vec node1 node2 ...)
|
; Split token list on statement separators (diamond / newline)
|
||||||
|
; Only splits at depth 0 (ignores separators inside { } or ( ) )
|
||||||
|
; ============================================================
|
||||||
|
|
||||||
(define
|
(define
|
||||||
find-first-fn-loop
|
find-first-fn-loop
|
||||||
(fn
|
(fn
|
||||||
@@ -370,10 +413,9 @@
|
|||||||
(get (first segs) :node)
|
(get (first segs) :node)
|
||||||
(cons :vec (map (fn (s) (get s :node)) segs)))))
|
(cons :vec (map (fn (s) (get s :node)) segs)))))
|
||||||
|
|
||||||
|
|
||||||
; ============================================================
|
; ============================================================
|
||||||
; Split token list on statement separators (diamond / newline)
|
; Parse a dfn body (tokens between { and })
|
||||||
; Only splits at depth 0 (ignores separators inside { } or ( ) )
|
; Handles guard expressions: cond : expr
|
||||||
; ============================================================
|
; ============================================================
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -408,11 +450,6 @@
|
|||||||
split-statements
|
split-statements
|
||||||
(fn (tokens) (split-statements-loop tokens (list) (list) 0)))
|
(fn (tokens) (split-statements-loop tokens (list) (list) 0)))
|
||||||
|
|
||||||
; ============================================================
|
|
||||||
; Parse a dfn body (tokens between { and })
|
|
||||||
; Handles guard expressions: cond : expr
|
|
||||||
; ============================================================
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
split-statements-loop
|
split-statements-loop
|
||||||
(fn
|
(fn
|
||||||
@@ -467,6 +504,10 @@
|
|||||||
((stmt-groups (split-statements tokens)))
|
((stmt-groups (split-statements tokens)))
|
||||||
(let ((stmts (map parse-dfn-stmt stmt-groups))) (cons :dfn stmts)))))
|
(let ((stmts (map parse-dfn-stmt stmt-groups))) (cons :dfn stmts)))))
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; Parse a single statement (assignment or expression)
|
||||||
|
; ============================================================
|
||||||
|
|
||||||
(define
|
(define
|
||||||
parse-dfn-stmt
|
parse-dfn-stmt
|
||||||
(fn
|
(fn
|
||||||
@@ -483,12 +524,17 @@
|
|||||||
(parse-apl-expr body-tokens)))
|
(parse-apl-expr body-tokens)))
|
||||||
(parse-stmt tokens)))))
|
(parse-stmt tokens)))))
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; Parse an expression from a flat token list
|
||||||
|
; ============================================================
|
||||||
|
|
||||||
(define
|
(define
|
||||||
find-top-level-colon
|
find-top-level-colon
|
||||||
(fn (tokens i) (find-top-level-colon-loop tokens i 0)))
|
(fn (tokens i) (find-top-level-colon-loop tokens i 0)))
|
||||||
|
|
||||||
; ============================================================
|
; ============================================================
|
||||||
; Parse a single statement (assignment or expression)
|
; Main entry point
|
||||||
|
; parse-apl: string → AST
|
||||||
; ============================================================
|
; ============================================================
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -508,10 +554,6 @@
|
|||||||
((and (= tt :colon) (= depth 0)) i)
|
((and (= tt :colon) (= depth 0)) i)
|
||||||
(true (find-top-level-colon-loop tokens (+ i 1) depth)))))))
|
(true (find-top-level-colon-loop tokens (+ i 1) depth)))))))
|
||||||
|
|
||||||
; ============================================================
|
|
||||||
; Parse an expression from a flat token list
|
|
||||||
; ============================================================
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
parse-stmt
|
parse-stmt
|
||||||
(fn
|
(fn
|
||||||
@@ -526,11 +568,6 @@
|
|||||||
(parse-apl-expr (slice tokens 2)))
|
(parse-apl-expr (slice tokens 2)))
|
||||||
(parse-apl-expr tokens))))
|
(parse-apl-expr tokens))))
|
||||||
|
|
||||||
; ============================================================
|
|
||||||
; Main entry point
|
|
||||||
; parse-apl: string → AST
|
|
||||||
; ============================================================
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
parse-apl-expr
|
parse-apl-expr
|
||||||
(fn
|
(fn
|
||||||
@@ -547,13 +584,15 @@
|
|||||||
((tokens (apl-tokenize src)))
|
((tokens (apl-tokenize src)))
|
||||||
(let
|
(let
|
||||||
((stmt-groups (split-statements tokens)))
|
((stmt-groups (split-statements tokens)))
|
||||||
(if
|
(begin
|
||||||
(= (len stmt-groups) 0)
|
(apl-collect-fn-bindings stmt-groups)
|
||||||
nil
|
|
||||||
(if
|
(if
|
||||||
(= (len stmt-groups) 1)
|
(= (len stmt-groups) 0)
|
||||||
(parse-stmt (first stmt-groups))
|
nil
|
||||||
(cons :program (map parse-stmt stmt-groups))))))))
|
(if
|
||||||
|
(= (len stmt-groups) 1)
|
||||||
|
(parse-stmt (first stmt-groups))
|
||||||
|
(cons :program (map parse-stmt stmt-groups)))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
maybe-bracket
|
maybe-bracket
|
||||||
|
|||||||
@@ -207,3 +207,38 @@
|
|||||||
(apl-test "string: 'a' is rank-0 scalar" (mksh (apl-run "'a'")) (list))
|
(apl-test "string: 'a' is rank-0 scalar" (mksh (apl-run "'a'")) (list))
|
||||||
|
|
||||||
(apl-test "string: 'hello' shape (5)" (mksh (apl-run "'hello'")) (list 5))
|
(apl-test "string: 'hello' shape (5)" (mksh (apl-run "'hello'")) (list 5))
|
||||||
|
|
||||||
|
(apl-test
|
||||||
|
"named-fn: f ← {⍺+⍵} ⋄ 3 f 4 → 7"
|
||||||
|
(mkrv (apl-run "f ← {⍺+⍵} ⋄ 3 f 4"))
|
||||||
|
(list 7))
|
||||||
|
|
||||||
|
(apl-test
|
||||||
|
"named-fn monadic: sq ← {⍵×⍵} ⋄ sq 7 → 49"
|
||||||
|
(mkrv (apl-run "sq ← {⍵×⍵} ⋄ sq 7"))
|
||||||
|
(list 49))
|
||||||
|
|
||||||
|
(apl-test
|
||||||
|
"named-fn dyadic: hyp ← {((⍺×⍺)+⍵×⍵)} ⋄ 3 hyp 4 → 25"
|
||||||
|
(mkrv (apl-run "hyp ← {((⍺×⍺)+⍵×⍵)} ⋄ 3 hyp 4"))
|
||||||
|
(list 25))
|
||||||
|
|
||||||
|
(apl-test
|
||||||
|
"named-fn: dbl ← {⍵+⍵} ⋄ dbl ⍳5"
|
||||||
|
(mkrv (apl-run "dbl ← {⍵+⍵} ⋄ dbl ⍳5"))
|
||||||
|
(list 2 4 6 8 10))
|
||||||
|
|
||||||
|
(apl-test
|
||||||
|
"named-fn factorial via ∇ recursion"
|
||||||
|
(mkrv (apl-run "fact ← {0=⍵:1 ⋄ ⍵×∇⍵-1} ⋄ fact 5"))
|
||||||
|
(list 120))
|
||||||
|
|
||||||
|
(apl-test
|
||||||
|
"named-fn used twice in expr: dbl ← {⍵+⍵} ⋄ (dbl 3) + dbl 4"
|
||||||
|
(mkrv (apl-run "dbl ← {⍵+⍵} ⋄ (dbl 3) + dbl 4"))
|
||||||
|
(list 14))
|
||||||
|
|
||||||
|
(apl-test
|
||||||
|
"named-fn with vector arg: neg ← {-⍵} ⋄ neg 1 2 3"
|
||||||
|
(mkrv (apl-run "neg ← {-⍵} ⋄ neg 1 2 3"))
|
||||||
|
(list -1 -2 -3))
|
||||||
|
|||||||
@@ -429,6 +429,18 @@
|
|||||||
((f (apl-resolve-dyadic inner env)))
|
((f (apl-resolve-dyadic inner env)))
|
||||||
(fn (arr) (apl-commute f arr))))
|
(fn (arr) (apl-commute f arr))))
|
||||||
(else (error "apl-resolve-monadic: unsupported op")))))
|
(else (error "apl-resolve-monadic: unsupported op")))))
|
||||||
|
((= tag :fn-name)
|
||||||
|
(let
|
||||||
|
((nm (nth fn-node 1)))
|
||||||
|
(let
|
||||||
|
((bound (get env nm)))
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(list? bound)
|
||||||
|
(> (len bound) 0)
|
||||||
|
(= (first bound) :dfn))
|
||||||
|
(fn (arg) (apl-call-dfn-m bound arg))
|
||||||
|
(error "apl-resolve-monadic: name not bound to dfn")))))
|
||||||
(else (error "apl-resolve-monadic: unknown fn-node tag"))))))
|
(else (error "apl-resolve-monadic: unknown fn-node tag"))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -452,6 +464,18 @@
|
|||||||
((f (apl-resolve-dyadic inner env)))
|
((f (apl-resolve-dyadic inner env)))
|
||||||
(fn (a b) (apl-commute-dyadic f a b))))
|
(fn (a b) (apl-commute-dyadic f a b))))
|
||||||
(else (error "apl-resolve-dyadic: unsupported op")))))
|
(else (error "apl-resolve-dyadic: unsupported op")))))
|
||||||
|
((= tag :fn-name)
|
||||||
|
(let
|
||||||
|
((nm (nth fn-node 1)))
|
||||||
|
(let
|
||||||
|
((bound (get env nm)))
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(list? bound)
|
||||||
|
(> (len bound) 0)
|
||||||
|
(= (first bound) :dfn))
|
||||||
|
(fn (a b) (apl-call-dfn bound a b))
|
||||||
|
(error "apl-resolve-dyadic: name not bound to dfn")))))
|
||||||
((= tag :outer)
|
((= tag :outer)
|
||||||
(let
|
(let
|
||||||
((inner (nth fn-node 2)))
|
((inner (nth fn-node 2)))
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ programs run from source, and starts pushing on performance.
|
|||||||
a single `:name "⎕←"` token (don't split on the assign glyph);
|
a single `:name "⎕←"` token (don't split on the assign glyph);
|
||||||
- string values in `apl-eval-ast` — handle `:str` (parser already produces
|
- string values in `apl-eval-ast` — handle `:str` (parser already produces
|
||||||
them) by wrapping into a vector of character codes (or rank-0 string).
|
them) by wrapping into a vector of character codes (or rank-0 string).
|
||||||
- [ ] **Named function definitions** — `f ← {⍺+⍵} ⋄ 1 f 2` and `2 f 3`.
|
- [x] **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;
|
- 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;
|
- 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;
|
- parser: a name in fn-position whose env value is a dfn dispatches as a fn;
|
||||||
@@ -186,6 +186,7 @@ data; format for string templating.
|
|||||||
|
|
||||||
_Newest first._
|
_Newest first._
|
||||||
|
|
||||||
|
- 2026-05-07: Phase 8 step 2 — named function defs end-to-end via parser pre-scan; apl-known-fn-names + apl-collect-fn-bindings detect `name ← {...}` patterns; collect-segments-loop emits :fn-name for known names; resolver looks up env for :fn-name; supports recursion (∇ in named dfn); +7 tests including fact via ∇; 467/467
|
||||||
- 2026-05-07: Phase 8 step 1 — quick-wins bundle: decimal literals (3.7, ¯2.5), ⎕← passthrough as monadic fn (single-token via tokenizer special-case), :str AST in eval-ast (single-char→scalar, multi-char→vec); +10 tests; 460/460
|
- 2026-05-07: Phase 8 step 1 — quick-wins bundle: decimal literals (3.7, ¯2.5), ⎕← passthrough as monadic fn (single-token via tokenizer special-case), :str AST in eval-ast (single-char→scalar, multi-char→vec); +10 tests; 460/460
|
||||||
- 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 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 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
|
||||||
|
|||||||
Reference in New Issue
Block a user