scheme: Phase 3.5 — let/let*/cond/when/unless/and/or + 21 tests
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
Adds the rest of the standard syntactic operators, all built on the existing eval/closure infrastructure from Phase 3: - let — parallel bindings in fresh child env; values evaluated in outer env (RHS sees pre-let bindings only). Multi-body via scheme-eval-body. - let* — sequential bindings, each in a nested child env; later bindings see earlier ones. - cond — clauses walked in order; first truthy test wins. `else` symbol is the catch-all. Test-only clauses (no body) return the test value. Scheme truthiness: only #f is false. - when / unless — single-test conditional execution, multi-body body via scheme-eval-body. - and / or — short-circuit boolean. Empty `(and)` = true, `(or)` = false. Both return the actual value at the point of short-circuit (not coerced to bool), matching R7RS. 130 total Scheme tests (62 parse + 23 eval + 45 syntax). The Scheme port is now self-hosting enough to write any non-stdlib program — factorial, list operations via primitives, closures with mutable state, all working. Next phase: standard env (runtime.sx) with variadic +/-, list ops as Scheme-visible applicatives.
This commit is contained in:
@@ -198,6 +198,141 @@
|
||||
:body body
|
||||
:env env}))
|
||||
|
||||
;; ── let / let* — bindings in a fresh child env ───────────────────
|
||||
|
||||
(define scm-bind-let-vals!
|
||||
(fn (local bindings dyn-env)
|
||||
(cond
|
||||
((or (nil? bindings) (= (length bindings) 0)) nil)
|
||||
(:else
|
||||
(let ((b (first bindings)))
|
||||
(cond
|
||||
((not (and (list? b) (= (length b) 2)))
|
||||
(error "let: each binding must be (name expr)"))
|
||||
((not (string? (first b)))
|
||||
(error "let: binding name must be a symbol"))
|
||||
(:else
|
||||
(begin
|
||||
(scheme-env-bind! local (first b)
|
||||
(scheme-eval (nth b 1) dyn-env))
|
||||
(scm-bind-let-vals! local (rest bindings) dyn-env)))))))))
|
||||
|
||||
(scheme-define-op! "let"
|
||||
(fn (args env)
|
||||
(cond
|
||||
((< (length args) 2)
|
||||
(error "let: expects (bindings body...)"))
|
||||
((not (list? (first args)))
|
||||
(error "let: bindings must be a list"))
|
||||
(:else
|
||||
(let ((local (scheme-extend-env env)))
|
||||
(scm-bind-let-vals! local (first args) env)
|
||||
(scheme-eval-body (rest args) local))))))
|
||||
|
||||
;; let* — sequential let; each binding sees earlier ones.
|
||||
(define scm-let*-step
|
||||
(fn (bindings env body)
|
||||
(cond
|
||||
((or (nil? bindings) (= (length bindings) 0))
|
||||
(scheme-eval-body body env))
|
||||
(:else
|
||||
(let ((b (first bindings)))
|
||||
(cond
|
||||
((not (and (list? b) (= (length b) 2)))
|
||||
(error "let*: each binding must be (name expr)"))
|
||||
(:else
|
||||
(let ((child (scheme-extend-env env)))
|
||||
(scheme-env-bind! child (first b)
|
||||
(scheme-eval (nth b 1) env))
|
||||
(scm-let*-step (rest bindings) child body)))))))))
|
||||
|
||||
(scheme-define-op! "let*"
|
||||
(fn (args env)
|
||||
(cond
|
||||
((< (length args) 2)
|
||||
(error "let*: expects (bindings body...)"))
|
||||
((not (list? (first args)))
|
||||
(error "let*: bindings must be a list"))
|
||||
(:else (scm-let*-step (first args) env (rest args))))))
|
||||
|
||||
;; ── cond / when / unless ─────────────────────────────────────────
|
||||
|
||||
(define scm-cond-clauses
|
||||
(fn (clauses env)
|
||||
(cond
|
||||
((or (nil? clauses) (= (length clauses) 0)) nil)
|
||||
(:else
|
||||
(let ((clause (first clauses)))
|
||||
(cond
|
||||
((not (list? clause))
|
||||
(error "cond: each clause must be a list"))
|
||||
((= (length clause) 0)
|
||||
(error "cond: empty clause"))
|
||||
((and (string? (first clause)) (= (first clause) "else"))
|
||||
(scheme-eval-body (rest clause) env))
|
||||
(:else
|
||||
(let ((test-val (scheme-eval (first clause) env)))
|
||||
(cond
|
||||
((not (= test-val false))
|
||||
(cond
|
||||
((= (length clause) 1) test-val)
|
||||
(:else (scheme-eval-body (rest clause) env))))
|
||||
(:else (scm-cond-clauses (rest clauses) env)))))))))))
|
||||
|
||||
(scheme-define-op! "cond"
|
||||
(fn (args env) (scm-cond-clauses args env)))
|
||||
|
||||
(scheme-define-op! "when"
|
||||
(fn (args env)
|
||||
(cond
|
||||
((< (length args) 1) (error "when: expects (when test body...)"))
|
||||
(:else
|
||||
(let ((v (scheme-eval (first args) env)))
|
||||
(cond
|
||||
((= v false) nil)
|
||||
(:else (scheme-eval-body (rest args) env))))))))
|
||||
|
||||
(scheme-define-op! "unless"
|
||||
(fn (args env)
|
||||
(cond
|
||||
((< (length args) 1)
|
||||
(error "unless: expects (unless test body...)"))
|
||||
(:else
|
||||
(let ((v (scheme-eval (first args) env)))
|
||||
(cond
|
||||
((= v false) (scheme-eval-body (rest args) env))
|
||||
(:else nil)))))))
|
||||
|
||||
;; ── and / or — short-circuit boolean operators ──────────────────
|
||||
|
||||
(define scm-and-step
|
||||
(fn (args env)
|
||||
(cond
|
||||
((or (nil? args) (= (length args) 0)) true)
|
||||
((= (length args) 1) (scheme-eval (first args) env))
|
||||
(:else
|
||||
(let ((v (scheme-eval (first args) env)))
|
||||
(cond
|
||||
((= v false) false)
|
||||
(:else (scm-and-step (rest args) env))))))))
|
||||
|
||||
(scheme-define-op! "and"
|
||||
(fn (args env) (scm-and-step args env)))
|
||||
|
||||
(define scm-or-step
|
||||
(fn (args env)
|
||||
(cond
|
||||
((or (nil? args) (= (length args) 0)) false)
|
||||
((= (length args) 1) (scheme-eval (first args) env))
|
||||
(:else
|
||||
(let ((v (scheme-eval (first args) env)))
|
||||
(cond
|
||||
((not (= v false)) v)
|
||||
(:else (scm-or-step (rest args) env))))))))
|
||||
|
||||
(scheme-define-op! "or"
|
||||
(fn (args env) (scm-or-step args env)))
|
||||
|
||||
;; ── eval-args helper ─────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
|
||||
Reference in New Issue
Block a user