Data-first HO forms, fix plan pages, aser error handling (1080/1080)
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Evaluator: data-first higher-order forms — ho-swap-args auto-detects (map coll fn) vs (map fn coll), both work. Threading + HO: (-> data (map fn)) dispatches through CEK HO machinery via quoted-value splice. 17 new tests in test-cek-advanced.sx. Fix plan pages: add mother-language, isolated-evaluator, rust-wasm-host to page-functions.sx plan() — were in defpage but missing from URL router. Aser error handling: pages.py now catches EvalError separately, renders visible error banner instead of silently sending empty content. All except blocks include traceback in logs. Scope primitives: register collect!/collected/clear-collected!/emitted/ emit!/context in shared/sx/primitives.py so hand-written _aser can resolve them (fixes ~cssx/flush expansion failure). New test file: shared/sx/tests/test_aser_errors.py — 19 pytest tests for error propagation through all aser control flow forms. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1684,62 +1684,91 @@
|
||||
;; (no nested eval-expr calls). When all args are evaluated, the
|
||||
;; HoSetupFrame dispatch in step-continue sets up the iteration frame.
|
||||
|
||||
;; ho-form-name? — is this symbol name a higher-order special form?
|
||||
(define ho-form-name?
|
||||
(fn (name)
|
||||
(or (= name "map") (= name "map-indexed") (= name "filter")
|
||||
(= name "reduce") (= name "some") (= name "every?")
|
||||
(= name "for-each"))))
|
||||
|
||||
;; ho-fn? — is this value usable as a HO callback?
|
||||
(define ho-fn?
|
||||
(fn (v) (or (callable? v) (lambda? v))))
|
||||
|
||||
;; ho-swap-args: normalise data-first arg order
|
||||
;; 2-arg forms: (coll fn) → (fn coll)
|
||||
;; 3-arg reduce: (coll fn init) → (fn init coll)
|
||||
(define ho-swap-args
|
||||
(fn (ho-type evaled)
|
||||
(if (= ho-type "reduce")
|
||||
(let ((a (first evaled))
|
||||
(b (nth evaled 1)))
|
||||
(if (and (not (ho-fn? a)) (ho-fn? b))
|
||||
(list b (nth evaled 2) a)
|
||||
evaled))
|
||||
(let ((a (first evaled))
|
||||
(b (nth evaled 1)))
|
||||
(if (and (not (ho-fn? a)) (ho-fn? b))
|
||||
(list b a)
|
||||
evaled)))))
|
||||
|
||||
;; ho-setup-dispatch: all HO args evaluated, set up iteration
|
||||
(define ho-setup-dispatch
|
||||
(fn (ho-type evaled env kont)
|
||||
(let ((f (first evaled)))
|
||||
(let ((ordered (ho-swap-args ho-type evaled)))
|
||||
(let ((f (first ordered)))
|
||||
(cond
|
||||
(= ho-type "map")
|
||||
(let ((coll (nth evaled 1)))
|
||||
(let ((coll (nth ordered 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)))
|
||||
(let ((coll (nth ordered 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)))
|
||||
(let ((coll (nth ordered 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)))
|
||||
(let ((init (nth ordered 1))
|
||||
(coll (nth ordered 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)))
|
||||
(let ((coll (nth ordered 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)))
|
||||
(let ((coll (nth ordered 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)))
|
||||
(let ((coll (nth ordered 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))))))
|
||||
:else (error (str "Unknown HO type: " ho-type)))))))
|
||||
|
||||
(define step-ho-map
|
||||
(fn (args env kont)
|
||||
@@ -1965,24 +1994,36 @@
|
||||
(make-cek-value value fenv rest-k)
|
||||
;; Apply next form to value
|
||||
(let ((form (first remaining))
|
||||
(rest-forms (rest remaining)))
|
||||
(let ((result (if (= (type-of form) "list")
|
||||
(let ((f (trampoline (eval-expr (first form) fenv)))
|
||||
(rargs (map (fn (a) (trampoline (eval-expr a fenv))) (rest form)))
|
||||
(all-args (cons value rargs)))
|
||||
(cond
|
||||
(and (callable? f) (not (lambda? f))) (apply f all-args)
|
||||
(lambda? f) (trampoline (call-lambda f all-args fenv))
|
||||
:else (error (str "-> form not callable: " (inspect f)))))
|
||||
(let ((f (trampoline (eval-expr form fenv))))
|
||||
(cond
|
||||
(and (callable? f) (not (lambda? f))) (f value)
|
||||
(lambda? f) (trampoline (call-lambda f (list value) fenv))
|
||||
:else (error (str "-> form not callable: " (inspect f))))))))
|
||||
(if (empty? rest-forms)
|
||||
(make-cek-value result fenv rest-k)
|
||||
(make-cek-value result fenv
|
||||
(kont-push (make-thread-frame rest-forms fenv) rest-k)))))))
|
||||
(rest-forms (rest remaining))
|
||||
(new-kont (if (empty? (rest remaining)) rest-k
|
||||
(kont-push (make-thread-frame (rest remaining) fenv) rest-k))))
|
||||
;; Check if form is a HO call like (map fn)
|
||||
(if (and (= (type-of form) "list")
|
||||
(not (empty? form))
|
||||
(= (type-of (first form)) "symbol")
|
||||
(ho-form-name? (symbol-name (first form))))
|
||||
;; HO form — splice value as quoted arg, dispatch via CEK
|
||||
(make-cek-state
|
||||
(cons (first form) (cons (list 'quote value) (rest form)))
|
||||
fenv new-kont)
|
||||
;; Normal: tree-walk eval + apply
|
||||
(let ((result (if (= (type-of form) "list")
|
||||
(let ((f (trampoline (eval-expr (first form) fenv)))
|
||||
(rargs (map (fn (a) (trampoline (eval-expr a fenv))) (rest form)))
|
||||
(all-args (cons value rargs)))
|
||||
(cond
|
||||
(and (callable? f) (not (lambda? f))) (apply f all-args)
|
||||
(lambda? f) (trampoline (call-lambda f all-args fenv))
|
||||
:else (error (str "-> form not callable: " (inspect f)))))
|
||||
(let ((f (trampoline (eval-expr form fenv))))
|
||||
(cond
|
||||
(and (callable? f) (not (lambda? f))) (f value)
|
||||
(lambda? f) (trampoline (call-lambda f (list value) fenv))
|
||||
:else (error (str "-> form not callable: " (inspect f))))))))
|
||||
(if (empty? rest-forms)
|
||||
(make-cek-value result fenv rest-k)
|
||||
(make-cek-value result fenv
|
||||
(kont-push (make-thread-frame rest-forms fenv) rest-k))))))))
|
||||
|
||||
;; --- ArgFrame: head or arg evaluated ---
|
||||
(= ft "arg")
|
||||
|
||||
@@ -598,3 +598,100 @@
|
||||
n
|
||||
(+ (fib (- n 1)) (fib (- n 2))))))
|
||||
(fib 7))"))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 8. Data-first higher-order forms
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defsuite "data-first-ho"
|
||||
(deftest "map — data-first arg order"
|
||||
(assert-equal (list 2 4 6)
|
||||
(map (list 1 2 3) (fn (x) (* x 2)))))
|
||||
|
||||
(deftest "filter — data-first arg order"
|
||||
(assert-equal (list 3 4 5)
|
||||
(filter (list 1 2 3 4 5) (fn (x) (> x 2)))))
|
||||
|
||||
(deftest "reduce — data-first arg order"
|
||||
(assert-equal 10
|
||||
(reduce (list 1 2 3 4) + 0)))
|
||||
|
||||
(deftest "some — data-first arg order"
|
||||
(assert-true
|
||||
(some (list 1 2 3) (fn (x) (> x 2))))
|
||||
(assert-false
|
||||
(some (list 1 2 3) (fn (x) (> x 5)))))
|
||||
|
||||
(deftest "every? — data-first arg order"
|
||||
(assert-true
|
||||
(every? (list 2 4 6) (fn (x) (> x 1))))
|
||||
(assert-false
|
||||
(every? (list 2 4 6) (fn (x) (> x 3)))))
|
||||
|
||||
(deftest "for-each — data-first arg order"
|
||||
(let ((acc (list)))
|
||||
(for-each (list 10 20 30)
|
||||
(fn (x) (set! acc (append acc (list x)))))
|
||||
(assert-equal (list 10 20 30) acc)))
|
||||
|
||||
(deftest "map-indexed — data-first arg order"
|
||||
(assert-equal (list "0:a" "1:b" "2:c")
|
||||
(map-indexed (list "a" "b" "c")
|
||||
(fn (i v) (str i ":" v)))))
|
||||
|
||||
(deftest "fn-first still works — map"
|
||||
(assert-equal (list 2 4 6)
|
||||
(map (fn (x) (* x 2)) (list 1 2 3))))
|
||||
|
||||
(deftest "fn-first still works — reduce"
|
||||
(assert-equal 10
|
||||
(reduce + 0 (list 1 2 3 4)))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 9. Threading with HO forms
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defsuite "thread-ho"
|
||||
(deftest "-> map"
|
||||
(assert-equal (list 2 4 6)
|
||||
(-> (list 1 2 3) (map (fn (x) (* x 2))))))
|
||||
|
||||
(deftest "-> filter"
|
||||
(assert-equal (list 3 4 5)
|
||||
(-> (list 1 2 3 4 5) (filter (fn (x) (> x 2))))))
|
||||
|
||||
(deftest "-> filter then map pipeline"
|
||||
(assert-equal (list 30 40 50)
|
||||
(-> (list 1 2 3 4 5)
|
||||
(filter (fn (x) (> x 2)))
|
||||
(map (fn (x) (* x 10))))))
|
||||
|
||||
(deftest "-> reduce"
|
||||
(assert-equal 15
|
||||
(-> (list 1 2 3 4 5) (reduce + 0))))
|
||||
|
||||
(deftest "-> map then reduce"
|
||||
(assert-equal 12
|
||||
(-> (list 1 2 3)
|
||||
(map (fn (x) (* x 2)))
|
||||
(reduce + 0))))
|
||||
|
||||
(deftest "-> some"
|
||||
(assert-true
|
||||
(-> (list 1 2 3) (some (fn (x) (> x 2)))))
|
||||
(assert-false
|
||||
(-> (list 1 2 3) (some (fn (x) (> x 5))))))
|
||||
|
||||
(deftest "-> every?"
|
||||
(assert-true
|
||||
(-> (list 2 4 6) (every? (fn (x) (> x 1))))))
|
||||
|
||||
(deftest "-> full pipeline: map filter reduce"
|
||||
;; Double each, keep > 4, sum
|
||||
(assert-equal 24
|
||||
(-> (list 1 2 3 4 5)
|
||||
(map (fn (x) (* x 2)))
|
||||
(filter (fn (x) (> x 4)))
|
||||
(reduce + 0)))))
|
||||
|
||||
Reference in New Issue
Block a user