Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 28s
Phase 5 cont. Adds `select` statement evaluation:
go-select-try-case env COMM →
:not-ready / extended-env / :eval-error
go-select-pick env CASES DEFAULT-OR-NIL →
body-result / blocked-error
go-eval-select-stmt env STMT — public entry
Walks cases in declared order:
* :send case — always ready in v0 (unbounded buffer). Sends value
via go-chan-send! and returns env unchanged.
* :short-decl / :assign case — RHS expected to be unary <- on a
channel. Ready iff go-chan-len > 0; on success, recv-into-var
binds the new value in env.
* Bare recv (:app (:var "<-") [CHAN]) — ready iff len > 0; consumes
the value (discarded).
* :default — deferred until end of walk. Runs if no other case
ready. Absence + no ready case → (:eval-error :select-blocked-
no-default).
New `go-chan-len` accessor on the channel closure-bundle so the
select can peek without consuming.
Subtle bug fix: the :select stmt branch in go-eval-stmt was returning
the old env instead of the env returned by the case body. Assignments
inside select cases (`select { case <-ch: x = 1 ; default: x = 99 }`)
now stick.
Tests (6):
default fires when no case ready
recv case fires when ready
recv-into-var binds the value
send case always ready
picks first ready case (deterministic order in v0)
no default + nothing ready → blocked error
combined with goroutine fan-in
runtime 18/18, total 475/475.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
158 lines
5.1 KiB
Plaintext
158 lines
5.1 KiB
Plaintext
;; Go runtime tests — goroutines + channels.
|
|
|
|
(define go-rt-test-count 0)
|
|
(define go-rt-test-pass 0)
|
|
(define go-rt-test-fails (list))
|
|
|
|
(define
|
|
go-rt-test
|
|
(fn
|
|
(name actual expected)
|
|
(set! go-rt-test-count (+ go-rt-test-count 1))
|
|
(if
|
|
(= actual expected)
|
|
(set! go-rt-test-pass (+ go-rt-test-pass 1))
|
|
(append! go-rt-test-fails {:name name :expected expected :actual actual}))))
|
|
|
|
;; ── channel primitives (direct API, no source parsing) ─────────
|
|
(go-rt-test "chan: make returns a chan value" (go-chan? (go-make-chan)) true)
|
|
|
|
(go-rt-test
|
|
"chan: distinct channels have distinct identity"
|
|
(= (go-make-chan) (go-make-chan))
|
|
false)
|
|
|
|
(go-rt-test
|
|
"chan: send + recv round-trip"
|
|
(let
|
|
((ch (go-make-chan)))
|
|
(go-chan-send! ch 42)
|
|
(go-chan-recv! ch))
|
|
42)
|
|
|
|
(go-rt-test
|
|
"chan: empty recv returns :empty marker"
|
|
(let ((ch (go-make-chan))) (go-chan-recv! ch))
|
|
:empty)
|
|
|
|
(go-rt-test
|
|
"chan: FIFO order"
|
|
(let
|
|
((ch (go-make-chan)))
|
|
(go-chan-send! ch 1)
|
|
(go-chan-send! ch 2)
|
|
(go-chan-send! ch 3)
|
|
(list (go-chan-recv! ch) (go-chan-recv! ch) (go-chan-recv! ch)))
|
|
(list 1 2 3))
|
|
|
|
(go-rt-test
|
|
"chan: closed? flag flips"
|
|
(let
|
|
((ch (go-make-chan)))
|
|
(let
|
|
((before (go-chan-closed? ch)))
|
|
(go-chan-close! ch)
|
|
(list before (go-chan-closed? ch))))
|
|
(list false true))
|
|
|
|
;; ── source-level: make / send / recv / close ───────────────────
|
|
(go-rt-test
|
|
"src: ch := make() returns chan"
|
|
(go-chan?
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()")))))
|
|
(go-env-lookup env "ch")))
|
|
true)
|
|
|
|
(go-rt-test
|
|
"src: ch <- 5 then <-ch = 5"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "ch <- 5")))))
|
|
(go-eval env (go-parse "<-ch")))
|
|
5)
|
|
|
|
(go-rt-test
|
|
"src: go + chan ping-pong"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "func sender(c chan int) { c <- 99 }") (go-parse "ch := make()") (go-parse "go sender(ch)")))))
|
|
(go-eval env (go-parse "<-ch")))
|
|
99)
|
|
|
|
(go-rt-test
|
|
"src: close(ch) marks it closed"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "close(ch)")))))
|
|
(go-chan-closed? (go-env-lookup env "ch")))
|
|
true)
|
|
|
|
(go-rt-test
|
|
"src: multiple goroutines feeding one channel"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "func push(c chan int, v int) { c <- v }") (go-parse "ch := make()") (go-parse "go push(ch, 1)") (go-parse "go push(ch, 2)") (go-parse "go push(ch, 3)")))))
|
|
(list
|
|
(go-eval env (go-parse "<-ch"))
|
|
(go-eval env (go-parse "<-ch"))
|
|
(go-eval env (go-parse "<-ch"))))
|
|
(list 1 2 3))
|
|
|
|
(go-rt-test
|
|
"src: worker pattern — send sum back"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "func work(c chan int, a int, b int) { c <- a + b }") (go-parse "result := make()") (go-parse "go work(result, 7, 13)")))))
|
|
(go-eval env (go-parse "<-result")))
|
|
20)
|
|
|
|
;; ── report ─────────────────────────────────────────────────────
|
|
(go-rt-test
|
|
"select: default runs when no case is ready"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "x := 0") (go-parse "select { case <-ch: x = 1 ; default: x = 99 }")))))
|
|
(go-env-lookup env "x"))
|
|
99)
|
|
|
|
(go-rt-test
|
|
"select: recv case fires when ready"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "ch <- 7") (go-parse "x := 0") (go-parse "select { case <-ch: x = 1 ; default: x = 99 }")))))
|
|
(go-env-lookup env "x"))
|
|
1)
|
|
|
|
(go-rt-test
|
|
"select: recv-into-var binds the value"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "ch <- 42") (go-parse "select { case v := <-ch: v }")))))
|
|
(go-env-lookup env "v"))
|
|
42)
|
|
|
|
(go-rt-test
|
|
"select: send case (always ready in v0)"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()") (go-parse "select { case ch <- 5: }")))))
|
|
(go-chan-len (go-env-lookup env "ch")))
|
|
1)
|
|
|
|
(go-rt-test
|
|
"select: picks first ready case"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "a := make()") (go-parse "b := make()") (go-parse "b <- 100") (go-parse "x := 0") (go-parse "select { case <-a: x = 1 ; case <-b: x = 2 ; default: x = 99 }")))))
|
|
(go-env-lookup env "x"))
|
|
2)
|
|
|
|
(go-rt-test
|
|
"select: no default + nothing ready → blocked error"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "ch := make()")))))
|
|
(go-eval-stmt env (go-parse "select { case <-ch: }") (list)))
|
|
(list :eval-error :select-blocked-no-default))
|
|
|
|
(go-rt-test
|
|
"select: combined with goroutine fan-in"
|
|
(let
|
|
((env (go-eval-program go-env-builtins (list (go-parse "func push(c chan int, v int) { c <- v }") (go-parse "ch := make()") (go-parse "go push(ch, 7)") (go-parse "result := 0") (go-parse "select { case v := <-ch: result = v ; default: result = -1 }")))))
|
|
(go-env-lookup env "result"))
|
|
7)
|
|
|
|
(define
|
|
go-rt-test-summary
|
|
(str "runtime " go-rt-test-pass "/" go-rt-test-count))
|