go: parse.sx — composite literals + 8 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s

Adds Go composite literals:
  T{}                                  empty
  T{1, 2}                              positional
  T{X: 1, Y: 2}                        keyed
  []int{1, 2, 3}                       slice
  [3]int{1, 2, 3}                      array
  map[string]int{"a": 1}               map
  pkg.Point{1, 2}                      qualified
  []Point{Point{1,2}, Point{3,4}}      nested

AST: (list :composite TYPE-OR-EXPR ELEMS). Each element is an
expression or (list :kv KEY VALUE).

Two parser entry points feed the same AST:
  * gp-parse-primary picks up type-prefixed composites by seeing
    a literal-type starter ([, map, struct) and parsing a type
    first, then optionally a '{' body.
  * The postfix loop picks up ident-prefixed composites — after
    any base expression, '{' wraps it as a composite literal.

Known limitation flagged in plan: when statement parsing arrives,
the postfix '{' branch will misread `if cond { ... }` as a composite
literal. Standard fix: parser-mode flag suppressing composite-lit
disambiguation in control-flow expression positions. Added to plan.

Elided types in nested composites (`[][]int{{1,2},{3,4}}` with the
inner `{1,2}` typed implicitly) deferred.

parse 114/114, total 243/243.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 08:21:47 +00:00
parent 48379e04bc
commit 632e06d3cf
5 changed files with 147 additions and 9 deletions

View File

@@ -63,7 +63,58 @@
(do (gp-advance!) (ast-literal v))
(= ty "ident")
(do (gp-advance!) (ast-var v))
;; Type-prefixed composite literal starters: [, map, struct.
;; We parse a full type, then if '{' follows it's a composite
;; literal; otherwise the type is the operand (the caller
;; decides what to do — currently statement parsing isn't here).
(or (and (= ty "op") (= v "["))
(and (= ty "keyword")
(or (= v "map") (= v "struct"))))
(let ((tytree (gp-parse-type)))
(cond
(and (= (gp-tok-type) "op") (= (gp-tok-value) "{"))
(do
(gp-advance!)
(list :composite tytree (gp-parse-composite-elems)))
:else tytree))
:else nil))))
(define
gp-parse-composite-elems
;; Caller has consumed '{'. Parses elements until '}'.
;; Each element: either an expression, or KEY ':' VALUE.
;; KEY can be an ident (struct field name) or an expression
;; (map key) — parser is permissive, types phase disambiguates.
;; Returns a list of expression nodes or (list :kv KEY VALUE).
(fn
()
(let ((elems (list)))
(define
gp-comp-loop
(fn
()
(cond
(= (gp-tok-type) "semi")
(do (gp-advance!) (gp-comp-loop))
(and (= (gp-tok-type) "op") (= (gp-tok-value) "}"))
(gp-advance!)
:else
(do
(let ((first (gp-parse-expr 1)))
(cond
(and (= (gp-tok-type) "op")
(= (gp-tok-value) ":"))
(do
(gp-advance!)
(let ((val (gp-parse-expr 1)))
(append! elems (list :kv first val))))
:else
(append! elems first)))
(when (and (= (gp-tok-type) "op")
(= (gp-tok-value) ","))
(gp-advance!))
(gp-comp-loop)))))
(gp-comp-loop)
elems)))
(define
gp-parse-call-args
;; Parse comma-separated args inside (...). Caller has already
@@ -413,6 +464,14 @@
(do
(gp-advance!)
(gp-postfix-loop (gp-parse-bracket base)))
;; Ident-prefixed composite literal: T{...}. The base is
;; the AST expression for the type-name (an ast-var or a
;; :select node); a later phase resolves it as a type.
(and (= (get tok :type) "op") (= (get tok :value) "{"))
(do
(gp-advance!)
(gp-postfix-loop
(list :composite base (gp-parse-composite-elems))))
:else base)))))
(define
gp-unary-ops

View File

@@ -1,10 +1,10 @@
{
"language": "go",
"total_pass": 235,
"total": 235,
"total_pass": 243,
"total": 243,
"suites": [
{"name":"lex","pass":129,"total":129,"status":"ok"},
{"name":"parse","pass":106,"total":106,"status":"ok"},
{"name":"parse","pass":114,"total":114,"status":"ok"},
{"name":"types","pass":0,"total":0,"status":"pending"},
{"name":"eval","pass":0,"total":0,"status":"pending"},
{"name":"runtime","pass":0,"total":0,"status":"pending"},

View File

@@ -1,11 +1,11 @@
# Go-on-SX Scoreboard
**Total: 235 / 235 tests passing**
**Total: 243 / 243 tests passing**
| | Suite | Pass | Total |
|---|---|---|---|
| ✅ | lex | 129 | 129 |
| ✅ | parse | 106 | 106 |
| ✅ | parse | 114 | 114 |
| ⬜ | types | 0 | 0 |
| ⬜ | eval | 0 | 0 |
| ⬜ | runtime | 0 | 0 |

View File

@@ -650,6 +650,70 @@
(list (list :ty-name "int")))
(list :method "Close" (list) (list))))))
(go-parse-test
"comp: Point{} (empty)"
(go-parse "Point{}")
(list :composite (ast-var "Point") (list)))
(go-parse-test
"comp: Point{1, 2} (positional)"
(go-parse "Point{1, 2}")
(list
:composite (ast-var "Point")
(list (ast-literal "1") (ast-literal "2"))))
(go-parse-test
"comp: Point{X: 1, Y: 2} (keyed)"
(go-parse "Point{X: 1, Y: 2}")
(list
:composite (ast-var "Point")
(list
(list :kv (ast-var "X") (ast-literal "1"))
(list :kv (ast-var "Y") (ast-literal "2")))))
(go-parse-test
"comp: []int{1, 2, 3} (slice literal)"
(go-parse "[]int{1, 2, 3}")
(list
:composite (list :ty-slice (list :ty-name "int"))
(list (ast-literal "1") (ast-literal "2") (ast-literal "3"))))
(go-parse-test
"comp: [3]int{1, 2, 3} (array literal)"
(go-parse "[3]int{1, 2, 3}")
(list
:composite (list :ty-array (ast-literal "3") (list :ty-name "int"))
(list (ast-literal "1") (ast-literal "2") (ast-literal "3"))))
(go-parse-test
"comp: map[string]int{\"a\": 1, \"b\": 2} (map literal)"
(go-parse "map[string]int{\"a\": 1, \"b\": 2}")
(list
:composite (list :ty-map (list :ty-name "string") (list :ty-name "int"))
(list
(list :kv (ast-literal "a") (ast-literal "1"))
(list :kv (ast-literal "b") (ast-literal "2")))))
(go-parse-test
"comp: pkg.Point{1, 2} (qualified type)"
(go-parse "pkg.Point{1, 2}")
(list
:composite (list :select (ast-var "pkg") "Point")
(list (ast-literal "1") (ast-literal "2"))))
(go-parse-test
"comp: nested — []Point{Point{1,2}, Point{3,4}}"
(go-parse "[]Point{Point{1, 2}, Point{3, 4}}")
(list
:composite (list :ty-slice (list :ty-name "Point"))
(list
(list
:composite (ast-var "Point")
(list (ast-literal "1") (ast-literal "2")))
(list
:composite (ast-var "Point")
(list (ast-literal "3") (ast-literal "4"))))))
(go-parse-test "non-primary: '+'" (go-parse "+") nil)
(go-parse-test "non-primary: empty" (go-parse "") nil)