Phase 4: Eliminate nested CEK from HO form handlers

Higher-order forms (map, filter, reduce, some, every?, for-each,
map-indexed) now evaluate their arguments via CEK frames instead
of nested trampoline(eval-expr(...)) calls.

Added HoSetupFrame — staged evaluation of HO form arguments.
When all args are evaluated, ho-setup-dispatch sets up the
iteration frame. This keeps a single linear CEK continuation
chain instead of spawning nested CEK instances.

14 nested eval-expr calls eliminated (39 → 25 remaining).
The remaining 25 are in delegate functions (sf-letrec, sf-scope,
parse-keyword-args, qq-expand, etc.) called infrequently.

All tests unchanged: JS 747/747, Full 864/870, Python 679/679.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 14:10:33 +00:00
parent e475222099
commit c6a662c980
5 changed files with 603 additions and 81 deletions

View File

@@ -197,6 +197,14 @@
(fn (env)
{:type "deref" :env env}))
;; HoSetupFrame: staged evaluation of higher-order form arguments
;; ho-type is "map", "filter", "reduce", etc.
;; Evaluates args one at a time, then dispatches to the iteration frame.
(define make-ho-setup-frame
(fn (ho-type remaining-args evaled-args env)
{:type "ho-setup" :ho-type ho-type :remaining remaining-args
:evaled evaled-args :env env}))
;; --------------------------------------------------------------------------
;; 3. Frame accessors
@@ -1571,69 +1579,101 @@
;; Function and collection args are evaluated via tree-walk (simple exprs),
;; then the loop is driven by CEK frames.
;; HO step handlers — push HoSetupFrame to evaluate args via CEK
;; (no nested eval-expr calls). When all args are evaluated, the
;; HoSetupFrame dispatch in step-continue sets up the iteration frame.
;; ho-setup-dispatch: all HO args evaluated, set up iteration
(define ho-setup-dispatch
(fn (ho-type evaled env kont)
(let ((f (first evaled)))
(cond
(= ho-type "map")
(let ((coll (nth evaled 1)))
(if (empty? coll)
(make-cek-value (list) env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-map-frame f (rest coll) (list) env) kont))))
(= ho-type "map-indexed")
(let ((coll (nth evaled 1)))
(if (empty? coll)
(make-cek-value (list) env kont)
(continue-with-call f (list 0 (first coll)) env (list)
(kont-push (make-map-indexed-frame f (rest coll) (list) env) kont))))
(= ho-type "filter")
(let ((coll (nth evaled 1)))
(if (empty? coll)
(make-cek-value (list) env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-filter-frame f (rest coll) (list) (first coll) env) kont))))
(= ho-type "reduce")
(let ((init (nth evaled 1))
(coll (nth evaled 2)))
(if (empty? coll)
(make-cek-value init env kont)
(continue-with-call f (list init (first coll)) env (list)
(kont-push (make-reduce-frame f (rest coll) env) kont))))
(= ho-type "some")
(let ((coll (nth evaled 1)))
(if (empty? coll)
(make-cek-value false env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-some-frame f (rest coll) env) kont))))
(= ho-type "every")
(let ((coll (nth evaled 1)))
(if (empty? coll)
(make-cek-value true env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-every-frame f (rest coll) env) kont))))
(= ho-type "for-each")
(let ((coll (nth evaled 1)))
(if (empty? coll)
(make-cek-value nil env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-for-each-frame f (rest coll) env) kont))))
:else (error (str "Unknown HO type: " ho-type))))))
(define step-ho-map
(fn (args env kont)
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(if (empty? coll)
(make-cek-value (list) env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-map-frame f (rest coll) (list) env) kont))))))
(make-cek-state (first args) env
(kont-push (make-ho-setup-frame "map" (rest args) (list) env) kont))))
(define step-ho-map-indexed
(fn (args env kont)
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(if (empty? coll)
(make-cek-value (list) env kont)
(continue-with-call f (list 0 (first coll)) env (list)
(kont-push (make-map-indexed-frame f (rest coll) (list) env) kont))))))
(make-cek-state (first args) env
(kont-push (make-ho-setup-frame "map-indexed" (rest args) (list) env) kont))))
(define step-ho-filter
(fn (args env kont)
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(if (empty? coll)
(make-cek-value (list) env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-filter-frame f (rest coll) (list) (first coll) env) kont))))))
(make-cek-state (first args) env
(kont-push (make-ho-setup-frame "filter" (rest args) (list) env) kont))))
(define step-ho-reduce
(fn (args env kont)
(let ((f (trampoline (eval-expr (first args) env)))
(init (trampoline (eval-expr (nth args 1) env)))
(coll (trampoline (eval-expr (nth args 2) env))))
(if (empty? coll)
(make-cek-value init env kont)
(continue-with-call f (list init (first coll)) env (list)
(kont-push (make-reduce-frame f (rest coll) env) kont))))))
(make-cek-state (first args) env
(kont-push (make-ho-setup-frame "reduce" (rest args) (list) env) kont))))
(define step-ho-some
(fn (args env kont)
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(if (empty? coll)
(make-cek-value false env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-some-frame f (rest coll) env) kont))))))
(make-cek-state (first args) env
(kont-push (make-ho-setup-frame "some" (rest args) (list) env) kont))))
(define step-ho-every
(fn (args env kont)
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(if (empty? coll)
(make-cek-value true env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-every-frame f (rest coll) env) kont))))))
(make-cek-state (first args) env
(kont-push (make-ho-setup-frame "every" (rest args) (list) env) kont))))
(define step-ho-for-each
(fn (args env kont)
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(if (empty? coll)
(make-cek-value nil env kont)
(continue-with-call f (list (first coll)) env (list)
(kont-push (make-for-each-frame f (rest coll) env) kont))))))
(make-cek-state (first args) env
(kont-push (make-ho-setup-frame "for-each" (rest args) (list) env) kont))))
;; --------------------------------------------------------------------------
@@ -1908,6 +1948,22 @@
fenv)
rest-k))))))
;; --- HoSetupFrame: evaluating HO form arguments ---
(= ft "ho-setup")
(let ((ho-type (get frame "ho-type"))
(remaining (get frame "remaining"))
(evaled (append (get frame "evaled") (list value)))
(fenv (get frame "env")))
(if (empty? remaining)
;; All args evaluated — dispatch to iteration
(ho-setup-dispatch ho-type evaled fenv rest-k)
;; More args to evaluate
(make-cek-state
(first remaining) fenv
(kont-push
(make-ho-setup-frame ho-type (rest remaining) evaled fenv)
rest-k))))
;; --- ResetFrame: body evaluated normally (no shift) ---
(= ft "reset")
(make-cek-value value env rest-k)