From 976c6dd0ef360a58c4b5e2a431cdeef6cd37aa8a Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 27 May 2026 07:33:31 +0000 Subject: [PATCH] =?UTF-8?q?go:=20parse.sx=20scaffold=20=E2=80=94=20primary?= =?UTF-8?q?=20expressions=20+=20Go=20precedence=20table=20+=2017=20tests?= =?UTF-8?q?=20[consumes-pratt=20consumes-ast]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starts Phase 2. lib/go/parse.sx defines: * go-precedence-table — Go's five operator-precedence levels in the (NAME PREC ASSOC) entry shape from lib/guest/pratt.sx, ready for the binary-operator iteration to consume via pratt-op-lookup. * go-parse(src) — tokenises and parses ONE primary expression: int, float, imag, string, rune literals become (ast-literal VALUE); identifiers become (ast-var NAME). Built directly on lib/guest/ast.sx constructors — no intermediate AST shape. Conformance.sh extended to load lib/guest/{ast,pratt}.sx and run the new parse suite. Scoreboard cleanup: drop the "pending" parse row since the suite is now real. parse 17/17 (lex still 129/129). Total 146/146. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/go/conformance.sh | 9 ++++-- lib/go/parse.sx | 66 ++++++++++++++++++++++++++++++++++++++++++ lib/go/scoreboard.json | 6 ++-- lib/go/scoreboard.md | 4 +-- lib/go/tests/parse.sx | 43 +++++++++++++++++++++++++++ plans/go-on-sx.md | 45 ++++++++++++++++++---------- 6 files changed, 150 insertions(+), 23 deletions(-) create mode 100644 lib/go/parse.sx create mode 100644 lib/go/tests/parse.sx diff --git a/lib/go/conformance.sh b/lib/go/conformance.sh index 4fc3f564..b978edf8 100755 --- a/lib/go/conformance.sh +++ b/lib/go/conformance.sh @@ -28,13 +28,18 @@ trap "rm -f $TMPFILE $OUTFILE" EXIT # Each suite: name | pass-counter | total-counter SUITES=( "lex|go-test-pass|go-test-count" + "parse|go-parse-test-pass|go-parse-test-count" ) cat > "$TMPFILE" <<'EPOCHS' (epoch 1) (load "lib/guest/lex.sx") +(load "lib/guest/ast.sx") +(load "lib/guest/pratt.sx") (load "lib/go/lex.sx") +(load "lib/go/parse.sx") (load "lib/go/tests/lex.sx") +(load "lib/go/tests/parse.sx") EPOCHS idx=0 @@ -99,7 +104,6 @@ cat > lib/go/scoreboard.json < lib/go/scoreboard.md <>" 5 :left) + (list "&" 5 :left) + (list "&^" 5 :left) + (list "+" 4 :left) + (list "-" 4 :left) + (list "|" 4 :left) + (list "^" 4 :left) + (list "==" 3 :left) + (list "!=" 3 :left) + (list "<" 3 :left) + (list "<=" 3 :left) + (list ">" 3 :left) + (list ">=" 3 :left) + (list "&&" 2 :left) + (list "||" 1 :left))) + +(define + go-parse + (fn + (src) + (let + ((gp-tokens (go-tokenize src)) (gp-idx 0)) + (define gp-cur (fn () (nth gp-tokens gp-idx))) + (define gp-advance! (fn () (set! gp-idx (+ gp-idx 1)))) + (define gp-tok-type (fn () (get (gp-cur) :type))) + (define gp-tok-value (fn () (get (gp-cur) :value))) + (define + gp-parse-primary + (fn + () + (let + ((ty (gp-tok-type)) (v (gp-tok-value))) + (cond + (or + (= ty "int") + (= ty "float") + (= ty "imag") + (= ty "string") + (= ty "rune")) + (do (gp-advance!) (ast-literal v)) + (= ty "ident") + (do (gp-advance!) (ast-var v)) + :else nil)))) + (gp-parse-primary)))) diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index b7863644..32152771 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,10 +1,10 @@ { "language": "go", - "total_pass": 129, - "total": 129, + "total_pass": 146, + "total": 146, "suites": [ {"name":"lex","pass":129,"total":129,"status":"ok"}, - {"name":"parse","pass":0,"total":0,"status":"pending"}, + {"name":"parse","pass":17,"total":17,"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 b0346fdb..535fedfd 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,11 +1,11 @@ # Go-on-SX Scoreboard -**Total: 129 / 129 tests passing** +**Total: 146 / 146 tests passing** | | Suite | Pass | Total | |---|---|---|---| | ✅ | lex | 129 | 129 | -| ⬜ | parse | 0 | 0 | +| ✅ | parse | 17 | 17 | | ⬜ | types | 0 | 0 | | ⬜ | eval | 0 | 0 | | ⬜ | runtime | 0 | 0 | diff --git a/lib/go/tests/parse.sx b/lib/go/tests/parse.sx new file mode 100644 index 00000000..8025dded --- /dev/null +++ b/lib/go/tests/parse.sx @@ -0,0 +1,43 @@ +;; Go parser tests. + +(define go-parse-test-count 0) +(define go-parse-test-pass 0) +(define go-parse-test-fails (list)) + +(define + go-parse-test + (fn + (name actual expected) + (set! go-parse-test-count (+ go-parse-test-count 1)) + (if + (= actual expected) + (set! go-parse-test-pass (+ go-parse-test-pass 1)) + (append! go-parse-test-fails {:name name :expected expected :actual actual})))) + +;; ── primary: literals ───────────────────────────────────────────── +(go-parse-test "int literal" (go-parse "42") (ast-literal "42")) +(go-parse-test "zero literal" (go-parse "0") (ast-literal "0")) +(go-parse-test "hex literal" (go-parse "0xFF") (ast-literal "0xFF")) +(go-parse-test "float literal" (go-parse "3.14") (ast-literal "3.14")) +(go-parse-test "leading-dot float" (go-parse ".5") (ast-literal ".5")) +(go-parse-test "exponent float" (go-parse "1e10") (ast-literal "1e10")) +(go-parse-test "imag literal" (go-parse "2i") (ast-literal "2i")) +(go-parse-test "string literal" (go-parse "\"hi\"") (ast-literal "hi")) +(go-parse-test "empty string" (go-parse "\"\"") (ast-literal "")) +(go-parse-test "raw string" (go-parse "`a\nb`") (ast-literal "a\nb")) +(go-parse-test "rune literal" (go-parse "'a'") (ast-literal "a")) + +;; ── primary: identifiers ────────────────────────────────────────── +(go-parse-test "ident: simple" (go-parse "x") (ast-var "x")) +(go-parse-test "ident: underscore" (go-parse "_foo") (ast-var "_foo")) +(go-parse-test "ident: mixed case" (go-parse "fooBar") (ast-var "fooBar")) +(go-parse-test "ident: with digit" (go-parse "x123") (ast-var "x123")) + +;; ── primary: non-primary returns nil ────────────────────────────── +(go-parse-test "non-primary: '+'" (go-parse "+") nil) +(go-parse-test "non-primary: empty" (go-parse "") nil) + +;; ── report ──────────────────────────────────────────────────────── +(define + go-parse-test-summary + (str "parse " go-parse-test-pass "/" go-parse-test-count)) diff --git a/plans/go-on-sx.md b/plans/go-on-sx.md index 70457860..1e573dd4 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -154,21 +154,28 @@ Progress-log line → push `origin/loops/go`. done** — hex floats deferred (rare). Move to Phase 2 next. ### Phase 2 — Parser (`lib/go/parse.sx`) ⬜ -- Consume `lib/guest/core/pratt.sx` + `lib/guest/core/ast.sx`. Chisel notes - `consumes-pratt consumes-ast`. -- Grammar coverage: - - Declarations: `package`, `import`, `var`, `const`, `type`, `func` - - Types: basic, slice `[]T`, array `[N]T`, map `map[K]V`, chan `chan T`, - func `func(...)...`, struct, interface, pointer `*T` - - Expressions: literals, identifier, call, index `[]`, slice `[a:b]`, - type assertion `v.(T)`, operators - - Statements: `if`/`else`, `for` (C-style + range), `switch`, `select`, - `return`, `defer`, `go`, `break`/`continue`, assign, short-decl `:=`, - send `ch <- v`, recv `<-ch` -- Output: SX-shaped AST per `lib/guest/core/ast.sx` conventions. -- Tests: round-trip parse of hello world, fibonacci, FizzBuzz, goroutine - ping-pong, struct + method. -- **Acceptance:** parse/ suite at 80+ tests. +- [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`). +- [ ] Binary operators (Pratt precedence climbing using + `pratt-op-lookup` + Go precedence table). +- [ ] Unary operators (`!x`, `-x`, `^x`, `*p`, `&v`, `<-ch`). +- [ ] Function calls `f(a, b)` and member access `x.field`. +- [ ] Index `x[i]` and slice `x[a:b]`/`x[a:b:c]`. +- [ ] Type assertion `v.(T)`. +- [ ] Type expressions: basic, slice `[]T`, array `[N]T`, map `map[K]V`, + chan `chan T` / `chan<- T` / `<-chan T`, func, struct, interface, + pointer `*T`. +- [ ] Composite literals: `T{...}`, `[]T{...}`, `map[K]V{...}`, + `struct{...}{...}`. +- [ ] Declarations: `package`, `import`, `var`, `const`, `type`, `func` + (including methods, parameter lists, return types). +- [ ] Statements: `if`/`else`, `for` (C-style + range), `switch` (expr + + type), `select`, `return`, `defer`, `go`, `break`/`continue`, + 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: 17/17. ### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜ - **Independent implementation.** Do NOT use lib/guest/static-types- @@ -427,6 +434,14 @@ 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 first slice: `lib/go/parse.sx` parser scaffold. + Defines `go-precedence-table` using `lib/guest/pratt.sx` entry shape + `(NAME PREC ASSOC)` — five Go precedence levels, all left-associative + per Go spec § Operator precedence. `go-parse` tokenises via + `go-tokenize`, then `gp-parse-primary` reads one literal / identifier + and emits a canonical AST node via `lib/guest/ast.sx`'s `ast-literal` + / `ast-var`. parse 17/17, lex still 129/129, total 146/146. + `[consumes-pratt consumes-ast]`. - 2026-05-27 — **Phase 1 complete.** Operator-set audit: added missing `~` (Go 1.18+ generics type-set), exhaustive op coverage tests grouped by category. Two kit gaps observed and logged in Blockers: