From 342e1a2ccf8527a71c44adab255a86ce5b11e953 Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 14 May 2026 06:45:39 +0000 Subject: [PATCH] =?UTF-8?q?scheme:=20Phase=207=20=E2=80=94=20eval/interact?= =?UTF-8?q?ion-environment/null-env=20+=2013=20tests=20[shapes-reflective]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runtime.sx binds R7RS reflective primitives: - eval EXPR ENV - interaction-environment — returns env captured by closure - null-environment VERSION — fresh empty env (ignores version) - scheme-report-environment N — fresh full standard env - environment? V interaction-environment closes over the standard env being built; each invocation of scheme-standard-env produces a distinct interaction env that returns ITSELF when queried — so user-side (define name expr) inside (eval ... (interaction-environment)) persists for subsequent (eval 'name ...) lookups. 13 tests cover: - eval over quoted forms (literal + constructed via list) - define-then-lookup through interaction-environment - eqv? identity of interaction-environment across calls - sandbox semantics: eval in null-environment errors on + - scheme-report-environment is fresh and distinct from interaction **Second consumer for lib/guest/reflective/evaluator.sx unlocked.** Scheme's eval/interaction-environment/null-environment triple is the same protocol Kernel exposes via eval-applicative / get-current-environment / make-environment. Extraction now satisfies the two-consumer rule — same playbook as env.sx and class-chain.sx, awaits a follow-up commit to actually extract the kit. 270 total Scheme tests (62 + 23 + 49 + 78 + 25 + 20 + 13). --- lib/scheme/runtime.sx | 34 +++++++++++ lib/scheme/tests/reflection.sx | 100 +++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 lib/scheme/tests/reflection.sx diff --git a/lib/scheme/runtime.sx b/lib/scheme/runtime.sx index f2a869ca..d8473171 100644 --- a/lib/scheme/runtime.sx +++ b/lib/scheme/runtime.sx @@ -586,6 +586,40 @@ ((and (dict? outcome) (get outcome :scm-weh-raised)) (scheme-apply handler (list (get outcome :value)))) (:else outcome)))))))) + ;; ── R7RS reflection: eval / environment accessors ─────── + ;; eval EXPR ENV — apply the evaluator to a user-supplied AST. + (scheme-env-bind! env "eval" + (fn (args) + (cond + ((not (= (length args) 2)) + (error "eval: expects (eval expr env)")) + (:else (scheme-eval (first args) (nth args 1)))))) + ;; interaction-environment — the env we're currently building. + ;; The closure captures `env`, so each invocation of + ;; scheme-standard-env produces a distinct interaction env + ;; whose interaction-environment fn returns itself. + (scheme-env-bind! env "interaction-environment" + (fn (args) + (cond + ((not (= (length args) 0)) + (error "interaction-environment: expects 0 args")) + (:else env)))) + ;; null-environment — fresh empty env. R7RS ignores version arg. + (scheme-env-bind! env "null-environment" + (fn (args) + (cond + ((not (= (length args) 1)) + (error "null-environment: expects (version)")) + (:else (scheme-make-env))))) + ;; scheme-report-environment — fresh full standard env. + (scheme-env-bind! env "scheme-report-environment" + (fn (args) + (cond + ((not (= (length args) 1)) + (error "scheme-report-environment: expects (version)")) + (:else (scheme-standard-env))))) + (scheme-env-bind! env "environment?" + (scm-unary "environment?" (fn (v) (scheme-env? v)))) ;; dynamic-wind BEFORE THUNK AFTER — runs BEFORE, then THUNK, ;; then AFTER. If THUNK raises, AFTER still runs before the ;; raise propagates. This is the basic-correctness version; diff --git a/lib/scheme/tests/reflection.sx b/lib/scheme/tests/reflection.sx new file mode 100644 index 00000000..2cb3410c --- /dev/null +++ b/lib/scheme/tests/reflection.sx @@ -0,0 +1,100 @@ +;; lib/scheme/tests/reflection.sx — Phase 7 reflective primitives. + +(define scm-ref-pass 0) +(define scm-ref-fail 0) +(define scm-ref-fails (list)) + +(define + scm-ref-test + (fn + (name actual expected) + (if + (= actual expected) + (set! scm-ref-pass (+ scm-ref-pass 1)) + (begin + (set! scm-ref-fail (+ scm-ref-fail 1)) + (append! scm-ref-fails {:name name :actual actual :expected expected}))))) + +(define + scm-ref + (fn (src) (scheme-eval (scheme-parse src) (scheme-standard-env)))) + +(define + scm-ref-all + (fn + (src) + (scheme-eval-program (scheme-parse-all src) (scheme-standard-env)))) + +;; ── eval ───────────────────────────────────────────────────────── + +(scm-ref-test + "eval: arithmetic" + (scm-ref "(eval '(+ 1 2 3) (interaction-environment))") + 6) +(scm-ref-test + "eval: nested" + (scm-ref "(eval '(* (+ 1 2) (- 5 1)) (interaction-environment))") + 12) +(scm-ref-test + "eval: constructed form" + (scm-ref "(eval (list '+ 10 20) (interaction-environment))") + 30) +(scm-ref-test + "eval: variable reference" + (scm-ref-all "(define x 42) (eval 'x (interaction-environment))") + 42) + +;; ── interaction-environment ───────────────────────────────────── + +(scm-ref-test + "interaction-environment: is an env" + (scm-ref "(environment? (interaction-environment))") + true) +(scm-ref-test + "interaction-environment: define persists" + (scm-ref-all + "(define ie (interaction-environment))\n (eval '(define stashed 99) ie)\n (eval 'stashed ie)") + 99) +(scm-ref-test + "interaction-environment: same env across calls" + (scm-ref-all + "(define a (interaction-environment))\n (define b (interaction-environment))\n (eqv? a b)") + true) + +;; ── null-environment ──────────────────────────────────────────── + +(scm-ref-test + "null-environment: is an env" + (scm-ref "(environment? (null-environment 7))") + true) +(scm-ref-test + "null-environment: has no + binding" + (scm-ref-all + "(define ne (null-environment 7))\n (guard (e (else 'unbound)) (eval '+ ne))") + "unbound") + +;; ── scheme-report-environment ─────────────────────────────────── + +(scm-ref-test + "scheme-report-environment: is an env" + (scm-ref "(environment? (scheme-report-environment 7))") + true) +(scm-ref-test + "scheme-report-environment: has +" + (scm-ref "(eval '(+ 1 2) (scheme-report-environment 7))") + 3) +(scm-ref-test + "scheme-report-environment: distinct from interaction" + (scm-ref-all + "(define ie (interaction-environment))\n (define re (scheme-report-environment 7))\n (eval '(define only-in-ie 1) ie)\n (guard (e (else 'unbound)) (eval 'only-in-ie re))") + "unbound") + +;; ── eval with explicit env for sandboxing ────────────────────── + +(scm-ref-test + "eval: sandbox with null-environment" + (scm-ref-all + "(define sandbox (null-environment 7))\n (guard (e (else 'unbound))\n (eval '(+ 1 1) sandbox))") + "unbound") + +(define scm-ref-tests-run! (fn () {:total (+ scm-ref-pass scm-ref-fail) :passed scm-ref-pass :failed scm-ref-fail :fails scm-ref-fails}))