kernel: Phase 3 $vau/$lambda/wrap/unwrap + 34 tests [shapes-reflective]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
User-defined operatives via $vau; applicatives via $lambda (wrap ∘ $vau). wrap/unwrap as Kernel-level applicatives. kernel-call-operative forks on :impl (primitive) vs :body (user) tag. kernel-base-env wires the four combiners + operative?/applicative? predicates. Env-param sentinel `_` / `#ignore` → :knl-ignore (skip dyn-env bind). Flat parameter list only; destructuring later. Headline test: custom applicative + custom operative composed from user code.
This commit is contained in:
289
lib/kernel/tests/vau.sx
Normal file
289
lib/kernel/tests/vau.sx
Normal file
@@ -0,0 +1,289 @@
|
||||
;; lib/kernel/tests/vau.sx — exercises lib/kernel/runtime.sx.
|
||||
;;
|
||||
;; Verifies the Phase 3 promise: user-defined operatives and applicatives
|
||||
;; constructible from inside the language. Tests build a Kernel
|
||||
;; base-env, bind a few helper applicatives (+, *, list, =, $if), and
|
||||
;; run programs that construct and use custom combiners.
|
||||
|
||||
(define kv-test-pass 0)
|
||||
(define kv-test-fail 0)
|
||||
(define kv-test-fails (list))
|
||||
|
||||
(define
|
||||
kv-test
|
||||
(fn
|
||||
(name actual expected)
|
||||
(if
|
||||
(= actual expected)
|
||||
(set! kv-test-pass (+ kv-test-pass 1))
|
||||
(begin
|
||||
(set! kv-test-fail (+ kv-test-fail 1))
|
||||
(append! kv-test-fails {:name name :actual actual :expected expected})))))
|
||||
|
||||
(define kv-eval-src (fn (src env) (kernel-eval (kernel-parse src) env)))
|
||||
|
||||
(define
|
||||
kv-make-env
|
||||
(fn
|
||||
()
|
||||
(let
|
||||
((env (kernel-base-env)))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"+"
|
||||
(kernel-make-primitive-applicative
|
||||
(fn (args) (+ (first args) (nth args 1)))))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"*"
|
||||
(kernel-make-primitive-applicative
|
||||
(fn (args) (* (first args) (nth args 1)))))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"-"
|
||||
(kernel-make-primitive-applicative
|
||||
(fn (args) (- (first args) (nth args 1)))))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"="
|
||||
(kernel-make-primitive-applicative
|
||||
(fn (args) (= (first args) (nth args 1)))))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"list"
|
||||
(kernel-make-primitive-applicative (fn (args) args)))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"cons"
|
||||
(kernel-make-primitive-applicative
|
||||
(fn (args) (cons (first args) (nth args 1)))))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"$quote"
|
||||
(kernel-make-primitive-operative (fn (args dyn-env) (first args))))
|
||||
(kernel-env-bind!
|
||||
env
|
||||
"$if"
|
||||
(kernel-make-primitive-operative
|
||||
(fn
|
||||
(args dyn-env)
|
||||
(if
|
||||
(kernel-eval (first args) dyn-env)
|
||||
(kernel-eval (nth args 1) dyn-env)
|
||||
(kernel-eval (nth args 2) dyn-env)))))
|
||||
env)))
|
||||
|
||||
;; ── $vau: builds an operative ───────────────────────────────────
|
||||
(kv-test
|
||||
"vau: identity returns first arg unevaluated"
|
||||
(kv-eval-src "(($vau (a) _ a) hello)" (kv-make-env))
|
||||
"hello")
|
||||
|
||||
(kv-test
|
||||
"vau: returns args as raw expressions"
|
||||
(kv-eval-src "(($vau (a b) _ (list a b)) (+ 1 2) (+ 3 4))" (kv-make-env))
|
||||
(list (list "+" 1 2) (list "+" 3 4)))
|
||||
|
||||
(kv-test
|
||||
"vau: env-param is a kernel env"
|
||||
(kernel-env? (kv-eval-src "(($vau () e e))" (kv-make-env)))
|
||||
true)
|
||||
|
||||
(kv-test
|
||||
"vau: returns operative"
|
||||
(kernel-operative? (kv-eval-src "($vau (x) _ x)" (kv-make-env)))
|
||||
true)
|
||||
|
||||
(kv-test
|
||||
"vau: returns operative not applicative"
|
||||
(kernel-applicative? (kv-eval-src "($vau (x) _ x)" (kv-make-env)))
|
||||
false)
|
||||
|
||||
(kv-test
|
||||
"vau: zero-arg body"
|
||||
(kv-eval-src "(($vau () _ 42))" (kv-make-env))
|
||||
42)
|
||||
|
||||
(kv-test
|
||||
"vau: static-env closure captured"
|
||||
(let
|
||||
((outer (kv-make-env)))
|
||||
(kernel-env-bind! outer "captured" 17)
|
||||
(let
|
||||
((op (kv-eval-src "($vau () _ captured)" outer))
|
||||
(caller (kv-make-env)))
|
||||
(kernel-env-bind! caller "captured" 99)
|
||||
(kernel-combine op (list) caller)))
|
||||
17)
|
||||
|
||||
(kv-test
|
||||
"vau: env-param exposes caller's dynamic env"
|
||||
(let
|
||||
((outer (kv-make-env)))
|
||||
(kernel-env-bind! outer "x" 1)
|
||||
(let
|
||||
((op (kv-eval-src "($vau () e e)" outer)) (caller (kv-make-env)))
|
||||
(kernel-env-bind! caller "x" 2)
|
||||
(let
|
||||
((e-val (kernel-combine op (list) caller)))
|
||||
(kernel-env-lookup e-val "x"))))
|
||||
2)
|
||||
|
||||
;; ── $lambda: applicatives evaluate their args ───────────────────
|
||||
(kv-test
|
||||
"lambda: identity"
|
||||
(kv-eval-src "(($lambda (x) x) 42)" (kv-make-env))
|
||||
42)
|
||||
|
||||
(kv-test
|
||||
"lambda: addition"
|
||||
(kv-eval-src "(($lambda (x y) (+ x y)) 3 4)" (kv-make-env))
|
||||
7)
|
||||
|
||||
(kv-test
|
||||
"lambda: args are evaluated before bind"
|
||||
(kv-eval-src "(($lambda (x) x) (+ 2 3))" (kv-make-env))
|
||||
5)
|
||||
|
||||
(kv-test
|
||||
"lambda: zero args"
|
||||
(kv-eval-src "(($lambda () 99))" (kv-make-env))
|
||||
99)
|
||||
|
||||
(kv-test
|
||||
"lambda: returns applicative"
|
||||
(kernel-applicative? (kv-eval-src "($lambda (x) x)" (kv-make-env)))
|
||||
true)
|
||||
|
||||
(kv-test
|
||||
"lambda: returns applicative not operative"
|
||||
(kernel-operative? (kv-eval-src "($lambda (x) x)" (kv-make-env)))
|
||||
false)
|
||||
|
||||
(kv-test
|
||||
"lambda: higher-order"
|
||||
(kv-eval-src "(($lambda (f) (f 10)) ($lambda (x) (+ x 1)))" (kv-make-env))
|
||||
11)
|
||||
|
||||
;; ── wrap / unwrap as user-callable applicatives ─────────────────
|
||||
|
||||
(kv-test
|
||||
"wrap: makes applicative from operative"
|
||||
(kernel-applicative? (kv-eval-src "(wrap ($vau (x) _ x))" (kv-make-env)))
|
||||
true)
|
||||
|
||||
(kv-test
|
||||
"wrap: result evaluates its arg"
|
||||
(kv-eval-src "((wrap ($vau (x) _ x)) (+ 1 2))" (kv-make-env))
|
||||
3)
|
||||
|
||||
(kv-test
|
||||
"unwrap: extracts operative from applicative"
|
||||
(kernel-operative? (kv-eval-src "(unwrap ($lambda (x) x))" (kv-make-env)))
|
||||
true)
|
||||
|
||||
(kv-test
|
||||
"wrap/unwrap roundtrip preserves identity"
|
||||
(kv-eval-src
|
||||
"(($lambda (op) (= op (unwrap (wrap op)))) ($vau (x) _ x))"
|
||||
(kv-make-env))
|
||||
true)
|
||||
|
||||
;; ── operative? / applicative? as user-visible predicates ────────
|
||||
|
||||
(kv-test
|
||||
"operative? on vau result"
|
||||
(kv-eval-src "(operative? ($vau (x) _ x))" (kv-make-env))
|
||||
true)
|
||||
|
||||
(kv-test
|
||||
"operative? on lambda result"
|
||||
(kv-eval-src "(operative? ($lambda (x) x))" (kv-make-env))
|
||||
false)
|
||||
|
||||
(kv-test
|
||||
"applicative? on lambda result"
|
||||
(kv-eval-src "(applicative? ($lambda (x) x))" (kv-make-env))
|
||||
true)
|
||||
|
||||
(kv-test
|
||||
"applicative? on vau result"
|
||||
(kv-eval-src "(applicative? ($vau (x) _ x))" (kv-make-env))
|
||||
false)
|
||||
|
||||
(kv-test
|
||||
"operative? on number"
|
||||
(kv-eval-src "(operative? 42)" (kv-make-env))
|
||||
false)
|
||||
|
||||
;; ── Build BOTH layers from user code ────────────────────────────
|
||||
;; The headline Phase 3 test: defining an operative on top of an
|
||||
;; applicative defined on top of a vau.
|
||||
|
||||
(kv-test
|
||||
"custom: applicative + operative compose"
|
||||
(let
|
||||
((env (kv-make-env)))
|
||||
(kernel-env-bind! env "square" (kv-eval-src "($lambda (x) (* x x))" env))
|
||||
(kv-eval-src "(square 4)" env))
|
||||
16)
|
||||
|
||||
(kv-test "custom: operative captures argument syntax"
|
||||
;; ($capture x) returns the raw expression `x`, regardless of value.
|
||||
(let ((env (kv-make-env)))
|
||||
(kernel-env-bind! env "$capture"
|
||||
(kv-eval-src "($vau (form) _ form)" env))
|
||||
(kv-eval-src "($capture (+ 1 2))" env))
|
||||
(list "+" 1 2))
|
||||
|
||||
(kv-test "custom: applicative re-wraps an operative"
|
||||
;; Build a captured operative, then wrap it into an applicative that
|
||||
;; evaluates args before re-entry. This exercises wrap+$vau composed.
|
||||
(let ((env (kv-make-env)))
|
||||
(kernel-env-bind! env "id-app"
|
||||
(kv-eval-src "(wrap ($vau (x) _ x))" env))
|
||||
(kv-eval-src "(id-app (+ 10 20))" env))
|
||||
30)
|
||||
|
||||
;; ── Error cases ──────────────────────────────────────────────────
|
||||
|
||||
(kv-test
|
||||
"vau: rejects non-list formals"
|
||||
(guard (e (true :raised)) (kv-eval-src "($vau x _ x)" (kv-make-env)))
|
||||
:raised)
|
||||
|
||||
(kv-test
|
||||
"vau: rejects non-symbol formal"
|
||||
(guard (e (true :raised)) (kv-eval-src "($vau (1) _ x)" (kv-make-env)))
|
||||
:raised)
|
||||
|
||||
(kv-test
|
||||
"vau: rejects non-symbol env-param"
|
||||
(guard (e (true :raised)) (kv-eval-src "($vau (x) 7 x)" (kv-make-env)))
|
||||
:raised)
|
||||
|
||||
(kv-test
|
||||
"vau: too few args at call site"
|
||||
(guard
|
||||
(e (true :raised))
|
||||
(kv-eval-src "(($vau (x y) _ x) 1)" (kv-make-env)))
|
||||
:raised)
|
||||
|
||||
(kv-test
|
||||
"vau: too many args at call site"
|
||||
(guard
|
||||
(e (true :raised))
|
||||
(kv-eval-src "(($vau (x) _ x) 1 2)" (kv-make-env)))
|
||||
:raised)
|
||||
|
||||
(kv-test
|
||||
"wrap: rejects non-operative"
|
||||
(guard (e (true :raised)) (kv-eval-src "(wrap 42)" (kv-make-env)))
|
||||
:raised)
|
||||
|
||||
(kv-test
|
||||
"unwrap: rejects non-applicative"
|
||||
(guard (e (true :raised)) (kv-eval-src "(unwrap 42)" (kv-make-env)))
|
||||
:raised)
|
||||
|
||||
(define kv-tests-run! (fn () {:total (+ kv-test-pass kv-test-fail) :passed kv-test-pass :failed kv-test-fail :fails kv-test-fails}))
|
||||
Reference in New Issue
Block a user