go: parse.sx — go/defer/send/for-range + 9 tests [shapes-scheduler]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 28s

Adds Go's concurrency + iteration primitives to the statement parser:

  go EXPR                     →  (list :go EXPR)
  defer EXPR                  →  (list :defer EXPR)
  ch <- v                     →  (list :send CHAN VALUE)
  for range COLL { ... }      →  (list :range-for nil nil nil COLL BODY)
  for k := range C { ... }    →  (list :range-for :short-decl KEY nil COLL BODY)
  for k, v := range C { }     →  (list :range-for :short-decl KEY VAL COLL BODY)
  for k, v = range C { ... }  →  (list :range-for :assign KEY VAL COLL BODY)

gp-for-find-range pre-scans the for-header (to '{' or eof) looking
for the 'range' keyword; if present, dispatches to gp-parse-for-range
which handles the four range shapes. C-style and while-like and
infinite are now in gp-parse-for-c-style — gp-parse-for is just a
dispatcher.

Send statement detection lives in the LHS-list branch of gp-parse-stmt:
after parsing a single LHS expression, '<-' triggers (:send LHS RHS).
Channel-recv (`<-ch`) was already parsed as unary `<-` in the expression
layer, so both directions cover.

This is the **chiselling-relevant iteration** for the scheduler sister
kit: the AST shapes Go-on-SX will eventually feed into the kit's
scheduler primitives (sched-spawn, sched-defer, chan-op) have landed.
Sister-plan diary updated with three design insights:

  * :go / :defer both wrap a single expr — kit's sched-spawn should
    accept a thunk uniformly across Erlang's spawn(M,F,A) and Go's
    go fn().
  * :send carries CHAN+VALUE symmetrically with the unary <- recv —
    both reduce to (chan-op direction chan value) in the kit.
  * `for v := range ch` uses the same :range-for shape as range-over-
    slice; the scheduler kit's range dispatch is where chan-recv ⇄
    iteration polymorphism lives.

parse 161/161, total 290/290.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 20:24:23 +00:00
parent ba41f8a580
commit 171a08a2f8
6 changed files with 233 additions and 40 deletions

View File

@@ -977,6 +977,76 @@
(go-parse "if Foo {}")
(list :if (ast-var "Foo") (list :block (list)) nil))
(go-parse-test
"stmt: go f()"
(go-parse "go f()")
(list :go (ast-app (ast-var "f") (list))))
(go-parse-test
"stmt: go method(x, y)"
(go-parse "go obj.method(x, y)")
(list
:go (ast-app
(list :select (ast-var "obj") "method")
(list (ast-var "x") (ast-var "y")))))
(go-parse-test
"stmt: defer cleanup()"
(go-parse "defer cleanup()")
(list :defer (ast-app (ast-var "cleanup") (list))))
(go-parse-test
"stmt: send ch <- v"
(go-parse "ch <- 42")
(list :send (ast-var "ch") (ast-literal "42")))
(go-parse-test
"for-range: no kv — for range coll { }"
(go-parse "for range coll { }")
(list :range-for nil nil nil (ast-var "coll") (list :block (list))))
(go-parse-test
"for-range: key only — for k := range m { }"
(go-parse "for k := range m { }")
(list
:range-for :short-decl
(ast-var "k")
nil
(ast-var "m")
(list :block (list))))
(go-parse-test
"for-range: k, v := range m"
(go-parse "for k, v := range m { }")
(list
:range-for :short-decl
(ast-var "k")
(ast-var "v")
(ast-var "m")
(list :block (list))))
(go-parse-test
"for-range: assign form k = range coll"
(go-parse "for k = range coll { }")
(list
:range-for :assign
(ast-var "k")
nil
(ast-var "coll")
(list :block (list))))
(go-parse-test
"concurrency: defer + go in func body"
(go-parse "func main() { defer cleanup(); go worker() }")
(list
:func-decl "main"
(list)
(list)
(list
:block (list
(list :defer (ast-app (ast-var "cleanup") (list)))
(list :go (ast-app (ast-var "worker") (list)))))))
(go-parse-test "non-primary: '+'" (go-parse "+") nil)
(go-parse-test "non-primary: empty" (go-parse "") nil)