From 48379e04bc89718ef494988a3d6f6ef0fdc69de5 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 27 May 2026 08:16:24 +0000 Subject: [PATCH] =?UTF-8?q?go:=20parse.sx=20=E2=80=94=20interface=20type?= =?UTF-8?q?=20expressions=20+=208=20tests;=20type=20expressions=20DONE=20[?= =?UTF-8?q?nothing]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Go interface type expressions: interface {} → empty interface { Close() } → no-param method interface { String() string } → with single return interface { Read([]byte) (int, error) } → multi-return method interface { Stringer } → embedded named iface interface { io.Reader } → qualified embedded interface { io.Reader; Close() error } → mixed gp-parse-interface-elems walks elements tolerating ASI semis. Each element is either: (list :method NAME PARAMS RESULTS) (list :embed TYPE) Method params/results reuse gp-parse-func-type-params/results — the shape is identical to a free-standing func type. Go 1.18+ type sets (interface { ~int | ~float64 }) are deferred until the generics sub-deliverable. With this, the full Phase 2 **type expressions** sub-deliverable is complete (pending only field tags, struct/iface embeds details, variadic, named func params, generics — all flagged later). parse 106/106, total 235/235. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/go/parse.sx | 56 ++++++++++++++++++++++++++++++++ lib/go/scoreboard.json | 6 ++-- lib/go/scoreboard.md | 4 +-- lib/go/tests/parse.sx | 73 ++++++++++++++++++++++++++++++++++++++++++ plans/go-on-sx.md | 25 +++++++++++---- 5 files changed, 153 insertions(+), 11 deletions(-) diff --git a/lib/go/parse.sx b/lib/go/parse.sx index 7d49f72f..ec9741e7 100644 --- a/lib/go/parse.sx +++ b/lib/go/parse.sx @@ -95,6 +95,58 @@ :else nil))) (gp-args-rest) args))))) + (define + gp-parse-interface-elems + ;; Caller positioned BEFORE '{'. Parses elements until '}'. + ;; Two element shapes: + ;; M(params) [results] → (list :method "M" PARAMS RESULTS) + ;; T or pkg.T → (list :embed TYPE) + ;; Type sets (Go 1.18+: ~int | ~float64) deferred. + (fn + () + (when (and (= (gp-tok-type) "op") (= (gp-tok-value) "{")) + (gp-advance!)) + (let ((elems (list))) + (define + gp-iface-loop + (fn + () + (cond + (= (gp-tok-type) "semi") + (do (gp-advance!) (gp-iface-loop)) + (and (= (gp-tok-type) "op") (= (gp-tok-value) "}")) + (gp-advance!) + (= (gp-tok-type) "ident") + (do + (let ((name (gp-tok-value))) + (gp-advance!) + (cond + (and (= (gp-tok-type) "op") + (= (gp-tok-value) "(")) + (let ((params (gp-parse-func-type-params))) + (let ((results (gp-parse-func-type-results))) + (append! elems + (list :method name params results)))) + (and (= (gp-tok-type) "op") + (= (gp-tok-value) ".")) + (do + (gp-advance!) + (cond + (= (gp-tok-type) "ident") + (let ((sel-name (gp-tok-value))) + (gp-advance!) + (append! elems + (list :embed + (list :ty-sel name sel-name)))) + :else + (append! elems + (list :embed (list :ty-name name))))) + :else + (append! elems (list :embed (list :ty-name name))))) + (gp-iface-loop)) + :else nil))) + (gp-iface-loop) + elems))) (define gp-parse-struct-fields ;; Caller positioned BEFORE '{'. Parses fields until '}'. @@ -251,6 +303,10 @@ (do (gp-advance!) (list :ty-struct (gp-parse-struct-fields))) + (and (= (gp-tok-type) "keyword") (= (gp-tok-value) "interface")) + (do + (gp-advance!) + (list :ty-interface (gp-parse-interface-elems))) (= (gp-tok-type) "ident") (let ((name (gp-tok-value))) (gp-advance!) diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index 1e97f9af..b0dce7eb 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,10 +1,10 @@ { "language": "go", - "total_pass": 227, - "total": 227, + "total_pass": 235, + "total": 235, "suites": [ {"name":"lex","pass":129,"total":129,"status":"ok"}, - {"name":"parse","pass":98,"total":98,"status":"ok"}, + {"name":"parse","pass":106,"total":106,"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"}, diff --git a/lib/go/scoreboard.md b/lib/go/scoreboard.md index c435cc94..f11686b6 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,11 +1,11 @@ # Go-on-SX Scoreboard -**Total: 227 / 227 tests passing** +**Total: 235 / 235 tests passing** | | Suite | Pass | Total | |---|---|---|---| | ✅ | lex | 129 | 129 | -| ✅ | parse | 98 | 98 | +| ✅ | parse | 106 | 106 | | ⬜ | types | 0 | 0 | | ⬜ | eval | 0 | 0 | | ⬜ | runtime | 0 | 0 | diff --git a/lib/go/tests/parse.sx b/lib/go/tests/parse.sx index d695540c..602cf5b5 100644 --- a/lib/go/tests/parse.sx +++ b/lib/go/tests/parse.sx @@ -577,6 +577,79 @@ (list :ty-struct (list (list :field (list "x") (list :ty-name "int"))))))))) +(go-parse-test + "ty: interface {} (empty)" + (go-parse "v.(interface {})") + (list :assert (ast-var "v") (list :ty-interface (list)))) + +(go-parse-test + "ty: interface { Close() } (single method, no params, no return)" + (go-parse "v.(interface { Close() })") + (list + :assert (ast-var "v") + (list :ty-interface (list (list :method "Close" (list) (list)))))) + +(go-parse-test + "ty: interface { String() string } (single return)" + (go-parse "v.(interface { String() string })") + (list + :assert (ast-var "v") + (list + :ty-interface (list (list :method "String" (list) (list (list :ty-name "string"))))))) + +(go-parse-test + "ty: interface { Read([]byte) (int, error) } (multi return)" + (go-parse "v.(interface { Read([]byte) (int, error) })") + (list + :assert (ast-var "v") + (list + :ty-interface (list + (list + :method "Read" + (list (list :ty-slice (list :ty-name "byte"))) + (list (list :ty-name "int") (list :ty-name "error"))))))) + +(go-parse-test + "ty: interface { Stringer } (embedded interface)" + (go-parse "v.(interface { Stringer })") + (list + :assert (ast-var "v") + (list :ty-interface (list (list :embed (list :ty-name "Stringer")))))) + +(go-parse-test + "ty: interface { io.Reader } (qualified embedded)" + (go-parse "v.(interface { io.Reader })") + (list + :assert (ast-var "v") + (list :ty-interface (list (list :embed (list :ty-sel "io" "Reader")))))) + +(go-parse-test + "ty: interface with embed + methods (io.ReadWriter style)" + (go-parse "v.(interface { io.Reader; Close() error })") + (list + :assert (ast-var "v") + (list + :ty-interface (list + (list :embed (list :ty-sel "io" "Reader")) + (list :method "Close" (list) (list (list :ty-name "error"))))))) + +(go-parse-test + "ty: interface with multiple methods" + (go-parse "v.(interface { Read([]byte) int; Write([]byte) int; Close() })") + (list + :assert (ast-var "v") + (list + :ty-interface (list + (list + :method "Read" + (list (list :ty-slice (list :ty-name "byte"))) + (list (list :ty-name "int"))) + (list + :method "Write" + (list (list :ty-slice (list :ty-name "byte"))) + (list (list :ty-name "int"))) + (list :method "Close" (list) (list)))))) + (go-parse-test "non-primary: '+'" (go-parse "+") nil) (go-parse-test "non-primary: empty" (go-parse "") nil) diff --git a/plans/go-on-sx.md b/plans/go-on-sx.md index ec65c7e8..133e8b90 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -174,12 +174,13 @@ 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: **slice `[]T`, array `[N]T`, map `map[K]V`, chan +- [x] 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(...) ...`, 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. + shared-type field rows (`x, y int`), interface `interface{...}` + with methods + embedded interfaces (named and qualified)** all + done — kit has no type primitives. Field tags, struct embeds, + variadic, named func-params, Go 1.18 type sets, generics deferred. - [ ] Composite literals: `T{...}`, `[]T{...}`, `map[K]V{...}`, `struct{...}{...}`. - [ ] Declarations: `package`, `import`, `var`, `const`, `type`, `func` @@ -190,8 +191,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: - 98/98.** Remaining sub-items (interface types, composite literals, - decls, stmts, e2e) still keep Phase 2 open ⬜. + 106/106.** Remaining sub-items (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- @@ -508,6 +509,18 @@ 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.: interface-type expressions. `interface {}`, + `interface { Close() }`, `interface { String() string }`, + `interface { Read([]byte) (int, error) }`, plus embedded interfaces + (`Stringer`, `io.Reader`). AST shape: + `(list :ty-interface ELEMS)` where each element is either + `(list :method NAME PARAMS RESULTS)` or `(list :embed TYPE)`. Method + params reuse `gp-parse-func-type-params` — same anonymous-only shape + as func types. Go 1.18+ type sets (`~int | ~float64`) deferred to + generics work. With this, all Phase-2 **type expressions** are + complete. +8 tests, parse 106/106, total 235/235. `[nothing]` — pure + Go parser; the field-binding-group kit-gap proposal from the previous + commit covers the cross-language angle. - 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