go: parse.sx — switch + select + 8 tests; stmts done [shapes-scheduler]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 32s

Adds Go's switch and select statements:

  switch TAG { case V1, V2: a; case V3: b; default: c }
  switch { case cond: ... }                            — tagless
  select { case x := <-ch: a; case ch <- v: b; default: c }

AST shapes:
  (list :switch TAG CASES)             — TAG nil for tagless
  (list :case VALUES BODY)             — VALUES is expr-list
  (list :select CASES)
  (list :select-case COMM-STMT BODY)   — COMM-STMT is send/recv-assign/bare-recv
  (list :default BODY)

gp-parse-case-body reads stmts until the next case/default/}/eof
without consuming the terminator — used by both switch and select.

select-case parsing reuses gp-parse-stmt for the comm-stmt, so all
four shapes (send, x := <-ch, x = <-ch, bare <-ch) fall out from the
existing stmt parser. Composite-lit suppression is engaged for the
switch tag expression.

Type-switch (`switch v := x.(type) { case int: ... }`) is the one
deferred shape; needs the `.(type)` pseudo-syntax recognised in the
expression layer. Phase 2 statement coverage is otherwise complete.

This is also a chiselling iteration for scheduler sister kit. Diary
updated with select-case design insights:

  * All four select-case shapes share (list :select-case STMT BODY)
    — kit primitive sched-select accepts a uniform list of cases.
  * Default vs no-default determines blocking semantics. Erlang's
    `receive ... after Timeout -> ...` is the analogue — both fit
    "non-blocking fallback case" in the kit API.

parse 169/169, total 298/298.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 20:29:37 +00:00
parent 171a08a2f8
commit 44fb231391
6 changed files with 253 additions and 12 deletions

View File

@@ -1047,6 +1047,86 @@
(list :defer (ast-app (ast-var "cleanup") (list)))
(list :go (ast-app (ast-var "worker") (list)))))))
(go-parse-test
"switch: tagged with two cases"
(go-parse "switch x { case 1: a; case 2: b }")
(list
:switch (ast-var "x")
(list
(list :case (list (ast-literal "1")) (list (ast-var "a")))
(list :case (list (ast-literal "2")) (list (ast-var "b"))))))
(go-parse-test
"switch: multi-value case"
(go-parse "switch x { case 1, 2: a; case 3: b }")
(list
:switch (ast-var "x")
(list
(list
:case (list (ast-literal "1") (ast-literal "2"))
(list (ast-var "a")))
(list :case (list (ast-literal "3")) (list (ast-var "b"))))))
(go-parse-test
"switch: tagless (if-else chain)"
(go-parse "switch { case x > 0: a; case x < 0: b; default: c }")
(list
:switch nil
(list
(list
:case (list
(ast-app (ast-var ">") (list (ast-var "x") (ast-literal "0"))))
(list (ast-var "a")))
(list
:case (list
(ast-app (ast-var "<") (list (ast-var "x") (ast-literal "0"))))
(list (ast-var "b")))
(list :default (list (ast-var "c"))))))
(go-parse-test
"switch: with default only"
(go-parse "switch x { default: y }")
(list :switch (ast-var "x") (list (list :default (list (ast-var "y"))))))
(go-parse-test
"select: recv-into-var case"
(go-parse "select { case x := <-ch: a }")
(list
:select (list
(list
:select-case (list
:short-decl (list (ast-var "x"))
(list (ast-app (ast-var "<-") (list (ast-var "ch")))))
(list (ast-var "a"))))))
(go-parse-test
"select: send case"
(go-parse "select { case ch <- v: done() }")
(list
:select (list
(list
:select-case (list :send (ast-var "ch") (ast-var "v"))
(list (ast-app (ast-var "done") (list)))))))
(go-parse-test
"select: recv (discard) case"
(go-parse "select { case <-ch: a }")
(list
:select (list
(list
:select-case (ast-app (ast-var "<-") (list (ast-var "ch")))
(list (ast-var "a"))))))
(go-parse-test
"select: with default (non-blocking)"
(go-parse "select { case <-ch: a; default: b }")
(list
:select (list
(list
:select-case (ast-app (ast-var "<-") (list (ast-var "ch")))
(list (ast-var "a")))
(list :default (list (ast-var "b"))))))
(go-parse-test "non-primary: '+'" (go-parse "+") nil)
(go-parse-test "non-primary: empty" (go-parse "") nil)