go: parse.sx — slice/array/map/chan type expressions + 11 tests; parse acceptance crossed [proposes-ast]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 18s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 18s
Adds the bulk of Go's type-expression grammar: []T → (list :ty-slice T) [N]T → (list :ty-array N T) — N is an expr map[K]V → (list :ty-map K V) chan T → (list :ty-chan :both T) chan<- T → (list :ty-chan :send T) <-chan T → (list :ty-chan :recv T) gp-parse-type now dispatches on the head token: *, [, map, chan, <-, or ident; each branch recurses for nested types. Channel direction is encoded as :both / :send / :recv (Go-specific tag). Coverage: nested types end-to-end — []*T, [][]int, map[string][]int, chan map[K]V, *[]int — all via the v.(T) assertion carrier. Logged a concrete kit-gap proposal in plans/go-on-sx.md Blockers for canonical type-node shapes. The first six (:ty-name, :ty-sel, :ty-ptr, :ty-slice, :ty-array, :ty-map) are universal across statically-typed guests and worth promoting on the next consumer; channel/func shapes stay guest-specific until a second user. Phase 2 parse acceptance bar (80+ tests) crossed: parse 81/81, total 210/210. Func / struct / interface types and full decls + stmts still keep Phase 2 open. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,19 +97,56 @@
|
||||
args)))))
|
||||
(define
|
||||
gp-parse-type
|
||||
;; Minimal type-expression parser. This iteration handles:
|
||||
;; *T → (list :ty-ptr T)
|
||||
;; name → (list :ty-name "name")
|
||||
;; pkg.Name → (list :ty-sel "pkg" "Name")
|
||||
;; Full type grammar (slice, array, map, chan, func, struct,
|
||||
;; interface) is a separate Phase 2 sub-deliverable.
|
||||
;; Go type-expression parser. Covers:
|
||||
;; *T → (list :ty-ptr T)
|
||||
;; name → (list :ty-name "name")
|
||||
;; pkg.Name → (list :ty-sel "pkg" "Name")
|
||||
;; []T → (list :ty-slice T)
|
||||
;; [N]T → (list :ty-array N T)
|
||||
;; map[K]V → (list :ty-map K V)
|
||||
;; chan T → (list :ty-chan :both T)
|
||||
;; chan<- T → (list :ty-chan :send T)
|
||||
;; <-chan T → (list :ty-chan :recv T)
|
||||
;; Struct, interface, func types are deferred to a later slice.
|
||||
(fn
|
||||
()
|
||||
(cond
|
||||
(and (= (gp-tok-type) "op") (= (gp-tok-value) "*"))
|
||||
(do (gp-advance!) (list :ty-ptr (gp-parse-type)))
|
||||
(and (= (gp-tok-type) "op") (= (gp-tok-value) "["))
|
||||
(do
|
||||
(gp-advance!)
|
||||
(list :ty-ptr (gp-parse-type)))
|
||||
(cond
|
||||
(and (= (gp-tok-type) "op") (= (gp-tok-value) "]"))
|
||||
(do (gp-advance!) (list :ty-slice (gp-parse-type)))
|
||||
:else
|
||||
(let ((sz (gp-parse-expr 1)))
|
||||
(when (and (= (gp-tok-type) "op") (= (gp-tok-value) "]"))
|
||||
(gp-advance!))
|
||||
(list :ty-array sz (gp-parse-type)))))
|
||||
(and (= (gp-tok-type) "keyword") (= (gp-tok-value) "map"))
|
||||
(do
|
||||
(gp-advance!)
|
||||
(when (and (= (gp-tok-type) "op") (= (gp-tok-value) "["))
|
||||
(gp-advance!))
|
||||
(let ((k (gp-parse-type)))
|
||||
(when (and (= (gp-tok-type) "op") (= (gp-tok-value) "]"))
|
||||
(gp-advance!))
|
||||
(let ((v (gp-parse-type)))
|
||||
(list :ty-map k v))))
|
||||
(and (= (gp-tok-type) "op") (= (gp-tok-value) "<-"))
|
||||
(do
|
||||
(gp-advance!)
|
||||
(when (and (= (gp-tok-type) "keyword") (= (gp-tok-value) "chan"))
|
||||
(gp-advance!))
|
||||
(list :ty-chan :recv (gp-parse-type)))
|
||||
(and (= (gp-tok-type) "keyword") (= (gp-tok-value) "chan"))
|
||||
(do
|
||||
(gp-advance!)
|
||||
(cond
|
||||
(and (= (gp-tok-type) "op") (= (gp-tok-value) "<-"))
|
||||
(do (gp-advance!) (list :ty-chan :send (gp-parse-type)))
|
||||
:else (list :ty-chan :both (gp-parse-type))))
|
||||
(= (gp-tok-type) "ident")
|
||||
(let ((name (gp-tok-value)))
|
||||
(gp-advance!)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"language": "go",
|
||||
"total_pass": 199,
|
||||
"total": 199,
|
||||
"total_pass": 210,
|
||||
"total": 210,
|
||||
"suites": [
|
||||
{"name":"lex","pass":129,"total":129,"status":"ok"},
|
||||
{"name":"parse","pass":70,"total":70,"status":"ok"},
|
||||
{"name":"parse","pass":81,"total":81,"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: 199 / 199 tests passing**
|
||||
**Total: 210 / 210 tests passing**
|
||||
|
||||
| | Suite | Pass | Total |
|
||||
|---|---|---|---|
|
||||
| ✅ | lex | 129 | 129 |
|
||||
| ✅ | parse | 70 | 70 |
|
||||
| ✅ | parse | 81 | 81 |
|
||||
| ⬜ | types | 0 | 0 |
|
||||
| ⬜ | eval | 0 | 0 |
|
||||
| ⬜ | runtime | 0 | 0 |
|
||||
|
||||
@@ -363,6 +363,79 @@
|
||||
(list :assert (ast-var "v") (list :ty-name "int"))
|
||||
(ast-literal "1"))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: []int (slice)"
|
||||
(go-parse "v.([]int)")
|
||||
(list :assert (ast-var "v") (list :ty-slice (list :ty-name "int"))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: [10]int (array)"
|
||||
(go-parse "v.([10]int)")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list :ty-array (ast-literal "10") (list :ty-name "int"))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: map[string]int"
|
||||
(go-parse "v.(map[string]int)")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list :ty-map (list :ty-name "string") (list :ty-name "int"))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: chan int (bidirectional)"
|
||||
(go-parse "v.(chan int)")
|
||||
(list :assert (ast-var "v") (list :ty-chan :both (list :ty-name "int"))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: chan<- int (send-only)"
|
||||
(go-parse "v.(chan<- int)")
|
||||
(list :assert (ast-var "v") (list :ty-chan :send (list :ty-name "int"))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: <-chan int (recv-only)"
|
||||
(go-parse "v.(<-chan int)")
|
||||
(list :assert (ast-var "v") (list :ty-chan :recv (list :ty-name "int"))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: []*T (slice of pointers)"
|
||||
(go-parse "v.([]*T)")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list :ty-slice (list :ty-ptr (list :ty-name "T")))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: [][]int (slice of slice)"
|
||||
(go-parse "v.([][]int)")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list :ty-slice (list :ty-slice (list :ty-name "int")))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: map[string][]int (map with slice value)"
|
||||
(go-parse "v.(map[string][]int)")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-map (list :ty-name "string")
|
||||
(list :ty-slice (list :ty-name "int")))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: chan map[K]V (chan of map type)"
|
||||
(go-parse "v.(chan map[K]V)")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list
|
||||
:ty-chan :both
|
||||
(list :ty-map (list :ty-name "K") (list :ty-name "V")))))
|
||||
|
||||
(go-parse-test
|
||||
"ty: *[]int (pointer to slice)"
|
||||
(go-parse "v.(*[]int)")
|
||||
(list
|
||||
:assert (ast-var "v")
|
||||
(list :ty-ptr (list :ty-slice (list :ty-name "int")))))
|
||||
|
||||
(go-parse-test "non-primary: '+'" (go-parse "+") nil)
|
||||
|
||||
(go-parse-test "non-primary: empty" (go-parse "") nil)
|
||||
|
||||
@@ -174,9 +174,10 @@ Progress-log line → push `origin/loops/go`.
|
||||
- [x] Type assertion `v.(T)`. `(list :assert OBJ TYPE)`. Includes a
|
||||
minimal `gp-parse-type` (named / qualified `pkg.T` / pointer
|
||||
`*T` / `**T`); full type grammar still pending below.
|
||||
- [ ] Type expressions: basic, slice `[]T`, array `[N]T`, map `map[K]V`,
|
||||
chan `chan T` / `chan<- T` / `<-chan T`, func, struct, interface,
|
||||
pointer `*T`.
|
||||
- [/] 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`** all done — kit has no type primitives.
|
||||
func / struct / interface / generics deferred.
|
||||
- [ ] Composite literals: `T{...}`, `[]T{...}`, `map[K]V{...}`,
|
||||
`struct{...}{...}`.
|
||||
- [ ] Declarations: `package`, `import`, `var`, `const`, `type`, `func`
|
||||
@@ -186,7 +187,9 @@ Progress-log line → push `origin/loops/go`.
|
||||
assign, short-decl `:=`, send `ch <- v`, recv `<-ch`.
|
||||
- [ ] End-to-end: hello-world, fibonacci, FizzBuzz, goroutine ping-pong,
|
||||
struct + method.
|
||||
- **Acceptance:** parse/ suite at 80+ tests. Current: 70/70.
|
||||
- **Acceptance:** parse/ suite at 80+ tests. **Acceptance bar crossed:
|
||||
81/81.** Remaining sub-items (func/struct/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-
|
||||
@@ -446,6 +449,25 @@ 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
|
||||
to express types in source. Proposed canonical shapes:
|
||||
|
||||
```
|
||||
(list :ty-name "T") — named type
|
||||
(list :ty-sel "pkg" "T") — qualified type
|
||||
(list :ty-ptr T) — pointer to T
|
||||
(list :ty-slice T) — slice / dynamic array of T
|
||||
(list :ty-array N T) — fixed array, N is an expr
|
||||
(list :ty-map K V) — map type (also Python dict, Rust HashMap)
|
||||
```
|
||||
|
||||
The first six are universal: Rust, Swift, TS, Kotlin, Scala, Hack
|
||||
all need them. Go-specific extensions like `:ty-chan` (channel with
|
||||
direction) and `:ty-func` (parameter+return) should stay
|
||||
guest-specific until a second consumer wants them.
|
||||
|
||||
Minimal repro: see `lib/go/parse.sx#gp-parse-type`.
|
||||
|
||||
### Kit-gap proposals against `lib/guest/lex.sx`
|
||||
|
||||
Observed from building the Go tokenizer. Not blocking Phase 2; surfaced
|
||||
@@ -469,6 +491,19 @@ 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.: type expressions — slice `[]T`, array
|
||||
`[N]T`, map `map[K]V`, chan in all three directions (`chan T`,
|
||||
`chan<- T`, `<-chan T`). `gp-parse-type` now dispatches on
|
||||
`*`/`[`/`map`/`chan`/`<-`/ident; each branch recurses for nested
|
||||
types. Channel direction is `:both`/`:send`/`:recv`. AST stays
|
||||
Go-specific tagged lists — kit has no type primitives at all.
|
||||
Covers nested types end-to-end (slice-of-pointer, slice-of-slice,
|
||||
map-with-slice-value, chan-of-map, pointer-to-slice). **Parse
|
||||
acceptance bar (80+) crossed: +11 tests, parse 81/81, total 210/210.**
|
||||
Func / struct / interface types and generics still pending in Phase 2.
|
||||
`[proposes-ast]` — surfaces concrete type-node proposals (slice / array
|
||||
/ map are universal across statically-typed guests; channel direction
|
||||
is Go-specific). Logged in Blockers.
|
||||
- 2026-05-27 — Phase 2 cont.: type assertion `v.(T)` postfix form.
|
||||
Postfix `.` branch now disambiguates between `.field` (selector) and
|
||||
`.(...)` (type assertion) by peeking at the next token. New
|
||||
|
||||
Reference in New Issue
Block a user