Add (param :as type) annotations to all fn/lambda params across SX spec

Extend the type annotation system from defcomp-only to fn/lambda params:
- Infrastructure: sf-lambda, py/js-collect-params-loop, and bootstrap_py.py
  now recognize (name :as type) in param lists, extracting just the name
- bootstrap_py.py: add _extract_param_name() helper, fix _emit_for_each_stmt
- 521 type annotations across 22 .sx spec files (eval, types, adapters,
  transpilers, engine, orchestration, deps, signals, router, prove, etc.)
- Zero behavioral change: annotations are metadata for static analysis only
- All bootstrappers (Python, JS, G1) pass, 81/81 spec tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 20:27:36 +00:00
parent c82941d93c
commit b99e69d1bb
23 changed files with 532 additions and 498 deletions

View File

@@ -39,7 +39,7 @@
(define scan-refs-walk
(fn (node refs)
(fn (node (refs :as list))
(cond
;; Symbol starting with ~ → component reference
(= (type-of node) "symbol")
@@ -68,26 +68,26 @@
;; that it can transitively render. Handles cycles via seen-set.
(define transitive-deps-walk
(fn (n seen env)
(fn ((n :as string) (seen :as list) (env :as dict))
(when (not (contains? seen n))
(append! seen n)
(let ((val (env-get env n)))
(cond
(= (type-of val) "component")
(for-each (fn (ref) (transitive-deps-walk ref seen env))
(for-each (fn ((ref :as string)) (transitive-deps-walk ref seen env))
(scan-refs (component-body val)))
(= (type-of val) "macro")
(for-each (fn (ref) (transitive-deps-walk ref seen env))
(for-each (fn ((ref :as string)) (transitive-deps-walk ref seen env))
(scan-refs (macro-body val)))
:else nil)))))
(define transitive-deps
(fn (name env)
(fn ((name :as string) (env :as dict))
(let ((seen (list))
(key (if (starts-with? name "~") name (str "~" name))))
(transitive-deps-walk key seen env)
(filter (fn (x) (not (= x key))) seen))))
(filter (fn ((x :as string)) (not (= x key))) seen))))
;; --------------------------------------------------------------------------
@@ -101,9 +101,9 @@
;; (component-set-deps! comp deps) → store deps on component
(define compute-all-deps
(fn (env)
(fn ((env :as dict))
(for-each
(fn (name)
(fn ((name :as string))
(let ((val (env-get env name)))
(when (= (type-of val) "component")
(component-set-deps! val (transitive-deps name env)))))
@@ -120,9 +120,9 @@
;; (regex-find-all pattern source) → list of matched group strings
(define scan-components-from-source
(fn (source)
(fn ((source :as string))
(let ((matches (regex-find-all "\\(~([a-zA-Z_][a-zA-Z0-9_\\-]*)" source)))
(map (fn (m) (str "~" m)) matches))))
(map (fn ((m :as string)) (str "~" m)) matches))))
;; --------------------------------------------------------------------------
@@ -132,13 +132,13 @@
;; the transitive closure. Returns list of ~names.
(define components-needed
(fn (page-source env)
(fn ((page-source :as string) (env :as dict))
(let ((direct (scan-components-from-source page-source))
(all-needed (list)))
;; Add each direct ref + its transitive deps
(for-each
(fn (name)
(fn ((name :as string))
(when (not (contains? all-needed name))
(append! all-needed name))
(let ((val (env-get env name)))
@@ -147,7 +147,7 @@
(component-deps val)
(transitive-deps name env))))
(for-each
(fn (dep)
(fn ((dep :as string))
(when (not (contains? all-needed dep))
(append! all-needed dep)))
deps))))
@@ -166,7 +166,7 @@
;; This replaces the "send everything" approach with per-page bundles.
(define page-component-bundle
(fn (page-source env)
(fn ((page-source :as string) (env :as dict))
(components-needed page-source env)))
@@ -181,17 +181,17 @@
;; (scan-css-classes source) → set/list of class strings from source
(define page-css-classes
(fn (page-source env)
(fn ((page-source :as string) (env :as dict))
(let ((needed (components-needed page-source env))
(classes (list)))
;; Collect classes from needed components
(for-each
(fn (name)
(fn ((name :as string))
(let ((val (env-get env name)))
(when (= (type-of val) "component")
(for-each
(fn (cls)
(fn ((cls :as string))
(when (not (contains? classes cls))
(append! classes cls)))
(component-css-classes val)))))
@@ -199,7 +199,7 @@
;; Add classes from page source
(for-each
(fn (cls)
(fn ((cls :as string))
(when (not (contains? classes cls))
(append! classes cls)))
(scan-css-classes page-source))
@@ -219,7 +219,7 @@
;; (component-set-io-refs! c r) → cache IO refs on component
(define scan-io-refs-walk
(fn (node io-names refs)
(fn (node (io-names :as list) (refs :as list))
(cond
;; Symbol → check if name is in the IO set
(= (type-of node) "symbol")
@@ -242,7 +242,7 @@
(define scan-io-refs
(fn (node io-names)
(fn (node (io-names :as list))
(let ((refs (list)))
(scan-io-refs-walk node io-names refs)
refs)))
@@ -253,7 +253,7 @@
;; --------------------------------------------------------------------------
(define transitive-io-refs-walk
(fn (n seen all-refs env io-names)
(fn ((n :as string) (seen :as list) (all-refs :as list) (env :as dict) (io-names :as list))
(when (not (contains? seen n))
(append! seen n)
(let ((val (env-get env n)))
@@ -262,31 +262,31 @@
(do
;; Scan this component's body for IO refs
(for-each
(fn (ref)
(fn ((ref :as string))
(when (not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (component-body val) io-names))
;; Recurse into component deps
(for-each
(fn (dep) (transitive-io-refs-walk dep seen all-refs env io-names))
(fn ((dep :as string)) (transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (component-body val))))
(= (type-of val) "macro")
(do
(for-each
(fn (ref)
(fn ((ref :as string))
(when (not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (macro-body val) io-names))
(for-each
(fn (dep) (transitive-io-refs-walk dep seen all-refs env io-names))
(fn ((dep :as string)) (transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (macro-body val))))
:else nil)))))
(define transitive-io-refs
(fn (name env io-names)
(fn ((name :as string) (env :as dict) (io-names :as list))
(let ((all-refs (list))
(seen (list))
(key (if (starts-with? name "~") name (str "~" name))))
@@ -299,9 +299,9 @@
;; --------------------------------------------------------------------------
(define compute-all-io-refs
(fn (env io-names)
(fn ((env :as dict) (io-names :as list))
(for-each
(fn (name)
(fn ((name :as string))
(let ((val (env-get env name)))
(when (= (type-of val) "component")
(component-set-io-refs! val (transitive-io-refs name env io-names)))))
@@ -309,7 +309,7 @@
(define component-io-refs-cached
(fn (name env io-names)
(fn ((name :as string) (env :as dict) (io-names :as list))
(let ((key (if (starts-with? name "~") name (str "~" name))))
(let ((val (env-get env key)))
(if (and (= (type-of val) "component")
@@ -320,7 +320,7 @@
(transitive-io-refs name env io-names))))))
(define component-pure?
(fn (name env io-names)
(fn ((name :as string) (env :as dict) (io-names :as list))
(let ((key (if (starts-with? name "~") name (str "~" name))))
(let ((val (env-get env key)))
(if (and (= (type-of val) "component")
@@ -344,7 +344,7 @@
;; Returns: "server" | "client"
(define render-target
(fn (name env io-names)
(fn ((name :as string) (env :as dict) (io-names :as list))
(let ((key (if (starts-with? name "~") name (str "~" name))))
(let ((val (env-get env key)))
(if (not (= (type-of val) "component"))
@@ -373,7 +373,7 @@
;; without recomputing at every request.
(define page-render-plan
(fn (page-source env io-names)
(fn ((page-source :as string) (env :as dict) (io-names :as list))
(let ((needed (components-needed page-source env))
(comp-targets (dict))
(server-list (list))
@@ -381,7 +381,7 @@
(io-deps (list)))
(for-each
(fn (name)
(fn ((name :as string))
(let ((target (render-target name env io-names)))
(dict-set! comp-targets name target)
(if (= target "server")
@@ -389,7 +389,7 @@
(append! server-list name)
;; Collect IO deps from server components (use cache)
(for-each
(fn (io-ref)
(fn ((io-ref :as string))
(when (not (contains? io-deps io-ref))
(append! io-deps io-ref)))
(component-io-refs-cached name env io-names)))
@@ -451,9 +451,9 @@
;; Moved from platform to spec: pure logic using type predicates.
(define env-components
(fn (env)
(fn ((env :as dict))
(filter
(fn (k)
(fn ((k :as string))
(let ((v (env-get env k)))
(or (component? v) (macro? v))))
(keys env))))