From 2404a593bdec826e3198ceefd9e85100836bffe0 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 27 May 2026 20:34:16 +0000 Subject: [PATCH] =?UTF-8?q?go:=20parse.sx=20=E2=80=94=20multi-form=20file?= =?UTF-8?q?=20parsing=20+=207=20e2e=20tests;=20PHASE=202=20COMPLETE=20[not?= =?UTF-8?q?hing]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final Phase 2 sub-deliverable. go-parse now handles whole Go files: - Empty source → nil - Single top-level form → that form (backward-compatible with ~169 existing single-stmt / single-decl tests) - Multiple forms → (list :file FORMS), the canonical Go file shape Implementation: gp-parse-all loops gp-parse-top until eof, tolerating ASI semis between forms, then returns based on form count. End-to-end test set (asserts the top-level decl-tag sequence via a new decl-tags helper, not the full AST tree — that'd be unwieldy): - hello-world :package :import :func-decl - recursive fibonacci :package :func-decl - FizzBuzz :package :import :func-decl - goroutine ping-pong :package :func-decl :func-decl - struct + method :package :type-decl :method-decl :func-decl - interface + method :package :type-decl :type-decl :method-decl - defer + select + range :package :func-decl Type-switch (`switch v := x.(type) { ... }`) is the one syntactic shape still deferred from Phase 2; doesn't gate Phase 3. **Phase 2 (parser) is complete.** parse 176/176, total 305/305. Next: Phase 3 — bidirectional type checker. The sister-plan diary for static-types-bidirectional already has the :field binding-group insight; Phase 3 will add the synth/check shape that emerges. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/go/parse.sx | 32 +++++++++++++++++++++- lib/go/scoreboard.json | 6 ++--- lib/go/scoreboard.md | 4 +-- lib/go/tests/parse.sx | 60 ++++++++++++++++++++++++++++++++++++++++++ plans/go-on-sx.md | 26 +++++++++++++----- 5 files changed, 116 insertions(+), 12 deletions(-) diff --git a/lib/go/parse.sx b/lib/go/parse.sx index 4e5a64ee..280ae216 100644 --- a/lib/go/parse.sx +++ b/lib/go/parse.sx @@ -1175,4 +1175,34 @@ (= (gp-tok-value) "func"))) (gp-parse-decl) :else (gp-parse-stmt)))) - (gp-parse-top)))) + (define + gp-parse-all + ;; Parse all top-level forms until eof. Returns: + ;; nil — empty input + ;; single form — backward-compatible with single-stmt + ;; /single-decl tests; ~169 of them. + ;; (list :file FORMS) — multiple forms (canonical Go file shape) + (fn + () + (let ((forms (list))) + (define + gp-all-loop + (fn + () + (cond + (= (gp-tok-type) "eof") nil + (= (gp-tok-type) "semi") + (do (gp-advance!) (gp-all-loop)) + :else + (do + (let ((saved-idx gp-idx)) + (let ((d (gp-parse-top))) + (when (not (= d nil)) (append! forms d))) + (when (= gp-idx saved-idx) (gp-advance!))) + (gp-all-loop))))) + (gp-all-loop) + (cond + (= (len forms) 0) nil + (= (len forms) 1) (first forms) + :else (list :file forms))))) + (gp-parse-all)))) diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index 190918da..584ac961 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,10 +1,10 @@ { "language": "go", - "total_pass": 298, - "total": 298, + "total_pass": 305, + "total": 305, "suites": [ {"name":"lex","pass":129,"total":129,"status":"ok"}, - {"name":"parse","pass":169,"total":169,"status":"ok"}, + {"name":"parse","pass":176,"total":176,"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 b7e046fc..fb656217 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,11 +1,11 @@ # Go-on-SX Scoreboard -**Total: 298 / 298 tests passing** +**Total: 305 / 305 tests passing** | | Suite | Pass | Total | |---|---|---|---| | ✅ | lex | 129 | 129 | -| ✅ | parse | 169 | 169 | +| ✅ | parse | 176 | 176 | | ⬜ | types | 0 | 0 | | ⬜ | eval | 0 | 0 | | ⬜ | runtime | 0 | 0 | diff --git a/lib/go/tests/parse.sx b/lib/go/tests/parse.sx index 91529d0a..593e0ba4 100644 --- a/lib/go/tests/parse.sx +++ b/lib/go/tests/parse.sx @@ -1127,6 +1127,66 @@ (list (ast-var "a"))) (list :default (list (ast-var "b")))))) +(define + decl-tags + (fn + (parsed) + (cond + (and (list? parsed) (= (first parsed) :file)) + (map (fn (d) (first d)) (nth parsed 1)) + (list? parsed) + (list (first parsed)) + :else (list)))) + +(go-parse-test + "e2e: hello-world top-level tags" + (decl-tags + (go-parse + "package main\nimport \"fmt\"\nfunc main() { fmt.Println(\"hello, world\") }")) + (list :package :import :func-decl)) + +(go-parse-test + "e2e: recursive fibonacci" + (decl-tags + (go-parse + "package main\nfunc fib(n int) int {\n if n < 2 { return n }\n return fib(n-1) + fib(n-2)\n}")) + (list :package :func-decl)) + +(go-parse-test + "e2e: FizzBuzz with for + if-else chain" + (decl-tags + (go-parse + "package main\nimport \"fmt\"\nfunc fizzbuzz(n int) {\n for i := 1; i <= n; i++ {\n if i % 15 == 0 { fmt.Println(\"FizzBuzz\") } else if i % 3 == 0 { fmt.Println(\"Fizz\") } else if i % 5 == 0 { fmt.Println(\"Buzz\") } else { fmt.Println(i) }\n }\n}")) + (list :package :import :func-decl)) + +(go-parse-test + "e2e: goroutine ping-pong (channels)" + (decl-tags + (go-parse + "package main\nfunc sender(ch chan int) { ch <- 1 }\nfunc main() {\n ch := make(chan int)\n go sender(ch)\n x := <-ch\n print(x)\n}")) + (list :package :func-decl :func-decl)) + +(go-parse-test + "e2e: struct + method" + (decl-tags + (go-parse + "package main\ntype Point struct { x, y int }\nfunc (p Point) Sum() int { return p.x + p.y }\nfunc main() {\n p := Point{1, 2}\n print(p.Sum())\n}")) + (list :package :type-decl :method-decl :func-decl)) + +(go-parse-test + "e2e: interface + structural satisfaction setup" + (decl-tags + (go-parse + "package main\ntype Stringer interface { String() string }\ntype T struct { v int }\nfunc (t T) String() string { return \"t\" }")) + (list :package :type-decl :type-decl :method-decl)) + +(go-parse-test + "e2e: defer + select + range" + (decl-tags + (go-parse + "package main\nfunc worker(jobs chan int, results chan int) {\n defer close(results)\n for j := range jobs {\n select {\n case results <- j * 2:\n default:\n return\n }\n }\n}")) + (list :package :func-decl)) + (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 a895426c..3a0618cb 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -153,7 +153,7 @@ Progress-log line → push `origin/loops/go`. - **Acceptance:** lex/ suite at 50+ tests. Current: 129/129. **Phase 1 done** — hex floats deferred (rare). Move to Phase 2 next. -### Phase 2 — Parser (`lib/go/parse.sx`) ⬜ +### Phase 2 — Parser (`lib/go/parse.sx`) ✅ - [x] Parser scaffold + Go operator-precedence table (entry shape from `lib/guest/pratt.sx`) + primary expressions (int/float/imag/string/ rune/ident → ast-literal / ast-var via `lib/guest/ast.sx`). @@ -203,11 +203,15 @@ Progress-log line → push `origin/loops/go`. bare-recv / default)** all done. Composite-literal `{` suppression active in control-flow conditions. Type-switch (`switch v := x.(type)`) deferred to a follow-up. -- [ ] End-to-end: hello-world, fibonacci, FizzBuzz, goroutine ping-pong, - struct + method. -- **Acceptance:** parse/ suite at 80+ tests. **Acceptance bar crossed: - 169/169.** Remaining sub-item (end-to-end programs) keeps Phase 2 - open ⬜. +- [x] End-to-end: hello-world, fibonacci, FizzBuzz, goroutine ping-pong, + struct + method, interface, defer+select+range. `go-parse` extended + to handle multi-form files: returns the single form for one-form + input (backward compat) or `(list :file FORMS)` for multiple. + Structural tests assert top-level decl-tag sequences via the + `decl-tags` helper rather than full ASTs. +- **Acceptance:** parse/ suite at 80+ tests. Current: **176/176**. + **Phase 2 complete.** Type-switch is the one syntactic shape still + deferred to a follow-up; it doesn't gate Phase 3. ### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜ - **Independent implementation.** Do NOT use lib/guest/static-types- @@ -524,6 +528,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 complete.** End-to-end multi-form file parsing. + `go-parse` now returns single forms for backward compat (~169 tests + unchanged) or `(list :file FORMS)` for multi-form input. Tests cover + hello-world, fibonacci, FizzBuzz, goroutine ping-pong, struct+method, + interface+method, and defer+select+range — each asserted via top- + level `decl-tags`. Type-switch is the one syntactic shape still + deferred. +7 tests, parse 176/176, total 305/305. Next: Phase 3 + (bidirectional type checker). `[nothing]` — pure Go parser + composition; the cross-language insights are already in the sister- + plan diaries from earlier Phase 2 commits. - 2026-05-27 — Phase 2 cont.: `switch` and `select` statements. Tagged + tagless switch, multi-value cases, `default`, and select with recv-into-var / send / bare-recv / default cases. New