From 503bdf12d6d1623f2237ff3fa3770f312d77bb05 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 27 May 2026 07:57:29 +0000 Subject: [PATCH] =?UTF-8?q?go:=20parse.sx=20=E2=80=94=20type=20assertion?= =?UTF-8?q?=20v.(T)=20+=20minimal=20type=20parser=20+=209=20tests=20[nothi?= =?UTF-8?q?ng]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Postfix '.' branch now peeks at the next token to disambiguate: .ident → selector / member access (list :select OBJ "field") .(TYPE) → type assertion (list :assert OBJ TYPE) New gp-parse-type covers the minimum types needed for assertions: name → (list :ty-name "int") pkg.Name → (list :ty-sel "pkg" "Name") *T / **T → (list :ty-ptr (list :ty-ptr ...)) Full type grammar — slice []T, array [N]T, map[K]V, chan, func, struct, interface — is a separate Phase 2 sub-deliverable. Type AST shapes are Go-specific tagged lists; the canonical AST kit has no type-system primitives at all yet. Worth a richer kit discussion once Phase 3 (bidirectional type checker) lands and the sister plan static-types-bidirectional has a real surface to react to. parse 70/70, total 199/199. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/go/parse.sx | 47 ++++++++++++++++++++++++++++++++++--- lib/go/scoreboard.json | 6 ++--- lib/go/scoreboard.md | 4 ++-- lib/go/tests/parse.sx | 53 ++++++++++++++++++++++++++++++++++++++++++ plans/go-on-sx.md | 16 +++++++++++-- 5 files changed, 116 insertions(+), 10 deletions(-) diff --git a/lib/go/parse.sx b/lib/go/parse.sx index df398b2e..ab5b1dad 100644 --- a/lib/go/parse.sx +++ b/lib/go/parse.sx @@ -95,6 +95,36 @@ :else nil))) (gp-args-rest) 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. + (fn + () + (cond + (and (= (gp-tok-type) "op") (= (gp-tok-value) "*")) + (do + (gp-advance!) + (list :ty-ptr (gp-parse-type))) + (= (gp-tok-type) "ident") + (let ((name (gp-tok-value))) + (gp-advance!) + (cond + (and (= (gp-tok-type) "op") (= (gp-tok-value) ".")) + (do + (gp-advance!) + (cond + (= (gp-tok-type) "ident") + (let ((sel-name (gp-tok-value))) + (gp-advance!) + (list :ty-sel name sel-name)) + :else (list :ty-name name))) + :else (list :ty-name name))) + :else nil))) (define gp-parse-bracket-expr ;; Optional expression inside brackets — returns nil if next token @@ -159,13 +189,24 @@ (and (= (get tok :type) "op") (= (get tok :value) ".")) (do (gp-advance!) - (let ((field-tok (gp-cur))) + (let ((next-tok (gp-cur))) (cond - (= (get field-tok :type) "ident") + ;; .(T) — type assertion + (and (= (get next-tok :type) "op") + (= (get next-tok :value) "(")) + (do + (gp-advance!) + (let ((ty (gp-parse-type))) + (when (and (= (gp-tok-type) "op") + (= (gp-tok-value) ")")) + (gp-advance!)) + (gp-postfix-loop (list :assert base ty)))) + ;; .ident — selector / member access + (= (get next-tok :type) "ident") (do (gp-advance!) (gp-postfix-loop - (list :select base (get field-tok :value)))) + (list :select base (get next-tok :value)))) :else base))) (and (= (get tok :type) "op") (= (get tok :value) "(")) (do diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index be68fedc..6134f9f6 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,10 +1,10 @@ { "language": "go", - "total_pass": 190, - "total": 190, + "total_pass": 199, + "total": 199, "suites": [ {"name":"lex","pass":129,"total":129,"status":"ok"}, - {"name":"parse","pass":61,"total":61,"status":"ok"}, + {"name":"parse","pass":70,"total":70,"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 e08026c3..6c2a4b2c 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,11 +1,11 @@ # Go-on-SX Scoreboard -**Total: 190 / 190 tests passing** +**Total: 199 / 199 tests passing** | | Suite | Pass | Total | |---|---|---|---| | ✅ | lex | 129 | 129 | -| ✅ | parse | 61 | 61 | +| ✅ | parse | 70 | 70 | | ⬜ | types | 0 | 0 | | ⬜ | eval | 0 | 0 | | ⬜ | runtime | 0 | 0 | diff --git a/lib/go/tests/parse.sx b/lib/go/tests/parse.sx index 18c9e560..a1e6e4b9 100644 --- a/lib/go/tests/parse.sx +++ b/lib/go/tests/parse.sx @@ -310,6 +310,59 @@ (go-parse "a[i:j]") (list :slice (ast-var "a") (ast-var "i") (ast-var "j") nil)) +(go-parse-test + "assert: v.(int)" + (go-parse "v.(int)") + (list :assert (ast-var "v") (list :ty-name "int"))) + +(go-parse-test + "assert: v.(string)" + (go-parse "v.(string)") + (list :assert (ast-var "v") (list :ty-name "string"))) + +(go-parse-test + "assert: v.(MyType) (user-defined)" + (go-parse "v.(MyType)") + (list :assert (ast-var "v") (list :ty-name "MyType"))) + +(go-parse-test + "assert: v.(*T) (pointer type)" + (go-parse "v.(*T)") + (list :assert (ast-var "v") (list :ty-ptr (list :ty-name "T")))) + +(go-parse-test + "assert: v.(**T) (pointer-to-pointer)" + (go-parse "v.(**T)") + (list + :assert (ast-var "v") + (list :ty-ptr (list :ty-ptr (list :ty-name "T"))))) + +(go-parse-test + "assert: v.(pkg.T) (qualified type)" + (go-parse "v.(pkg.T)") + (list :assert (ast-var "v") (list :ty-sel "pkg" "T"))) + +(go-parse-test + "assert: f().(int) (on call result)" + (go-parse "f().(int)") + (list :assert (ast-app (ast-var "f") (list)) (list :ty-name "int"))) + +(go-parse-test + "assert: obj.field.(int) (after selector)" + (go-parse "obj.field.(int)") + (list + :assert (list :select (ast-var "obj") "field") + (list :ty-name "int"))) + +(go-parse-test + "assert: v.(int) + 1 (assert binds tighter than binary +)" + (go-parse "v.(int) + 1") + (ast-app + (ast-var "+") + (list + (list :assert (ast-var "v") (list :ty-name "int")) + (ast-literal "1")))) + (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 96f72ffb..b950292d 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -171,7 +171,9 @@ Progress-log line → push `origin/loops/go`. `(list :index OBJ IDX)` and `(list :slice OBJ LOW HIGH MAX)` (LOW/HIGH/MAX may be nil) — kit lacks both. Permissive parser accepts `a[1::3]` (strict Go rejects, but type phase can enforce). -- [ ] Type assertion `v.(T)`. +- [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`. @@ -184,7 +186,7 @@ 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: 61/61. +- **Acceptance:** parse/ suite at 80+ tests. Current: 70/70. ### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜ - **Independent implementation.** Do NOT use lib/guest/static-types- @@ -467,6 +469,16 @@ 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 assertion `v.(T)` postfix form. + Postfix `.` branch now disambiguates between `.field` (selector) and + `.(...)` (type assertion) by peeking at the next token. New + `gp-parse-type` handles the minimum needed: named (`int`, `MyType`), + qualified (`pkg.T`), pointer (`*T`, `**T`). AST shapes are + Go-specific tagged lists — kit has no notion of types at all yet + (this is a meta-gap: full bidirectional types arrive in Phase 3, but + even the parser needs a type substrate). Covers chained, + call-result, after-selector, and binary-precedence interactions. +9 + tests, parse 70/70, total 199/199. `[nothing]`. - 2026-05-27 — Phase 2 cont.: index `x[i]` and slice `x[a:b]` / `x[a:b:c]` postfix forms. New `gp-parse-bracket` + `gp-parse-bracket-expr` branch off the same postfix loop as calls/selectors. AST: Go-specific