diff --git a/lib/go/parse.sx b/lib/go/parse.sx index 654d20ff..c94a6d43 100644 --- a/lib/go/parse.sx +++ b/lib/go/parse.sx @@ -95,6 +95,57 @@ :else nil))) (gp-args-rest) args))))) + (define + gp-parse-func-type-params + ;; Anonymous-only func-type params: caller is positioned BEFORE + ;; the opening "(". Returns a list of type AST nodes. + ;; Named params (a int, b string) are deferred — they're needed + ;; for func DECLARATIONS, not pure func-type expressions. + (fn + () + (let ((params (list))) + (when (and (= (gp-tok-type) "op") (= (gp-tok-value) "(")) + (gp-advance!)) + (cond + (and (= (gp-tok-type) "op") (= (gp-tok-value) ")")) + (do (gp-advance!) params) + :else + (do + (let ((first (gp-parse-type))) + (when (not (= first nil)) (append! params first))) + (define + gp-params-rest + (fn + () + (cond + (and (= (gp-tok-type) "op") (= (gp-tok-value) ",")) + (do + (gp-advance!) + (let ((t (gp-parse-type))) + (when (not (= t nil)) (append! params t))) + (gp-params-rest)) + (and (= (gp-tok-type) "op") (= (gp-tok-value) ")")) + (gp-advance!) + :else nil))) + (gp-params-rest) + params))))) + (define + gp-parse-func-type-results + ;; Zero, one, or many return types. Caller is positioned after + ;; the closing ')' of params. + ;; no return — next token is not a type-starter + ;; single return — bare type follows + ;; multi return — '(' T, T, ... ')' + (fn + () + (cond + (and (= (gp-tok-type) "op") (= (gp-tok-value) "(")) + (gp-parse-func-type-params) + :else + (let ((t (gp-parse-type))) + (cond + (= t nil) (list) + :else (list t)))))) (define gp-parse-type ;; Go type-expression parser. Covers: @@ -147,6 +198,12 @@ (and (= (gp-tok-type) "op") (= (gp-tok-value) "<-")) (do (gp-advance!) (list :ty-chan :send (gp-parse-type))) :else (list :ty-chan :both (gp-parse-type)))) + (and (= (gp-tok-type) "keyword") (= (gp-tok-value) "func")) + (do + (gp-advance!) + (let ((params (gp-parse-func-type-params))) + (let ((results (gp-parse-func-type-results))) + (list :ty-func params results)))) (= (gp-tok-type) "ident") (let ((name (gp-tok-value))) (gp-advance!) diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index 3f4c7ea0..dfb6a997 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,10 +1,10 @@ { "language": "go", - "total_pass": 210, - "total": 210, + "total_pass": 219, + "total": 219, "suites": [ {"name":"lex","pass":129,"total":129,"status":"ok"}, - {"name":"parse","pass":81,"total":81,"status":"ok"}, + {"name":"parse","pass":90,"total":90,"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 7580251d..f4e9712a 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,11 +1,11 @@ # Go-on-SX Scoreboard -**Total: 210 / 210 tests passing** +**Total: 219 / 219 tests passing** | | Suite | Pass | Total | |---|---|---|---| | ✅ | lex | 129 | 129 | -| ✅ | parse | 81 | 81 | +| ✅ | parse | 90 | 90 | | ⬜ | types | 0 | 0 | | ⬜ | eval | 0 | 0 | | ⬜ | runtime | 0 | 0 | diff --git a/lib/go/tests/parse.sx b/lib/go/tests/parse.sx index d142308e..3cf5db2c 100644 --- a/lib/go/tests/parse.sx +++ b/lib/go/tests/parse.sx @@ -436,6 +436,77 @@ :assert (ast-var "v") (list :ty-ptr (list :ty-slice (list :ty-name "int"))))) +(go-parse-test + "ty: func() (no params, no return)" + (go-parse "v.(func())") + (list :assert (ast-var "v") (list :ty-func (list) (list)))) + +(go-parse-test + "ty: func() int (no params, one return)" + (go-parse "v.(func() int)") + (list + :assert (ast-var "v") + (list :ty-func (list) (list (list :ty-name "int"))))) + +(go-parse-test + "ty: func(int)" + (go-parse "v.(func(int))") + (list + :assert (ast-var "v") + (list :ty-func (list (list :ty-name "int")) (list)))) + +(go-parse-test + "ty: func(int, string)" + (go-parse "v.(func(int, string))") + (list + :assert (ast-var "v") + (list + :ty-func (list (list :ty-name "int") (list :ty-name "string")) + (list)))) + +(go-parse-test + "ty: func(int) string" + (go-parse "v.(func(int) string)") + (list + :assert (ast-var "v") + (list + :ty-func (list (list :ty-name "int")) + (list (list :ty-name "string"))))) + +(go-parse-test + "ty: func() (int, error) (multi return)" + (go-parse "v.(func() (int, error))") + (list + :assert (ast-var "v") + (list + :ty-func (list) + (list (list :ty-name "int") (list :ty-name "error"))))) + +(go-parse-test + "ty: func(*T) []int (pointer param, slice return)" + (go-parse "v.(func(*T) []int)") + (list + :assert (ast-var "v") + (list + :ty-func (list (list :ty-ptr (list :ty-name "T"))) + (list (list :ty-slice (list :ty-name "int")))))) + +(go-parse-test + "ty: func() func() (nested func type as return)" + (go-parse "v.(func() func())") + (list + :assert (ast-var "v") + (list :ty-func (list) (list (list :ty-func (list) (list)))))) + +(go-parse-test + "ty: chan func() int (chan of func type)" + (go-parse "v.(chan func() int)") + (list + :assert (ast-var "v") + (list + :ty-chan :both + (list :ty-func (list) (list (list :ty-name "int")))))) + (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 dae41bdd..38384bae 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -176,8 +176,9 @@ Progress-log line → push `origin/loops/go`. `*T` / `**T`); full type grammar still pending below. - [/] 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`** all done — kit has no type primitives. - func / struct / interface / generics deferred. + qualified `pkg.T`, func `func(...) ...` (anonymous params, single + or multi return)** all done — kit has no type primitives. + struct / interface / variadic / named-params / generics deferred. - [ ] Composite literals: `T{...}`, `[]T{...}`, `map[K]V{...}`, `struct{...}{...}`. - [ ] Declarations: `package`, `import`, `var`, `const`, `type`, `func` @@ -188,7 +189,7 @@ 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: - 81/81.** Remaining sub-items (func/struct/interface types, composite + 90/90.** Remaining sub-items (struct/interface types, composite literals, decls, stmts, e2e) still keep Phase 2 open ⬜. ### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜ @@ -491,6 +492,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.: func-type expressions. `func()`, + `func() int`, `func(int, string)`, `func(int) string`, + `func() (int, error)`. AST shape `(list :ty-func PARAMS RESULTS)` + where both are lists of type nodes. Results parsing reuses param + parser for the multi-return `(T, T, ...)` case. Anonymous-only + params for now — named params (`func(a int, b string)`) need a + different shape and are required mainly for func DECLARATIONS not + pure func-type expressions. Variadic deferred. Covers nested + func-as-return and chan-of-func. +9 tests, parse 90/90, total 219/219. + `[nothing]` — pure Go parser; type AST proposals already in Blockers. - 2026-05-27 — Phase 2 cont.: type expressions — slice `[]T`, array `[N]T`, map `map[K]V`, chan in all three directions (`chan T`, `chan<- T`, `<-chan T`). `gp-parse-type` now dispatches on