go: Phase 7 foundation — generics syntax through parser/typer/eval [shapes-static-types-bidirectional]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s

gp-parse-type-params consumes the optional [NAMES CONSTRAINT, ...]
clause after a func name. AST stays backward-compatible: 5-slot
func-decl when no [...] is present, 6-slot when it is.

Typer binds each type-param name as (:ty-param NAME CONSTRAINT) so
body's (:ty-name "T") references resolve. Eval is type-erasing —
ignores type info, dispatches by name + arity.

10 new tests: parse (3), types (5), eval (2). Total 527/527.

Shape: the field binding-group from the canonical kit now feeds
6 consumers (struct fields, var-decls, const-decls, params,
receivers, type-params). Confirms it as a TRUE cross-deliverable
shape — sister-plan diary documents the 5 roles binding-groups
take and why the kit should expose ONE parser + pluggable validators.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 00:31:28 +00:00
parent c50f5d5155
commit 459427512d
9 changed files with 264 additions and 34 deletions

View File

@@ -694,6 +694,42 @@
(when (> depth 0) (gp-block-loop)))
:else (do (gp-advance!) (gp-block-loop)))))
(gp-block-loop))))
(define
gp-parse-type-params
;; Optional [...] preceding a func/type decl's regular params.
;; Each group is `NAMES constraint-type` (re-uses the regular
;; param-group parser). Returns a list of (:field NAMES TY)
;; records, or nil if no `[` is present. Type-set constraints
;; (`T int | float64`) deferred.
(fn ()
(cond
(not (and (= (gp-tok-type) "op") (= (gp-tok-value) "[")))
nil
:else
(do
(gp-advance!)
(let ((groups (list)))
(define
gp-tp-loop
(fn ()
(cond
(and (= (gp-tok-type) "op") (= (gp-tok-value) "]"))
(gp-advance!)
:else
(let ((group (gp-parse-decl-param-group)))
(cond
(= group nil)
(do (gp-advance!) (gp-tp-loop))
:else
(do
(append! groups group)
(cond
(and (= (gp-tok-type) "op")
(= (gp-tok-value) ","))
(do (gp-advance!) (gp-tp-loop))
:else (gp-tp-loop))))))))
(gp-tp-loop)
groups)))))
(define
gp-parse-func-decl
;; Caller has consumed 'func'.
@@ -715,18 +751,31 @@
(= (gp-tok-type) "ident")
(let ((name (gp-tok-value)))
(gp-advance!)
(let ((params (gp-parse-func-decl-params)))
(let ((results (gp-parse-func-type-results)))
(let ((body nil))
(when (and (= (gp-tok-type) "op")
(= (gp-tok-value) "{"))
(gp-advance!)
(set! body (gp-parse-block-body)))
(cond
(= recv nil)
(list :func-decl name params results body)
:else
(list :method-decl recv name params results body))))))
;; Type parameters: [T any] / [T, U any] / [T any, U comparable].
;; Same shape as a regular param group — (:field NAMES TY).
;; Type-set constraints (T int | float64) deferred.
(let ((type-params (gp-parse-type-params)))
(let ((params (gp-parse-func-decl-params)))
(let ((results (gp-parse-func-type-results)))
(let ((body nil))
(when (and (= (gp-tok-type) "op")
(= (gp-tok-value) "{"))
(gp-advance!)
(set! body (gp-parse-block-body)))
;; Keep the legacy 5-slot shape when there are
;; no type params so existing AST consumers
;; (parse tests, types/eval pattern matchers)
;; stay compatible. Only add the 6th slot when
;; a `[...]` clause was actually present.
(cond
(and (= recv nil) (= type-params nil))
(list :func-decl name params results body)
(= recv nil)
(list :func-decl name params results body type-params)
(= type-params nil)
(list :method-decl recv name params results body)
:else
(list :method-decl recv name params results body type-params)))))))
:else nil))))
(define
gp-parse-case-body