;; Go evaluator tests.

(define go-eval-test-count 0)
(define go-eval-test-pass 0)
(define go-eval-test-fails (list))

(define
  go-eval-test
  (fn
    (name actual expected)
    (set! go-eval-test-count (+ go-eval-test-count 1))
    (if
      (= actual expected)
      (set! go-eval-test-pass (+ go-eval-test-pass 1))
      (append! go-eval-test-fails {:name name :expected expected :actual actual}))))

(define gtev (fn (env src) (go-eval env (go-parse src))))

;; ── env ──────────────────────────────────────────────────────────
(go-eval-test
  "env: empty lookup returns nil"
  (go-env-lookup go-env-empty "x")
  nil)

(go-eval-test
  "env: extend then lookup"
  (go-env-lookup (go-env-extend go-env-empty "x" 42) "x")
  42)

;; ── literals ────────────────────────────────────────────────────
(go-eval-test "lit: 42 → 42" (gtev go-env-empty "42") 42)

(go-eval-test "lit: 0 → 0" (gtev go-env-empty "0") 0)

(go-eval-test "lit: 0xFF → 255" (gtev go-env-empty "0xFF") 255)

(go-eval-test "lit: 0b1010 → 10" (gtev go-env-empty "0b1010") 10)

(go-eval-test "lit: 0o17 → 15" (gtev go-env-empty "0o17") 15)

(go-eval-test
  "lit: underscore separator 1_000 → 1000"
  (gtev go-env-empty "1_000")
  1000)

(go-eval-test "lit: string" (gtev go-env-empty "\"hello\"") "hello")

;; ── predeclared ─────────────────────────────────────────────────
(go-eval-test "var: true" (gtev go-env-empty "true") true)
(go-eval-test "var: false" (gtev go-env-empty "false") false)
(go-eval-test "var: nil" (gtev go-env-empty "nil") nil)

;; ── variable lookup ─────────────────────────────────────────────
(go-eval-test
  "var: bound x → 5"
  (go-eval (go-env-extend go-env-empty "x" 5) (go-parse "x"))
  5)

(go-eval-test
  "var: unbound y → :eval-error"
  (gtev go-env-empty "y")
  (list :eval-error :unbound "y"))

;; ── binary ops ─────────────────────────────────────────────────
(go-eval-test "binop: 1 + 2 → 3" (gtev go-env-empty "1 + 2") 3)
(go-eval-test "binop: 10 - 4 → 6" (gtev go-env-empty "10 - 4") 6)
(go-eval-test "binop: 3 * 7 → 21" (gtev go-env-empty "3 * 7") 21)
(go-eval-test "binop: 42 / 7 → 6" (gtev go-env-empty "42 / 7") 6)
(go-eval-test
  "binop: 2 + 3 * 4 → 14 (prec)"
  (gtev go-env-empty "2 + 3 * 4")
  14)
(go-eval-test
  "binop: a + b uses env"
  (go-eval
    (go-env-extend (go-env-extend go-env-empty "a" 3) "b" 4)
    (go-parse "a + b"))
  7)

(go-eval-test "binop: 1 < 2 → true" (gtev go-env-empty "1 < 2") true)
(go-eval-test "binop: 5 == 5 → true" (gtev go-env-empty "5 == 5") true)
(go-eval-test "binop: 5 != 5 → false" (gtev go-env-empty "5 != 5") false)
(go-eval-test
  "binop: true && false → false"
  (gtev go-env-empty "true && false")
  false)
(go-eval-test
  "binop: false || true → true"
  (gtev go-env-empty "false || true")
  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)

(go-eval-test
  "for: count to 10 with sum"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "var sum = 0") (go-parse "for i := 0; i < 10; i++ { sum = sum + i }")))))
    (go-env-lookup env "sum"))
  45)

(go-eval-test
  "inc-dec: x++ updates env"
  (let
    ((env (go-eval-program (go-env-extend go-env-empty "x" 5) (list (go-parse "x++")))))
    (go-env-lookup env "x"))
  6)

(go-eval-test
  "inc-dec: x-- updates env"
  (let
    ((env (go-eval-program (go-env-extend go-env-empty "x" 5) (list (go-parse "x--")))))
    (go-env-lookup env "x"))
  4)

(go-eval-test
  "for: break exits the loop"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "var i = 0") (go-parse "for i < 100 { if i == 5 { break } ; i++ }")))))
    (go-env-lookup env "i"))
  5)

(go-eval-test
  "for: continue skips body but runs post"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "var sum = 0") (go-parse "for i := 0; i < 5; i++ { if i == 2 { continue } ; sum = sum + i }")))))
    (go-env-lookup env "sum"))
  8)

(go-eval-test
  "for: infinite + break with sum"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "var s = 0") (go-parse "var i = 1") (go-parse "for { if i > 4 { break } ; s = s + i ; i++ }")))))
    (go-env-lookup env "s"))
  10)

(go-eval-test
  "fn: iterative factorial via for-loop"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "func fact(n int) int { r := 1 ; for i := 2 ; i <= n ; i++ { r = r * i } ; return r }")))))
    (go-eval env (go-parse "fact(5)")))
  120)

(go-eval-test
  "slice: []int{1,2,3} → :go-slice"
  (gtev go-env-empty "[]int{1, 2, 3}")
  (list :go-slice (list 1 2 3)))

(go-eval-test
  "index: a[0] = 10, a[2] = 30"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "a := []int{10, 20, 30}")))))
    (list (go-eval env (go-parse "a[0]")) (go-eval env (go-parse "a[2]"))))
  (list 10 30))

(go-eval-test
  "index: out-of-range error"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "a := []int{1, 2}")))))
    (go-eval env (go-parse "a[5]")))
  (list :eval-error :index-out-of-range 5 2))

(go-eval-test
  "builtin: len(slice) = 3"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "a := []int{1, 2, 3}")))))
    (go-eval env (go-parse "len(a)")))
  3)

(go-eval-test
  "builtin: len(string)"
  (go-eval go-env-builtins (go-parse "len(\"hello\")"))
  5)

(go-eval-test
  "builtin: append(a, 4, 5)"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "a := []int{1, 2, 3}")))))
    (go-eval env (go-parse "append(a, 4, 5)")))
  (list
    :go-slice (list 1 2 3 4 5)))

(go-eval-test
  "slice expr: a[1:3]"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "a := []int{10, 20, 30, 40}")))))
    (go-eval env (go-parse "a[1:3]")))
  (list :go-slice (list 20 30)))

(go-eval-test
  "slice expr: a[:2] (omitted low)"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "a := []int{1, 2, 3, 4}")))))
    (go-eval env (go-parse "a[:2]")))
  (list :go-slice (list 1 2)))

(go-eval-test
  "slice expr: a[2:] (omitted high)"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "a := []int{1, 2, 3, 4}")))))
    (go-eval env (go-parse "a[2:]")))
  (list :go-slice (list 3 4)))

(go-eval-test
  "fn: sum slice via for-loop with len + index"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "a := []int{1, 2, 3, 4, 5}") (go-parse "sum := 0") (go-parse "for i := 0; i < len(a); i++ { sum = sum + a[i] }")))))
    (go-env-lookup env "sum"))
  15)

(go-eval-test
  "map: map[string]int{...} → :go-map"
  (gtev go-env-empty "map[string]int{\"a\": 1, \"b\": 2}")
  (list :go-map (list (list "a" 1) (list "b" 2))))

(go-eval-test
  "map: m[\"a\"] → 1"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "m := map[string]int{\"a\": 1, \"b\": 2}")))))
    (go-eval env (go-parse "m[\"a\"]")))
  1)

(go-eval-test
  "map: missing key → nil (v0 stand-in for zero value)"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "m := map[string]int{\"a\": 1}")))))
    (go-eval env (go-parse "m[\"missing\"]")))
  nil)

(go-eval-test
  "map: len(m) = 2"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "m := map[string]int{\"a\": 1, \"b\": 2}")))))
    (go-eval env (go-parse "len(m)")))
  2)

(go-eval-test
  "map: index-assign updates existing key"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "m := map[string]int{\"a\": 1}") (go-parse "m[\"a\"] = 99")))))
    (go-eval env (go-parse "m[\"a\"]")))
  99)

(go-eval-test
  "map: index-assign adds new key"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "m := map[string]int{}") (go-parse "m[\"new\"] = 7")))))
    (go-eval env (go-parse "m[\"new\"]")))
  7)

(go-eval-test
  "slice: index-assign a[0] = 99"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "a := []int{10, 20, 30}") (go-parse "a[0] = 99")))))
    (go-eval env (go-parse "a[0]")))
  99)

(go-eval-test
  "map: word count via loop"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "words := []string{\"a\", \"b\", \"a\", \"c\", \"a\"}") (go-parse "counts := map[string]int{}") (go-parse "for i := 0; i < len(words); i++ { counts[words[i]] = counts[words[i]] + 1 }")))))
    (go-eval env (go-parse "counts[\"a\"]")))
  3)

(go-eval-test
  "type-decl: registers struct field names"
  (go-env-lookup
    (go-eval-program
      go-env-empty
      (list (go-parse "type Point struct { x, y int }")))
    "Point")
  (list :go-struct-type (list "x" "y")))

(go-eval-test
  "struct: positional composite Point{1, 2}"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }")))))
    (go-eval env (go-parse "Point{1, 2}")))
  (list
    :go-struct "Point"
    (list (list "x" 1) (list "y" 2))))

(go-eval-test
  "struct: keyed composite Point{x: 5, y: 10}"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }")))))
    (go-eval env (go-parse "Point{x: 5, y: 10}")))
  (list
    :go-struct "Point"
    (list (list "x" 5) (list "y" 10))))

(go-eval-test
  "struct: selector p.x = 1"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "p := Point{1, 2}")))))
    (go-eval env (go-parse "p.x")))
  1)

(go-eval-test
  "struct: selector p.y = 2"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "p := Point{1, 2}")))))
    (go-eval env (go-parse "p.y")))
  2)

(go-eval-test
  "struct: selector-assign p.x = 99"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "p := Point{1, 2}") (go-parse "p.x = 99")))))
    (go-eval env (go-parse "p.x")))
  99)

(go-eval-test
  "struct: positional arity-mismatch"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }")))))
    (go-eval env (go-parse "Point{1}")))
  (list :eval-error :struct-arity-mismatch "Point" 2 1))

(go-eval-test
  "struct: function takes/returns struct"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "func add(a, b Point) Point { return Point{a.x + b.x, a.y + b.y} }")))))
    (go-eval env (go-parse "add(Point{1, 2}, Point{3, 4})")))
  (list
    :go-struct "Point"
    (list (list "x" 4) (list "y" 6))))

(go-eval-test
  "method: p.Sum() = 3"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "func (p Point) Sum() int { return p.x + p.y }") (go-parse "p := Point{1, 2}")))))
    (go-eval env (go-parse "p.Sum()")))
  3)

(go-eval-test
  "method: p.Add(5) = 6 (with arg)"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "func (p Point) Add(d int) int { return p.x + d }") (go-parse "p := Point{1, 2}")))))
    (go-eval env (go-parse "p.Add(5)")))
  6)

(go-eval-test
  "method: pointer receiver works value-style in v0"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "func (p *Point) GetX() int { return p.x }") (go-parse "p := Point{1, 2}")))))
    (go-eval env (go-parse "p.GetX()")))
  1)

(go-eval-test
  "method: missing method → :no-such-method"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Point struct { x, y int }") (go-parse "p := Point{1, 2}")))))
    (go-eval env (go-parse "p.Ghost()")))
  (list :eval-error :no-such-method "Point" "Ghost"))

(go-eval-test
  "unary: -x"
  (go-eval (go-env-extend go-env-empty "x" 5) (go-parse "-x"))
  -5)

(go-eval-test "unary: !true → false" (gtev go-env-empty "!true") false)

(go-eval-test "unary: !false → true" (gtev go-env-empty "!false") true)

(go-eval-test
  "unary: -3 + 5 = 2 (unary binds tighter)"
  (gtev go-env-empty "-3 + 5")
  2)

(go-eval-test
  "e2e: count odd numbers in 1..10 = 5"
  (let
    ((env (go-eval-program go-env-empty
            (list (go-parse "odds := 0")
                  (go-parse "i := 1")
                  (go-parse "for i <= 10 { odds = odds + 1; i = i + 2 }")))))
    (go-env-lookup env "odds"))
  5)

(go-eval-test
  "e2e: factorial via method on Counter"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Acc struct { v int }") (go-parse "func (a Acc) Mul(x int) Acc { return Acc{a.v * x} }") (go-parse "a := Acc{1}") (go-parse "for i := 1; i <= 5; i++ { a = a.Mul(i) }")))))
    (go-eval env (go-parse "a.v")))
  120)

(go-eval-test
  "e2e: recursive fibonacci fib(10) = 55"
  (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(10)")))
  55)

(go-eval-test
  "e2e: struct + method + iterative loop"
  (let
    ((env (go-eval-program go-env-empty (list (go-parse "type Counter struct { n int }") (go-parse "func (c Counter) Bump() Counter { return Counter{c.n + 1} }") (go-parse "c := Counter{0}") (go-parse "for i := 0; i < 7; i++ { c = c.Bump() }")))))
    (go-eval env (go-parse "c.n")))
  7)

(go-eval-test
  "e2e: linear search returns index"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func find(a []int, x int) int { for i := 0; i < len(a); i++ { if a[i] == x { return i } } ; return -1 }") (go-parse "nums := []int{10, 20, 30, 40}")))))
    (go-eval env (go-parse "find(nums, 30)")))
  2)

(go-eval-test
  "e2e: linear search returns -1 when missing"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func find(a []int, x int) int { for i := 0; i < len(a); i++ { if a[i] == x { return i } } ; return -1 }") (go-parse "nums := []int{10, 20, 30}")))))
    (go-eval env (go-parse "find(nums, 99)")))
  -1)

(go-eval-test
  "defer: single defer runs after surrounding fn body returns"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func push2(c chan int) { c <- 2 }") (go-parse "func run(c chan int) { defer push2(c) ; c <- 1 }") (go-parse "run(ch)") (go-parse "first := <-ch") (go-parse "second := <-ch")))))
    (list (go-env-lookup env "first") (go-env-lookup env "second")))
  (list 1 2))

(go-eval-test
  "defer: multiple defers run LIFO"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func p2(c chan int) { c <- 2 }") (go-parse "func p3(c chan int) { c <- 3 }") (go-parse "func run(c chan int) { defer p2(c) ; defer p3(c) ; c <- 1 }") (go-parse "run(ch)") (go-parse "a := <-ch") (go-parse "b := <-ch") (go-parse "d := <-ch")))))
    (list
      (go-env-lookup env "a")
      (go-env-lookup env "b")
      (go-env-lookup env "d")))
  (list 1 3 2))

(go-eval-test
  "defer: arguments are evaluated at defer-time (not call-time)"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func pushN(c chan int, v int) { c <- v }") (go-parse "func run(c chan int) { x := 7 ; defer pushN(c, x) ; x = 99 }") (go-parse "run(ch)") (go-parse "got := <-ch")))))
    (go-env-lookup env "got"))
  7)

(go-eval-test
  "defer: runs even when fn returns early via return"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func note(c chan int) { c <- 42 }") (go-parse "func run(c chan int) int { defer note(c) ; return 1 }") (go-parse "r := run(ch)") (go-parse "n := <-ch")))))
    (list (go-env-lookup env "r") (go-env-lookup env "n")))
  (list 1 42))

(go-eval-test
  "defer: stack is frame-local — outer defers don't run on inner return"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func push1(c chan int) { c <- 1 }") (go-parse "func push2(c chan int) { c <- 2 }") (go-parse "func inner(c chan int) { defer push2(c) }") (go-parse "func outer(c chan int) { defer push1(c) ; inner(c) }") (go-parse "outer(ch)") (go-parse "a := <-ch") (go-parse "b := <-ch")))))
    (list (go-env-lookup env "a") (go-env-lookup env "b")))
  (list 2 1))

(go-eval-test
  "defer: in a loop, all defers fire on fn return (not loop iter)"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func pushI(c chan int, v int) { c <- v }") (go-parse "func loop(c chan int) { for i := 0; i < 4; i = i + 1 { defer pushI(c, i) } }") (go-parse "loop(ch)") (go-parse "a := <-ch") (go-parse "b := <-ch") (go-parse "d := <-ch") (go-parse "e := <-ch")))))
    (list
      (go-env-lookup env "a")
      (go-env-lookup env "b")
      (go-env-lookup env "d")
      (go-env-lookup env "e")))
  (list 3 2 1 0))

(go-eval-test
  "panic: uncaught panic surfaces as (:go-panic V) from program"
  (let
    ((r (go-eval-program go-env-builtins (list (go-parse "panic(\"boom\")")))))
    r)
  (list :go-panic "boom"))

(go-eval-test
  "panic inside fn: surfaces from fn call too"
  (let
    ((r (go-eval-program go-env-builtins (list (go-parse "func boom() { panic(\"oops\") }") (go-parse "boom()")))))
    r)
  (list :go-panic "oops"))

(go-eval-test
  "recover: deferred recover swallows panic, fn returns normally"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func safe() { defer recover() ; panic(\"x\") }") (go-parse "safe()") (go-parse "after := 42")))))
    (go-env-lookup env "after"))
  42)

(go-eval-test
  "recover: deferred recover captures the panic value"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func grab(c chan int) { r := recover() ; c <- r }") (go-parse "func safe(c chan int) { defer grab(c) ; panic(99) }") (go-parse "safe(ch)") (go-parse "got := <-ch")))))
    (go-env-lookup env "got"))
  99)

(go-eval-test
  "panic: propagates through intermediate frames without defers"
  (let
    ((r (go-eval-program go-env-builtins (list (go-parse "func inner() { panic(\"deep\") }") (go-parse "func middle() { inner() }") (go-parse "func outer() { middle() }") (go-parse "outer()")))))
    r)
  (list :go-panic "deep"))

(go-eval-test
  "recover: middle-frame defer catches panic from deeper frame"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func inner() { panic(\"deep\") }") (go-parse "func middle() { inner() }") (go-parse "func outer() { defer recover() ; middle() }") (go-parse "outer()") (go-parse "after := 7")))))
    (go-env-lookup env "after"))
  7)

(go-eval-test
  "goroutine panic: surfaces synchronously back to spawner (v0)"
  (let
    ((r (go-eval-program go-env-builtins (list (go-parse "func boom() { panic(\"goroutine\") }") (go-parse "go boom()")))))
    r)
  (list :go-panic "goroutine"))

(go-eval-test
  "goroutine panic + spawner-defer-recover catches it (v0 sync)"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func boom() { panic(\"g\") }") (go-parse "func main() { defer recover() ; go boom() }") (go-parse "main()") (go-parse "after := 11")))))
    (go-env-lookup env "after"))
  11)

(go-eval-test
  "defer order with recover: all defers run, recover catches"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func p2(c chan int) { c <- 2 }") (go-parse "func rec(c chan int) { recover() ; c <- 7 }") (go-parse "func safe(c chan int) { defer p2(c) ; defer rec(c) ; panic(0) }") (go-parse "safe(ch)") (go-parse "a := <-ch") (go-parse "b := <-ch")))))
    (list (go-env-lookup env "a") (go-env-lookup env "b")))
  (list 7 2))

(go-eval-test
  "defer fires when fn panics (not just normal return)"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func note(c chan int) { c <- 5 }") (go-parse "func safe(c chan int) { defer note(c) ; defer recover() ; panic(\"!\") }") (go-parse "safe(ch)") (go-parse "got := <-ch")))))
    (go-env-lookup env "got"))
  5)

(go-eval-test
  "panic with nil value: still surfaces as (:go-panic nil)"
  (let
    ((r (go-eval-program go-env-builtins (list (go-parse "panic(nil)")))))
    r)
  (list :go-panic nil))

(go-eval-test
  "panic inside loop body: aborts loop + propagates"
  (let
    ((r (go-eval-program go-env-builtins (list (go-parse "func find(x int) { for i := 0; i < 10; i = i + 1 { if i == x { panic(i) } } }") (go-parse "find(3)")))))
    r)
  (list :go-panic 3))

(go-eval-test
  "defer in panicking fn: still runs even though no return reached"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func mark(c chan int) { c <- 8 }") (go-parse "func inner(c chan int) { defer mark(c) ; panic(\"!\") }") (go-parse "func outer(c chan int) { defer recover() ; inner(c) }") (go-parse "outer(ch)") (go-parse "got := <-ch")))))
    (go-env-lookup env "got"))
  8)

(go-eval-test
  "defer fn captures args by value, not reference (re-confirm)"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "func pushN(c chan int, v int) { c <- v }") (go-parse "func run(c chan int) { defer recover() ; x := 5 ; defer pushN(c, x) ; x = 999 ; panic(\"k\") }") (go-parse "run(ch)") (go-parse "got := <-ch")))))
    (go-env-lookup env "got"))
  5)

(go-eval-test
  "generic: identity Id[T any](x) returns x at runtime"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func Id[T any](x T) T { return x }") (go-parse "r := Id(42)")))))
    (go-env-lookup env "r"))
  42)

(go-eval-test
  "generic: Id works with strings (type erasure)"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func Id[T any](x T) T { return x }") (go-parse "r := Id(\"hi\")")))))
    (go-env-lookup env "r"))
  "hi")

(go-eval-test
  "generic: Map[T, U] over []int with double — produces []int"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func Map[T any, U any](xs []T, f func(T) U) []U { r := []int{} ; for i, v := range xs { r = append(r, f(v)) } ; return r }") (go-parse "func dbl(x int) int { return x * 2 }") (go-parse "out := Map([]int{1, 2, 3}, dbl)") (go-parse "first := out[0]") (go-parse "second := out[1]") (go-parse "third := out[2]")))))
    (list
      (go-env-lookup env "first")
      (go-env-lookup env "second")
      (go-env-lookup env "third")))
  (list 2 4 6))

(go-eval-test
  "generic: Filter[T any] keeps elements satisfying predicate"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func Filter[T any](xs []T, p func(T) bool) []T { r := []int{} ; for i, v := range xs { if p(v) { r = append(r, v) } } ; return r }") (go-parse "func gt3(x int) bool { return x > 3 }") (go-parse "out := Filter([]int{1, 2, 3, 4, 5, 6}, gt3)") (go-parse "n := len(out)") (go-parse "first := out[0]") (go-parse "last := out[2]")))))
    (list
      (go-env-lookup env "n")
      (go-env-lookup env "first")
      (go-env-lookup env "last")))
  (list 3 4 6))

(go-eval-test
  "generic: Reduce[T, U] sums []int with seed 0"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func Reduce[T any, U any](xs []T, seed U, f func(U, T) U) U { acc := seed ; for i, v := range xs { acc = f(acc, v) } ; return acc }") (go-parse "func add(a int, b int) int { return a + b }") (go-parse "total := Reduce([]int{10, 20, 30, 40}, 0, add)")))))
    (go-env-lookup env "total"))
  100)

(go-eval-test
  "generic: First[T any]([]T) T returns element zero"
  (let
    ((env (go-eval-program go-env-builtins (list (go-parse "func First[T any](xs []T) T { return xs[0] }") (go-parse "v := First([]int{42, 99})")))))
    (go-env-lookup env "v"))
  42)

(define
  go-eval-test-summary
  (str "eval " go-eval-test-pass "/" go-eval-test-count))
