Unify scoped effects: scope as general primitive, provide as sugar
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 12m54s

- Add `scope` special form to eval.sx: (scope name body...) or
  (scope name :value v body...) — general dynamic scope primitive
- `provide` becomes sugar: (provide name value body...) calls scope
- Rename provide-push!/provide-pop! to scope-push!/scope-pop! throughout
  all adapters (async, dom, html, sx) and platform implementations
- Update boundary.sx: Tier 5 now "Scoped effects" with scope-push!/
  scope-pop! as primary, provide-push!/provide-pop! as aliases
- Add scope form handling to async adapter and aser wire format
- Update sx-browser.js, sx_ref.py (bootstrapped output)
- Add scopes.sx docs page, update provide/spreads/demo docs
- Update nav-data, page-functions, docs page definitions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 17:30:34 +00:00
parent 6ca46bb295
commit 11fdd1a840
23 changed files with 869 additions and 285 deletions

View File

@@ -56,7 +56,7 @@
(list "if" "when" "cond" "case" "let" "let*" "begin" "do"
"define" "defcomp" "defisland" "defmacro" "defstyle" "defhandler"
"deftype" "defeffect"
"map" "map-indexed" "filter" "for-each" "provide"))
"map" "map-indexed" "filter" "for-each" "scope" "provide"))
(define render-html-form? :effects []
(fn ((name :as string))
@@ -229,18 +229,38 @@
(render-to-html (apply f (list item)) env)))
coll)))
;; provide — render-time dynamic scope
;; scope — unified render-time dynamic scope
(= name "scope")
(let ((scope-name (trampoline (eval-expr (nth expr 1) env)))
(rest-args (slice expr 2))
(scope-val nil)
(body-exprs nil))
;; Check for :value keyword
(if (and (>= (len rest-args) 2)
(= (type-of (first rest-args)) "keyword")
(= (keyword-name (first rest-args)) "value"))
(do (set! scope-val (trampoline (eval-expr (nth rest-args 1) env)))
(set! body-exprs (slice rest-args 2)))
(set! body-exprs rest-args))
(scope-push! scope-name scope-val)
(let ((result (if (= (len body-exprs) 1)
(render-to-html (first body-exprs) env)
(join "" (map (fn (e) (render-to-html e env)) body-exprs)))))
(scope-pop! scope-name)
result))
;; provide — sugar for scope with value
(= name "provide")
(let ((prov-name (trampoline (eval-expr (nth expr 1) env)))
(prov-val (trampoline (eval-expr (nth expr 2) env)))
(body-start 3)
(body-count (- (len expr) 3)))
(provide-push! prov-name prov-val)
(scope-push! prov-name prov-val)
(let ((result (if (= body-count 1)
(render-to-html (nth expr body-start) env)
(join "" (map (fn (i) (render-to-html (nth expr i) env))
(range body-start (+ body-start body-count)))))))
(provide-pop! prov-name)
(scope-pop! prov-name)
result))
;; Fallback
@@ -314,12 +334,12 @@
(str "<" tag (render-attrs attrs) " />")
;; Provide scope for spread emit!
(do
(provide-push! "element-attrs" nil)
(scope-push! "element-attrs" nil)
(let ((content (join "" (map (fn (c) (render-to-html c env)) children))))
(for-each
(fn (spread-dict) (merge-spread-attrs attrs spread-dict))
(emitted "element-attrs"))
(provide-pop! "element-attrs")
(scope-pop! "element-attrs")
(str "<" tag (render-attrs attrs) ">"
content
"</" tag ">")))))))
@@ -359,12 +379,12 @@
args)
;; Provide scope for spread emit!
(let ((lake-attrs (dict "data-sx-lake" (or lake-id ""))))
(provide-push! "element-attrs" nil)
(scope-push! "element-attrs" nil)
(let ((content (join "" (map (fn (c) (render-to-html c env)) children))))
(for-each
(fn (spread-dict) (merge-spread-attrs lake-attrs spread-dict))
(emitted "element-attrs"))
(provide-pop! "element-attrs")
(scope-pop! "element-attrs")
(str "<" lake-tag (render-attrs lake-attrs) ">"
content
"</" lake-tag ">"))))))
@@ -407,12 +427,12 @@
args)
;; Provide scope for spread emit!
(let ((marsh-attrs (dict "data-sx-marsh" (or marsh-id ""))))
(provide-push! "element-attrs" nil)
(scope-push! "element-attrs" nil)
(let ((content (join "" (map (fn (c) (render-to-html c env)) children))))
(for-each
(fn (spread-dict) (merge-spread-attrs marsh-attrs spread-dict))
(emitted "element-attrs"))
(provide-pop! "element-attrs")
(scope-pop! "element-attrs")
(str "<" marsh-tag (render-attrs marsh-attrs) ">"
content
"</" marsh-tag ">"))))))