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.
290 lines
8.0 KiB
Plaintext
290 lines
8.0 KiB
Plaintext
;; 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}))
|