erlang: list comprehensions (+12 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-25 06:19:14 +00:00
parent 8e809614ba
commit 193b0c04be
6 changed files with 172 additions and 10 deletions

View File

@@ -123,6 +123,7 @@
(= ty "send") (er-eval-send node env)
(= ty "receive") (er-eval-receive node env)
(= ty "try") (er-eval-try node env)
(= ty "lc") (er-eval-lc node env)
(= ty "match") (er-eval-match node env)
:else (error (str "Erlang eval: unsupported node type '" ty "'"))))))
@@ -1281,3 +1282,84 @@
(do
(er-env-restore! env snap)
(er-eval-of-clauses clauses subject env (+ i 1))))))))
;; ── list comprehensions ─────────────────────────────────────────
;; `[E || Pat <- Source, FilterExpr, ...]`. Walk qualifiers in order:
;; generators iterate their source list and bind the pattern (with
;; env snapshot/restore so each iteration starts from the same
;; baseline); filters skip when falsy. At the end of the qualifier
;; chain, evaluate `head` and append to the accumulator. Build the
;; final cons chain in O(n) with a single right-fold.
(define
er-eval-lc
(fn
(node env)
(let
((acc (list)))
(er-lc-walk (get node :qualifiers) 0 (get node :head) env acc)
(er-list-from-sx-list acc))))
(define
er-lc-walk
(fn
(quals i head env acc)
(if
(>= i (len quals))
(append! acc (er-eval-expr head env))
(let
((q (nth quals i)))
(cond
(= (get q :kind) "gen")
(let
((src (er-eval-expr (get q :source) env)))
(er-lc-iter-gen
src
(get q :pattern)
quals
i
head
env
acc))
(= (get q :kind) "filter")
(when
(er-truthy? (er-eval-expr (get q :expr) env))
(er-lc-walk quals (+ i 1) head env acc))
:else (error "Erlang LC: unknown qualifier"))))))
(define
er-lc-iter-gen
(fn
(src pat quals i head env acc)
(cond
(er-nil? src) nil
(er-cons? src)
(let
((snap (er-env-copy env)))
(when
(er-match! pat (get src :head) env)
(er-lc-walk quals (+ i 1) head env acc))
(er-env-restore! env snap)
(er-lc-iter-gen
(get src :tail)
pat
quals
i
head
env
acc))
:else (error "Erlang LC: generator source is not a list"))))
(define
er-list-from-sx-list
(fn
(xs)
(let
((acc (list (er-mk-nil))))
(for-each
(fn
(i)
(let
((j (- (- (len xs) 1) i)))
(set-nth! acc 0 (er-mk-cons (nth xs j) (nth acc 0)))))
(range 0 (len xs)))
(nth acc 0))))