Add provide/context/emit!/emitted — render-time dynamic scope

Four new primitives for scoped downward value passing and upward
accumulation through the render tree. Specced in .sx, bootstrapped
to Python and JS across all adapters (eval, html, sx, dom, async).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 02:58:21 +00:00
parent 41097eeef9
commit ea2b71cfa3
14 changed files with 436 additions and 18 deletions

View File

@@ -371,3 +371,51 @@
:effects [mutation]
:doc "Clear a named render-time accumulator bucket. Used at flush points
after emitting collected values (e.g. after writing a <style> tag).")
;; --------------------------------------------------------------------------
;; Tier 5: Dynamic scope — render-time provide/context/emit!
;;
;; `provide` is a special form (not a primitive) that creates a named scope
;; with a value and an empty accumulator. `context` reads the value from the
;; nearest enclosing provider. `emit!` appends to the accumulator, `emitted`
;; reads the accumulated values.
;;
;; The platform must implement per-name stacks. Each entry has a value and
;; an emitted list. `provide-push!`/`provide-pop!` manage the stack.
;; --------------------------------------------------------------------------
(declare-tier :dynamic-scope :source "eval.sx")
(declare-spread-primitive "provide-push!"
:params (name value)
:returns "nil"
:effects [mutation]
:doc "Push a provider scope with name and value (platform internal).")
(declare-spread-primitive "provide-pop!"
:params (name)
:returns "nil"
:effects [mutation]
:doc "Pop the most recent provider scope for name (platform internal).")
(declare-spread-primitive "context"
:params (name &rest default)
:returns "any"
:effects []
:doc "Read value from nearest enclosing provide with matching name.
Errors if no provider and no default given.")
(declare-spread-primitive "emit!"
:params (name value)
:returns "nil"
:effects [mutation]
:doc "Append value to nearest enclosing provide's accumulator.
Errors if no matching provider. No deduplication.")
(declare-spread-primitive "emitted"
:params (name)
:returns "list"
:effects []
:doc "Return list of values emitted into nearest matching provider.
Empty list if no provider.")