go: parse.sx — type assertion v.(T) + minimal type parser + 9 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 18s

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 07:57:29 +00:00
parent e64d72f554
commit 503bdf12d6
5 changed files with 116 additions and 10 deletions

View File

@@ -95,6 +95,36 @@
:else nil))) :else nil)))
(gp-args-rest) (gp-args-rest)
args))))) 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 (define
gp-parse-bracket-expr gp-parse-bracket-expr
;; Optional expression inside brackets — returns nil if next token ;; Optional expression inside brackets — returns nil if next token
@@ -159,13 +189,24 @@
(and (= (get tok :type) "op") (= (get tok :value) ".")) (and (= (get tok :type) "op") (= (get tok :value) "."))
(do (do
(gp-advance!) (gp-advance!)
(let ((field-tok (gp-cur))) (let ((next-tok (gp-cur)))
(cond (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 (do
(gp-advance!) (gp-advance!)
(gp-postfix-loop (gp-postfix-loop
(list :select base (get field-tok :value)))) (list :select base (get next-tok :value))))
:else base))) :else base)))
(and (= (get tok :type) "op") (= (get tok :value) "(")) (and (= (get tok :type) "op") (= (get tok :value) "("))
(do (do

View File

@@ -1,10 +1,10 @@
{ {
"language": "go", "language": "go",
"total_pass": 190, "total_pass": 199,
"total": 190, "total": 199,
"suites": [ "suites": [
{"name":"lex","pass":129,"total":129,"status":"ok"}, {"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":"types","pass":0,"total":0,"status":"pending"},
{"name":"eval","pass":0,"total":0,"status":"pending"}, {"name":"eval","pass":0,"total":0,"status":"pending"},
{"name":"runtime","pass":0,"total":0,"status":"pending"}, {"name":"runtime","pass":0,"total":0,"status":"pending"},

View File

@@ -1,11 +1,11 @@
# Go-on-SX Scoreboard # Go-on-SX Scoreboard
**Total: 190 / 190 tests passing** **Total: 199 / 199 tests passing**
| | Suite | Pass | Total | | | Suite | Pass | Total |
|---|---|---|---| |---|---|---|---|
| ✅ | lex | 129 | 129 | | ✅ | lex | 129 | 129 |
| ✅ | parse | 61 | 61 | | ✅ | parse | 70 | 70 |
| ⬜ | types | 0 | 0 | | ⬜ | types | 0 | 0 |
| ⬜ | eval | 0 | 0 | | ⬜ | eval | 0 | 0 |
| ⬜ | runtime | 0 | 0 | | ⬜ | runtime | 0 | 0 |

View File

@@ -310,6 +310,59 @@
(go-parse "a[i:j]") (go-parse "a[i:j]")
(list :slice (ast-var "a") (ast-var "i") (ast-var "j") nil)) (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: '+'" (go-parse "+") nil)
(go-parse-test "non-primary: empty" (go-parse "") nil) (go-parse-test "non-primary: empty" (go-parse "") nil)

View File

@@ -171,7 +171,9 @@ Progress-log line → push `origin/loops/go`.
`(list :index OBJ IDX)` and `(list :slice OBJ LOW HIGH MAX)` `(list :index OBJ IDX)` and `(list :slice OBJ LOW HIGH MAX)`
(LOW/HIGH/MAX may be nil) — kit lacks both. Permissive parser (LOW/HIGH/MAX may be nil) — kit lacks both. Permissive parser
accepts `a[1::3]` (strict Go rejects, but type phase can enforce). 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`, - [ ] Type expressions: basic, slice `[]T`, array `[N]T`, map `map[K]V`,
chan `chan T` / `chan<- T` / `<-chan T`, func, struct, interface, chan `chan T` / `chan<- T` / `<-chan T`, func, struct, interface,
pointer `*T`. pointer `*T`.
@@ -184,7 +186,7 @@ Progress-log line → push `origin/loops/go`.
assign, short-decl `:=`, send `ch <- v`, recv `<-ch`. assign, short-decl `:=`, send `ch <- v`, recv `<-ch`.
- [ ] End-to-end: hello-world, fibonacci, FizzBuzz, goroutine ping-pong, - [ ] End-to-end: hello-world, fibonacci, FizzBuzz, goroutine ping-pong,
struct + method. 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`) ⬜ ### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜
- **Independent implementation.** Do NOT use lib/guest/static-types- - **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._ _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]` / - 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` `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 branch off the same postfix loop as calls/selectors. AST: Go-specific