go: eval.sx — stmts + function application; recursive fib evaluates + 8 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 28s

Phase 4 cont. go-eval-stmt dispatches on:
  :return       → wraps value in (:return-value V) sentinel
  :var-decl     → bind each NAME via go-eval-var-decl
  :short-decl   → bind each (:var NAME) lhs to corresponding expr value
  :assign       → immutable-env shadowing (true mutation deferred)
  :block        → run stmts via go-eval-block, propagating :return-value
  :if / :else   → cond-driven dispatch
  :func-decl    → bind name to (list :go-fn PARAMS BODY)
  else          → expression statement, evaluate for side effects

go-eval-call extends the CALLER's env with param-names → arg-values
(dynamic-scope-ish — closures don't capture lexical env yet), runs the
body block, catches :return-value and unwraps.

**Recursive fib(5) = 5 evaluates correctly.** Recursion works because
top-level func bindings are in the calling env before the recursive
call happens.

True lexical closures (let bind sees outer var; assignments visible to
nested funcs) need an env-cell model with mutation; deferred to a
later slice.

eval 33/33, total 410/410.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 21:17:26 +00:00
parent ff9abe3ae6
commit 1340c2626b
5 changed files with 329 additions and 33 deletions

View File

@@ -174,6 +174,238 @@
;; ── main eval ────────────────────────────────────────────────────
(define
go-eval-binop-ops
(list "+" "-" "*" "/" "==" "!=" "<" "<=" ">" ">=" "&&" "||"))
(define
go-is-eval-binop?
(fn (head args)
(and (list? head) (= (first head) :var)
(= (len args) 2)
(some (fn (op) (= op (nth head 1))) go-eval-binop-ops))))
(define
go-eval-args
;; Returns a list of arg values or a (:eval-error ...).
(fn (env args)
(cond
(or (= args nil) (= (len args) 0)) (list)
:else
(let ((v (go-eval env (first args))))
(cond
(go-eval-error? v) v
:else
(let ((rest-vs (go-eval-args env (rest args))))
(cond
(go-eval-error? rest-vs) rest-vs
:else (cons v rest-vs))))))))
(define
go-flatten-param-names
;; PARAMS is a list of (:field NAMES TYPE) groups; return a flat name list.
(fn (params)
(cond
(or (= params nil) (= (len params) 0)) (list)
:else
(let ((field (first params)))
(let ((names (nth field 1)))
(go-name-concat names (go-flatten-param-names (rest params))))))))
(define
go-name-concat
(fn (a b)
(cond
(= (len a) 0) b
:else (cons (first a) (go-name-concat (rest a) b)))))
(define
go-bind-names
(fn (env names vals)
(cond
(= (len names) 0) env
:else
(go-bind-names
(go-env-extend env (first names) (first vals))
(rest names) (rest vals)))))
(define
go-eval-call
;; Apply a callable VAL to ARG-EXPRS in CALLER-ENV. Result is the
;; function's return value or a (:eval-error ...).
;;
;; Closure semantics: the function value carries no captured env in v0
;; (dynamic scope wrt outer bindings). Recursion at top level works
;; because the calling env already has the function bound. Nested
;; lexical closures arrive in a later slice.
(fn (caller-env callee-val args)
(cond
(not (and (list? callee-val) (= (first callee-val) :go-fn)))
(list :eval-error :not-callable callee-val)
:else
(let ((params (nth callee-val 1)) (body (nth callee-val 2)))
(let ((arg-vals (go-eval-args caller-env args)))
(cond
(go-eval-error? arg-vals) arg-vals
:else
(let ((param-names (go-flatten-param-names params)))
(cond
(not (= (len param-names) (len arg-vals)))
(list :eval-error :arity-mismatch
(len param-names) (len arg-vals))
:else
(let ((call-env
(go-bind-names caller-env param-names arg-vals)))
(cond
(= body nil) nil
(and (list? body) (= (first body) :block))
(let ((r (go-eval-block call-env (nth body 1))))
(cond
(and (list? r) (= (first r) :return-value))
(nth r 1)
(go-eval-error? r) r
:else nil))
:else nil))))))))))
(define
go-eval-var-decl
;; (:var-decl (:field NAMES TYPE) EXPRS) — bind each NAME to either
;; the corresponding EXPR's value or nil (zero-init when no EXPRS).
(fn (env stmt)
(let ((field (nth stmt 1)) (exprs (nth stmt 2)))
(let ((names (nth field 1)))
(cond
(or (= exprs nil) (= (len exprs) 0))
(go-bind-names env names
(go-zeros (len names)))
:else
(let ((vals (go-eval-args env exprs)))
(cond
(go-eval-error? vals) vals
:else (go-bind-names env names vals))))))))
(define
go-zeros (fn (n) (cond (<= n 0) (list) :else (cons nil (go-zeros (- n 1))))))
(define
go-eval-short-decl
;; (:short-decl LHS-LIST EXPRS) — LHS list of (:var NAME) nodes.
(fn (env stmt)
(let ((lhs-list (nth stmt 1)) (exprs (nth stmt 2)))
(let ((names
(map (fn (lhs)
(cond
(and (list? lhs) (= (first lhs) :var))
(nth lhs 1)
:else :unknown))
lhs-list)))
(let ((vals (go-eval-args env exprs)))
(cond
(go-eval-error? vals) vals
:else (go-bind-names env names vals)))))))
(define
go-eval-assign
;; v0: assignment shadows via env extension (immutable env model).
;; Mutation through closures deferred.
(fn (env stmt)
(let ((lhs-list (nth stmt 1)) (rhs-list (nth stmt 2)))
(let ((vals (go-eval-args env rhs-list)))
(cond
(go-eval-error? vals) vals
:else
(go-eval-assign-pairs env lhs-list vals))))))
(define
go-eval-assign-pairs
(fn (env lhs-list vals)
(cond
(= (len lhs-list) 0) env
:else
(let ((lhs (first lhs-list)))
(cond
(and (list? lhs) (= (first lhs) :var))
(go-eval-assign-pairs
(go-env-extend env (nth lhs 1) (first vals))
(rest lhs-list) (rest vals))
:else (list :eval-error :unsupported-lhs lhs))))))
(define
go-eval-if
(fn (env stmt)
(let ((cnd (nth stmt 1)) (then (nth stmt 2)) (els (nth stmt 3)))
(let ((c (go-eval env cnd)))
(cond
(go-eval-error? c) c
c (go-eval-stmt env then)
(not (= els nil)) (go-eval-stmt env els)
:else env)))))
(define
go-eval-func-decl
(fn (env stmt)
(let ((name (nth stmt 1)) (params (nth stmt 2))
(body (nth stmt 4)))
(go-env-extend env name (list :go-fn params body)))))
(define
go-eval-stmt
(fn (env stmt)
(cond
(and (list? stmt) (= (first stmt) :return))
(let ((exprs (nth stmt 1)))
(cond
(or (= exprs nil) (= (len exprs) 0))
(list :return-value nil)
:else
(let ((v (go-eval env (first exprs))))
(cond
(go-eval-error? v) v
:else (list :return-value v)))))
(and (list? stmt) (= (first stmt) :var-decl))
(go-eval-var-decl env stmt)
(and (list? stmt) (= (first stmt) :short-decl))
(go-eval-short-decl env stmt)
(and (list? stmt) (= (first stmt) :assign))
(go-eval-assign env stmt)
(and (list? stmt) (= (first stmt) :block))
(go-eval-block env (nth stmt 1))
(and (list? stmt) (= (first stmt) :if))
(go-eval-if env stmt)
(and (list? stmt) (= (first stmt) :func-decl))
(go-eval-func-decl env stmt)
:else
(let ((v (go-eval env stmt)))
(cond
(go-eval-error? v) v
:else env)))))
(define
go-eval-block
(fn (env stmts)
(cond
(or (= stmts nil) (= (len stmts) 0)) env
:else
(let ((r (go-eval-stmt env (first stmts))))
(cond
(and (list? r) (= (first r) :return-value)) r
(go-eval-error? r) r
:else (go-eval-block r (rest stmts)))))))
(define
go-eval-program
;; Evaluate a sequence of top-level forms in ENV. Returns the final
;; env (or :eval-error / :return-value if either propagates).
(fn (env forms)
(cond
(or (= forms nil) (= (len forms) 0)) env
:else
(let ((r (go-eval-stmt env (first forms))))
(cond
(and (list? r) (= (first r) :return-value)) r
(go-eval-error? r) r
:else (go-eval-program r (rest forms)))))))
(define
go-eval
(fn
@@ -185,31 +417,26 @@
(let
((name (nth expr 1)))
(cond
(= name "true")
true
(= name "false")
false
(= name "nil")
nil
(= name "true") true
(= name "false") false
(= name "nil") nil
:else (let
((v (go-env-lookup env name)))
(cond (= v nil) (list :eval-error :unbound name) :else v))))
(and
(list? expr)
(= (first expr) :app)
(list? (nth expr 1))
(= (first (nth expr 1)) :var)
(= (len (nth expr 2)) 2))
(let
((op (nth (nth expr 1) 1))
(args (nth expr 2)))
(let
((lv (go-eval env (first args)))
(rv (go-eval env (nth args 1))))
(cond
(go-eval-error? lv)
lv
(go-eval-error? rv)
rv
:else (go-eval-binop op lv rv))))
(and (list? expr) (= (first expr) :app))
(let ((head (nth expr 1)) (args (nth expr 2)))
(cond
(go-is-eval-binop? head args)
(let ((op (nth head 1)))
(let ((lv (go-eval env (first args)))
(rv (go-eval env (nth args 1))))
(cond
(go-eval-error? lv) lv
(go-eval-error? rv) rv
:else (go-eval-binop op lv rv))))
:else
(let ((callee (go-eval env head)))
(cond
(go-eval-error? callee) callee
:else (go-eval-call env callee args)))))
:else (list :eval-error :unsupported-eval expr))))

View File

@@ -1,12 +1,12 @@
{
"language": "go",
"total_pass": 402,
"total": 402,
"total_pass": 410,
"total": 410,
"suites": [
{"name":"lex","pass":129,"total":129,"status":"ok"},
{"name":"parse","pass":176,"total":176,"status":"ok"},
{"name":"types","pass":72,"total":72,"status":"ok"},
{"name":"eval","pass":25,"total":25,"status":"ok"},
{"name":"eval","pass":33,"total":33,"status":"ok"},
{"name":"runtime","pass":0,"total":0,"status":"pending"},
{"name":"stdlib","pass":0,"total":0,"status":"pending"},
{"name":"e2e","pass":0,"total":0,"status":"pending"}

View File

@@ -1,13 +1,13 @@
# Go-on-SX Scoreboard
**Total: 402 / 402 tests passing**
**Total: 410 / 410 tests passing**
| | Suite | Pass | Total |
|---|---|---|---|
| ✅ | lex | 129 | 129 |
| ✅ | parse | 176 | 176 |
| ✅ | types | 72 | 72 |
| ✅ | eval | 25 | 25 |
| ✅ | eval | 33 | 33 |
| ⬜ | runtime | 0 | 0 |
| ⬜ | stdlib | 0 | 0 |
| ⬜ | e2e | 0 | 0 |

View File

@@ -90,6 +90,62 @@
true)
;; ── report ──────────────────────────────────────────────────────
(go-eval-test
"var-decl: var x = 5 — env has x=5"
(go-env-lookup
(go-eval-program go-env-empty (list (go-parse "var x = 5")))
"x")
5)
(go-eval-test
"short-decl: a, b := 3, 4 — env has both"
(let
((env (go-eval-program go-env-empty (list (go-parse "a, b := 3, 4")))))
(list (go-env-lookup env "a") (go-env-lookup env "b")))
(list 3 4))
(go-eval-test
"assign: x = 5 then x → 5"
(let
((env (go-eval-program (go-env-extend go-env-empty "x" 1) (list (go-parse "x = 5")))))
(go-env-lookup env "x"))
5)
(go-eval-test
"if: true branch evaluates"
(let
((env (go-eval-program (go-env-extend go-env-empty "x" 0) (list (go-parse "if true { x = 1 }")))))
(go-env-lookup env "x"))
1)
(go-eval-test
"if-else: false → else branch"
(let
((env (go-eval-program (go-env-extend go-env-empty "x" 0) (list (go-parse "if false { x = 1 } else { x = 2 }")))))
(go-env-lookup env "x"))
2)
(go-eval-test
"fn: define + call — double(7) = 14"
(let
((env (go-eval-program go-env-empty (list (go-parse "func double(x int) int { return x * 2 }")))))
(go-eval env (go-parse "double(7)")))
14)
(go-eval-test
"fn: add(2, 3) = 5"
(let
((env (go-eval-program go-env-empty (list (go-parse "func add(x, y int) int { return x + y }")))))
(go-eval env (go-parse "add(2, 3)")))
5)
(go-eval-test
"fn: recursive fib(5) = 5"
(let
((env (go-eval-program go-env-empty (list (go-parse "func fib(n int) int { if n < 2 { return n } return fib(n-1) + fib(n-2) }")))))
(go-eval env (go-parse "fib(5)")))
5)
(define
go-eval-test-summary
(str "eval " go-eval-test-pass "/" go-eval-test-count))