go: parse.sx — struct type expressions + 8 tests [proposes-ast]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
Adds Go struct types to gp-parse-type:
struct {} → (list :ty-struct ())
struct { x int } → (list :ty-struct [(:field [x] (:ty-name int))])
struct { x int; y string } → multiple field rows
struct { x, y int } → shared-type row (NAMES is a list)
struct { inner struct { x int } } → nested struct types
gp-parse-struct-fields walks field rows tolerating ASI-inserted semis
(from newlines between fields). Each row collects 1+ names separated
by commas, then a single type that all the names share. Embedded
fields, field tags, and methods are deferred.
The :field shape (NAMES + TYPE) is a recurring multi-language pattern —
struct fields, func params, method receivers, var decls all map to it.
Logged in Blockers as a canonical-AST candidate
(ast-binding-group / ast-named-of-type); worth promoting once a second
consumer (parser of another statically-typed guest, or Go func decls)
exercises the same shape.
parse 98/98, total 227/227.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -95,6 +95,49 @@
|
||||
:else nil)))
|
||||
(gp-args-rest)
|
||||
args)))))
|
||||
(define
|
||||
gp-parse-struct-fields
|
||||
;; Caller positioned BEFORE '{'. Parses fields until '}'.
|
||||
;; field := name [, name]* TYPE
|
||||
;; Tolerates ASI-inserted semis between fields. Embedded fields
|
||||
;; (anonymous type without preceding names) and field tags are
|
||||
;; deferred. Returns a list of (list :field NAMES TYPE).
|
||||
(fn
|
||||
()
|
||||
(when (and (= (gp-tok-type) "op") (= (gp-tok-value) "{"))
|
||||
(gp-advance!))
|
||||
(let ((fields (list)))
|
||||
(define
|
||||
gp-struct-loop
|
||||
(fn
|
||||
()
|
||||
(cond
|
||||
(= (gp-tok-type) "semi")
|
||||
(do (gp-advance!) (gp-struct-loop))
|
||||
(and (= (gp-tok-type) "op") (= (gp-tok-value) "}"))
|
||||
(gp-advance!)
|
||||
(= (gp-tok-type) "ident")
|
||||
(do
|
||||
(let ((names (list (gp-tok-value))))
|
||||
(gp-advance!)
|
||||
(define
|
||||
gp-names-rest
|
||||
(fn
|
||||
()
|
||||
(when (and (= (gp-tok-type) "op")
|
||||
(= (gp-tok-value) ","))
|
||||
(gp-advance!)
|
||||
(when (= (gp-tok-type) "ident")
|
||||
(append! names (gp-tok-value))
|
||||
(gp-advance!))
|
||||
(gp-names-rest))))
|
||||
(gp-names-rest)
|
||||
(let ((ty (gp-parse-type)))
|
||||
(append! fields (list :field names ty))))
|
||||
(gp-struct-loop))
|
||||
:else nil)))
|
||||
(gp-struct-loop)
|
||||
fields)))
|
||||
(define
|
||||
gp-parse-func-type-params
|
||||
;; Anonymous-only func-type params: caller is positioned BEFORE
|
||||
@@ -204,6 +247,10 @@
|
||||
(let ((params (gp-parse-func-type-params)))
|
||||
(let ((results (gp-parse-func-type-results)))
|
||||
(list :ty-func params results))))
|
||||
(and (= (gp-tok-type) "keyword") (= (gp-tok-value) "struct"))
|
||||
(do
|
||||
(gp-advance!)
|
||||
(list :ty-struct (gp-parse-struct-fields)))
|
||||
(= (gp-tok-type) "ident")
|
||||
(let ((name (gp-tok-value)))
|
||||
(gp-advance!)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"language": "go",
|
||||
"total_pass": 219,
|
||||
"total": 219,
|
||||
"total_pass": 227,
|
||||
"total": 227,
|
||||
"suites": [
|
||||
{"name":"lex","pass":129,"total":129,"status":"ok"},
|
||||
{"name":"parse","pass":90,"total":90,"status":"ok"},
|
||||
{"name":"parse","pass":98,"total":98,"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"},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Go-on-SX Scoreboard
|
||||
|
||||
**Total: 219 / 219 tests passing**
|
||||
**Total: 227 / 227 tests passing**
|
||||
|
||||
| | Suite | Pass | Total |
|
||||
|---|---|---|---|
|
||||
| ✅ | lex | 129 | 129 |
|
||||
| ✅ | parse | 90 | 90 |
|
||||
| ✅ | parse | 98 | 98 |
|
||||
| ⬜ | types | 0 | 0 |
|
||||
| ⬜ | eval | 0 | 0 |
|
||||
| ⬜ | runtime | 0 | 0 |
|
||||
|
||||
@@ -507,6 +507,76 @@
|
||||
:ty-chan :both
|
||||
(list :ty-func (list) (list (list :ty-name "int"))))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: struct {} (empty)"
|
||||
(go-parse "v.(struct {})")
|
||||
(list :assert (ast-var "v") (list :ty-struct (list))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: struct { x int }"
|
||||
(go-parse "v.(struct { x int })")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list :ty-struct (list (list :field (list "x") (list :ty-name "int"))))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: struct { x int; y string } (multiple fields)"
|
||||
(go-parse "v.(struct { x int; y string })")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-struct (list
|
||||
(list :field (list "x") (list :ty-name "int"))
|
||||
(list :field (list "y") (list :ty-name "string"))))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: struct { x, y int } (shared type)"
|
||||
(go-parse "v.(struct { x, y int })")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-struct (list (list :field (list "x" "y") (list :ty-name "int"))))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: struct { p *T } (pointer field)"
|
||||
(go-parse "v.(struct { p *T })")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-struct (list (list :field (list "p") (list :ty-ptr (list :ty-name "T")))))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: struct { items []int } (slice field)"
|
||||
(go-parse "v.(struct { items []int })")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-struct (list
|
||||
(list :field (list "items") (list :ty-slice (list :ty-name "int")))))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: struct { a int; b, c string; d *T } (mixed)"
|
||||
(go-parse "v.(struct { a int; b, c string; d *T })")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-struct (list
|
||||
(list :field (list "a") (list :ty-name "int"))
|
||||
(list :field (list "b" "c") (list :ty-name "string"))
|
||||
(list :field (list "d") (list :ty-ptr (list :ty-name "T")))))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: nested struct { inner struct { x int } }"
|
||||
(go-parse "v.(struct { inner struct { x int } })")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-struct (list
|
||||
(list
|
||||
:field (list "inner")
|
||||
(list
|
||||
:ty-struct (list (list :field (list "x") (list :ty-name "int")))))))))
|
||||
|
||||
(go-parse-test "non-primary: '+'" (go-parse "+") nil)
|
||||
|
||||
(go-parse-test "non-primary: empty" (go-parse "") nil)
|
||||
|
||||
@@ -176,9 +176,10 @@ Progress-log line → push `origin/loops/go`.
|
||||
`*T` / `**T`); full type grammar still pending below.
|
||||
- [/] Type expressions: **slice `[]T`, array `[N]T`, map `map[K]V`, chan
|
||||
`chan T` / `chan<- T` / `<-chan T`, pointer `*T`, named `T`,
|
||||
qualified `pkg.T`, func `func(...) ...` (anonymous params, single
|
||||
or multi return)** all done — kit has no type primitives.
|
||||
struct / interface / variadic / named-params / generics deferred.
|
||||
qualified `pkg.T`, func `func(...) ...`, struct `struct{...}` with
|
||||
shared-type field rows (`x, y int`)** all done — kit has no type
|
||||
primitives. Interface, embedded fields, field tags, variadic,
|
||||
named func-params, generics deferred.
|
||||
- [ ] Composite literals: `T{...}`, `[]T{...}`, `map[K]V{...}`,
|
||||
`struct{...}{...}`.
|
||||
- [ ] Declarations: `package`, `import`, `var`, `const`, `type`, `func`
|
||||
@@ -189,8 +190,8 @@ Progress-log line → push `origin/loops/go`.
|
||||
- [ ] End-to-end: hello-world, fibonacci, FizzBuzz, goroutine ping-pong,
|
||||
struct + method.
|
||||
- **Acceptance:** parse/ suite at 80+ tests. **Acceptance bar crossed:
|
||||
90/90.** Remaining sub-items (struct/interface types, composite
|
||||
literals, decls, stmts, e2e) still keep Phase 2 open ⬜.
|
||||
98/98.** Remaining sub-items (interface types, composite literals,
|
||||
decls, stmts, e2e) still keep Phase 2 open ⬜.
|
||||
|
||||
### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜
|
||||
- **Independent implementation.** Do NOT use lib/guest/static-types-
|
||||
@@ -450,7 +451,22 @@ Observed from building the Go parser:
|
||||
|
||||
Minimal repro: see `lib/go/parse.sx#gp-parse-postfix` + `gp-parse-bracket`.
|
||||
|
||||
4. **No type-expression primitives.** Every statically-typed guest needs
|
||||
4. **No "named binding(s) of a type" node.** Building struct types
|
||||
surfaced a shape that recurs everywhere:
|
||||
|
||||
```
|
||||
(list :field NAMES TYPE)
|
||||
```
|
||||
|
||||
Same shape appears in: struct fields (`x, y int`), func parameters
|
||||
(`func(a, b int, c string)`), method receivers (`m(a, b int)`),
|
||||
variable declarations (`var x, y int`). Three Phase-2 sub-deliverables
|
||||
(struct fields, func decls, var decls) all want this shape. Promoting
|
||||
it once means Rust struct fields, Swift parameters, TS class fields,
|
||||
Java method signatures all get a free home. Candidate canonical name:
|
||||
`ast-binding-group` or `ast-named-of-type`.
|
||||
|
||||
5. **No type-expression primitives.** Every statically-typed guest needs
|
||||
to express types in source. Proposed canonical shapes:
|
||||
|
||||
```
|
||||
@@ -492,6 +508,15 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`.
|
||||
|
||||
_Newest first. Append one dated entry per commit._
|
||||
|
||||
- 2026-05-27 — Phase 2 cont.: struct-type expressions. `struct {}`,
|
||||
`struct { x int }`, `struct { x int; y string }`, `struct { x, y int }`
|
||||
(shared type), nested struct fields. `gp-parse-struct-fields` walks
|
||||
field rows tolerating ASI semis; each row is a name list + type. AST:
|
||||
`(list :ty-struct FIELDS)` with each field `(list :field NAMES TYPE)`.
|
||||
Embedded fields, tags, and methods deferred. +8 tests, parse 98/98,
|
||||
total 227/227. `[proposes-ast]` — the `:field` shape (NAMES + TYPE)
|
||||
recurs in func params, method receivers, var decls; flagged in
|
||||
Blockers as a unified `ast-binding-group` candidate for the kit.
|
||||
- 2026-05-27 — Phase 2 cont.: func-type expressions. `func()`,
|
||||
`func() int`, `func(int, string)`, `func(int) string`,
|
||||
`func() (int, error)`. AST shape `(list :ty-func PARAMS RESULTS)`
|
||||
|
||||
Reference in New Issue
Block a user