kernel: multi-expression body for $vau/$lambda + 5 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s
:body slot holds a LIST of forms now (was single expression). New knl-eval-body in eval.sx evaluates each form in sequence, returning the last. $vau and $lambda accept (formals env-param body...) / (formals body...). No $sequence dependency. 223 tests total.
This commit is contained in:
@@ -172,9 +172,19 @@
|
|||||||
(when
|
(when
|
||||||
(not (= eparam :knl-ignore))
|
(not (= eparam :knl-ignore))
|
||||||
(kernel-env-bind! local eparam dyn-env)))
|
(kernel-env-bind! local eparam dyn-env)))
|
||||||
(kernel-eval (get op :body) local)))
|
;; :body is a list of forms — evaluate in sequence, return last.
|
||||||
|
(knl-eval-body (get op :body) local)))
|
||||||
(:else (error "kernel-call-operative: malformed operative")))))
|
(:else (error "kernel-call-operative: malformed operative")))))
|
||||||
|
|
||||||
|
(define knl-eval-body
|
||||||
|
(fn (forms env)
|
||||||
|
(cond
|
||||||
|
((= (length forms) 1) (kernel-eval (first forms) env))
|
||||||
|
(:else
|
||||||
|
(begin
|
||||||
|
(kernel-eval (first forms) env)
|
||||||
|
(knl-eval-body (rest forms) env))))))
|
||||||
|
|
||||||
;; Phase 3 supports a flat parameter list only — destructuring later.
|
;; Phase 3 supports a flat parameter list only — destructuring later.
|
||||||
(define
|
(define
|
||||||
kernel-bind-params!
|
kernel-bind-params!
|
||||||
|
|||||||
@@ -40,13 +40,13 @@
|
|||||||
(fn
|
(fn
|
||||||
(args dyn-env)
|
(args dyn-env)
|
||||||
(cond
|
(cond
|
||||||
((not (= (length args) 3))
|
((< (length args) 3)
|
||||||
(error "$vau: expects (formals env-param body)"))
|
(error "$vau: expects (formals env-param body...)"))
|
||||||
(:else
|
(:else
|
||||||
(let
|
(let
|
||||||
((formals (first args))
|
((formals (first args))
|
||||||
(eparam-raw (nth args 1))
|
(eparam-raw (nth args 1))
|
||||||
(body (nth args 2)))
|
(body-forms (rest (rest args))))
|
||||||
(cond
|
(cond
|
||||||
((not (knl-formals-ok? formals))
|
((not (knl-formals-ok? formals))
|
||||||
(error "$vau: formals must be a list of symbols"))
|
(error "$vau: formals must be a list of symbols"))
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
(kernel-make-user-operative
|
(kernel-make-user-operative
|
||||||
formals
|
formals
|
||||||
(knl-eparam-sentinel eparam-raw)
|
(knl-eparam-sentinel eparam-raw)
|
||||||
body
|
body-forms
|
||||||
dyn-env))))))))
|
dyn-env))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -70,17 +70,21 @@
|
|||||||
(fn
|
(fn
|
||||||
(args dyn-env)
|
(args dyn-env)
|
||||||
(cond
|
(cond
|
||||||
((not (= (length args) 2))
|
((< (length args) 2)
|
||||||
(error "$lambda: expects (formals body)"))
|
(error "$lambda: expects (formals body...)"))
|
||||||
(:else
|
(:else
|
||||||
(let
|
(let
|
||||||
((formals (first args)) (body (nth args 1)))
|
((formals (first args)) (body-forms (rest args)))
|
||||||
(cond
|
(cond
|
||||||
((not (knl-formals-ok? formals))
|
((not (knl-formals-ok? formals))
|
||||||
(error "$lambda: formals must be a list of symbols"))
|
(error "$lambda: formals must be a list of symbols"))
|
||||||
(:else
|
(:else
|
||||||
(kernel-wrap
|
(kernel-wrap
|
||||||
(kernel-make-user-operative formals :knl-ignore body dyn-env)))))))))
|
(kernel-make-user-operative
|
||||||
|
formals
|
||||||
|
:knl-ignore
|
||||||
|
body-forms
|
||||||
|
dyn-env)))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
kernel-lambda-operative
|
kernel-lambda-operative
|
||||||
|
|||||||
@@ -286,4 +286,24 @@
|
|||||||
(guard (e (true :raised)) (kv-eval-src "(unwrap 42)" (kv-make-env)))
|
(guard (e (true :raised)) (kv-eval-src "(unwrap 42)" (kv-make-env)))
|
||||||
:raised)
|
:raised)
|
||||||
|
|
||||||
|
;; ── Multi-expression body (implicit $sequence) ──────────────────
|
||||||
|
|
||||||
|
(kv-test "lambda: two body forms — value of last"
|
||||||
|
(kv-eval-src "(($lambda (n) (+ n 1) (+ n 10)) 5)" (kv-make-env)) 15)
|
||||||
|
|
||||||
|
(kv-test "lambda: three body forms"
|
||||||
|
(kv-eval-src "(($lambda (n) n (+ n 1) (+ n 2)) 10)" (kv-make-env)) 12)
|
||||||
|
|
||||||
|
(kv-test "vau: two body forms"
|
||||||
|
(kv-eval-src "(($vau (a b) _ a (list a b)) 7 8)" (kv-make-env))
|
||||||
|
(list 7 8))
|
||||||
|
|
||||||
|
(kv-test "lambda: $define! in early body visible in later body"
|
||||||
|
(kv-eval-src
|
||||||
|
"(($lambda (n) ($define! double (+ n n)) double) 6)"
|
||||||
|
(kv-make-env)) 12)
|
||||||
|
|
||||||
|
(kv-test "lambda: zero-arg multi-body"
|
||||||
|
(kv-eval-src "(($lambda () 1 2 3))" (kv-make-env)) 3)
|
||||||
|
|
||||||
(define kv-tests-run! (fn () {:total (+ kv-test-pass kv-test-fail) :passed kv-test-pass :failed kv-test-fail :fails kv-test-fails}))
|
(define kv-tests-run! (fn () {:total (+ kv-test-pass kv-test-fail) :passed kv-test-pass :failed kv-test-fail :fails kv-test-fails}))
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ The motivation is that SX's host `make-env` family is registered only in HTTP/si
|
|||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- 2026-05-11 — Multi-expression body for `$vau`/`$lambda`. Both forms now accept `(formals env-param body1 body2 ...)` / `(formals body1 body2 ...)`. Implementation: `:body` slot now holds a LIST of forms (was a single expression); `kernel-call-operative` calls a new `knl-eval-body` that evaluates each in sequence, returning the last. No dependency on `$sequence` being in static-env — the iteration lives at the host level. 5 new tests in `tests/vau.sx` (multi-body lambda, multi-body vau, sequenced `$define!`, zero-arg multi-body). chisel: nothing (Kernel-internal improvement; doesn't change the reflective API surface). 223 tests total.
|
||||||
- 2026-05-11 — Phase 1 reader macros landed (the deferred checkbox from Phase 1). Parser now recognises four shorthand forms: `'expr` → `($quote expr)`, `` `expr `` → `($quasiquote expr)`, `,expr` → `($unquote expr)`, `,@expr` → `($unquote-splicing expr)`. Delimiter set extended to include `'`, `` ` ``, `,` so they don't slip into adjacent atom tokens. The runtime already has `$quote`; `$quasiquote` / `$unquote` / `$unquote-splicing` are not bound yet (would need a recursive walker for quasi-quote expansion — left for whenever a consumer needs it). 8 new reader-macro tests in `tests/parse.sx` bring parse to 62, total to 218. chisel: consumes-lex (parser still leans on `lib/guest/lex.sx` whitespace + digit predicates only).
|
- 2026-05-11 — Phase 1 reader macros landed (the deferred checkbox from Phase 1). Parser now recognises four shorthand forms: `'expr` → `($quote expr)`, `` `expr `` → `($quasiquote expr)`, `,expr` → `($unquote expr)`, `,@expr` → `($unquote-splicing expr)`. Delimiter set extended to include `'`, `` ` ``, `,` so they don't slip into adjacent atom tokens. The runtime already has `$quote`; `$quasiquote` / `$unquote` / `$unquote-splicing` are not bound yet (would need a recursive walker for quasi-quote expansion — left for whenever a consumer needs it). 8 new reader-macro tests in `tests/parse.sx` bring parse to 62, total to 218. chisel: consumes-lex (parser still leans on `lib/guest/lex.sx` whitespace + digit predicates only).
|
||||||
- 2026-05-11 — Phase 7 proposal complete (partial extraction per two-consumer rule). Consolidated the four candidate reflective files into the plan's API surface section: `env.sx` (Phase 2), `combiner.sx` (Phase 3), `evaluator.sx` (Phase 4), `hygiene.sx` (Phase 6). Total proposed surface ~25 functions, all sketched with signatures and representation notes. Kernel alone is the first consumer; the *second* consumer must materialise before any actual extraction. Listed candidate second consumers in priority order: metacircular Scheme (highest fit — same scope semantics), CL macro evaluator (medium fit — would drive the deferred hygiene work), Maru/Schemely (eventual). Extraction is estimated at <500 lines moved when the time comes — clean separation of concerns across this loop's six prior commits means the rename-and-move work is mechanical, not a redesign. chisel: proposes-reflective-extraction (the candidate API surface is the entire artefact of this phase). 210 tests across six test files, zero regressions across the loop. The kernel-on-sx loop sustained one feature per commit for seven commits.
|
- 2026-05-11 — Phase 7 proposal complete (partial extraction per two-consumer rule). Consolidated the four candidate reflective files into the plan's API surface section: `env.sx` (Phase 2), `combiner.sx` (Phase 3), `evaluator.sx` (Phase 4), `hygiene.sx` (Phase 6). Total proposed surface ~25 functions, all sketched with signatures and representation notes. Kernel alone is the first consumer; the *second* consumer must materialise before any actual extraction. Listed candidate second consumers in priority order: metacircular Scheme (highest fit — same scope semantics), CL macro evaluator (medium fit — would drive the deferred hygiene work), Maru/Schemely (eventual). Extraction is estimated at <500 lines moved when the time comes — clean separation of concerns across this loop's six prior commits means the rename-and-move work is mechanical, not a redesign. chisel: proposes-reflective-extraction (the candidate API surface is the entire artefact of this phase). 210 tests across six test files, zero regressions across the loop. The kernel-on-sx loop sustained one feature per commit for seven commits.
|
||||||
- 2026-05-11 — Phase 6 hygiene landed (mostly). Two helpers in `runtime.sx`: `$let` — proper hygienic let; values evaluated in caller env, names bound in fresh child env, body in that child env. `$define-in!` — operative that binds a name in a *specified* env, not the dyn-env. The key insight: hygiene-by-default was already the case from Phase 3's static-env extension semantics — $vau/$lambda close over their static env and bind formals + body $define!s in a CHILD of static-env, so caller's env stays untouched unless explicitly threaded via `eval` or `$define-in!`. The 18 tests in `tests/hygiene.sx` prove this property holds in practice: `$define!` inside an operative body doesn't escape to the caller; `$let`-bound names don't leak after the let; parallel let evaluates RHS in outer scope; `$define-in!` populates the target env without polluting the caller's. Full scope-set / frame-stamp hygiene (Shutt's later research-grade work) is documented in the proposed `lib/guest/reflective/hygiene.sx` notes but deferred — would require lifted symbols with provenance markers, a much larger redesign. chisel: shapes-reflective. The default-hygienic-by-static-env-extension property is itself a chisel finding worth recording — every reflective Lisp would benefit from this design choice, and the `lib/guest/reflective/env.sx` candidate API should make it the default semantic.
|
- 2026-05-11 — Phase 6 hygiene landed (mostly). Two helpers in `runtime.sx`: `$let` — proper hygienic let; values evaluated in caller env, names bound in fresh child env, body in that child env. `$define-in!` — operative that binds a name in a *specified* env, not the dyn-env. The key insight: hygiene-by-default was already the case from Phase 3's static-env extension semantics — $vau/$lambda close over their static env and bind formals + body $define!s in a CHILD of static-env, so caller's env stays untouched unless explicitly threaded via `eval` or `$define-in!`. The 18 tests in `tests/hygiene.sx` prove this property holds in practice: `$define!` inside an operative body doesn't escape to the caller; `$let`-bound names don't leak after the let; parallel let evaluates RHS in outer scope; `$define-in!` populates the target env without polluting the caller's. Full scope-set / frame-stamp hygiene (Shutt's later research-grade work) is documented in the proposed `lib/guest/reflective/hygiene.sx` notes but deferred — would require lifted symbols with provenance markers, a much larger redesign. chisel: shapes-reflective. The default-hygienic-by-static-env-extension property is itself a chisel finding worth recording — every reflective Lisp would benefit from this design choice, and the `lib/guest/reflective/env.sx` candidate API should make it the default semantic.
|
||||||
|
|||||||
Reference in New Issue
Block a user