Unify scoped effects: scope as general primitive, provide as sugar
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 12m54s
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:
@@ -175,14 +175,14 @@
|
||||
(svg-context-set! true)
|
||||
nil))
|
||||
(content-parts (list)))
|
||||
(provide-push! "element-attrs" nil)
|
||||
(scope-push! "element-attrs" nil)
|
||||
(for-each
|
||||
(fn (c) (append! content-parts (async-render c env ctx)))
|
||||
children)
|
||||
(for-each
|
||||
(fn (spread-dict) (merge-spread-attrs attrs spread-dict))
|
||||
(emitted "element-attrs"))
|
||||
(provide-pop! "element-attrs")
|
||||
(scope-pop! "element-attrs")
|
||||
(when token (svg-context-reset! token))
|
||||
(str "<" tag (render-attrs attrs) ">"
|
||||
(join "" content-parts)
|
||||
@@ -335,7 +335,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 async-render-form? :effects []
|
||||
(fn ((name :as string))
|
||||
@@ -419,17 +419,37 @@
|
||||
(coll (async-eval (nth expr 2) env ctx)))
|
||||
(join "" (async-map-fn-render f coll env ctx)))
|
||||
|
||||
;; provide — render-time dynamic scope
|
||||
;; scope — unified render-time dynamic scope
|
||||
(= name "scope")
|
||||
(let ((scope-name (async-eval (nth expr 1) env ctx))
|
||||
(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 (async-eval (nth rest-args 1) env ctx))
|
||||
(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)
|
||||
(async-render (first body-exprs) env ctx)
|
||||
(join "" (async-map-render body-exprs env ctx)))))
|
||||
(scope-pop! scope-name)
|
||||
result))
|
||||
|
||||
;; provide — sugar for scope with value
|
||||
(= name "provide")
|
||||
(let ((prov-name (async-eval (nth expr 1) env ctx))
|
||||
(prov-val (async-eval (nth expr 2) env ctx))
|
||||
(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)
|
||||
(async-render (nth expr body-start) env ctx)
|
||||
(join "" (async-map-render (slice expr body-start) env ctx)))))
|
||||
(provide-pop! prov-name)
|
||||
(scope-pop! prov-name)
|
||||
result))
|
||||
|
||||
;; Fallback
|
||||
@@ -847,7 +867,7 @@
|
||||
(skip false)
|
||||
(i 0))
|
||||
;; Provide scope for spread emit!
|
||||
(provide-push! "element-attrs" nil)
|
||||
(scope-push! "element-attrs" nil)
|
||||
(for-each
|
||||
(fn (arg)
|
||||
(if skip
|
||||
@@ -890,7 +910,7 @@
|
||||
(append! attr-parts (serialize v))))
|
||||
(keys spread-dict)))
|
||||
(emitted "element-attrs"))
|
||||
(provide-pop! "element-attrs")
|
||||
(scope-pop! "element-attrs")
|
||||
(when token (svg-context-reset! token))
|
||||
(let ((parts (concat (list name) attr-parts child-parts)))
|
||||
(make-sx-expr (str "(" (join " " parts) ")"))))))
|
||||
@@ -906,7 +926,7 @@
|
||||
"define" "defcomp" "defmacro" "defstyle"
|
||||
"defhandler" "defpage" "defquery" "defaction"
|
||||
"begin" "do" "quote" "->" "set!" "defisland"
|
||||
"deftype" "defeffect" "provide"))
|
||||
"deftype" "defeffect" "scope" "provide"))
|
||||
|
||||
(define ASYNC_ASER_HO_NAMES
|
||||
(list "map" "map-indexed" "filter" "for-each"))
|
||||
@@ -1044,15 +1064,35 @@
|
||||
(= name "deftype") (= name "defeffect"))
|
||||
(do (async-eval expr env ctx) nil)
|
||||
|
||||
;; provide — render-time dynamic scope
|
||||
;; scope — unified render-time dynamic scope
|
||||
(= name "scope")
|
||||
(let ((scope-name (async-eval (first args) env ctx))
|
||||
(rest-args (rest args))
|
||||
(scope-val nil)
|
||||
(body-args 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 (async-eval (nth rest-args 1) env ctx))
|
||||
(set! body-args (slice rest-args 2)))
|
||||
(set! body-args rest-args))
|
||||
(scope-push! scope-name scope-val)
|
||||
(let ((result nil))
|
||||
(for-each (fn (body) (set! result (async-aser body env ctx)))
|
||||
body-args)
|
||||
(scope-pop! scope-name)
|
||||
result))
|
||||
|
||||
;; provide — sugar for scope with value
|
||||
(= name "provide")
|
||||
(let ((prov-name (async-eval (first args) env ctx))
|
||||
(prov-val (async-eval (nth args 1) env ctx))
|
||||
(result nil))
|
||||
(provide-push! prov-name prov-val)
|
||||
(scope-push! prov-name prov-val)
|
||||
(for-each (fn (body) (set! result (async-aser body env ctx)))
|
||||
(slice args 2))
|
||||
(provide-pop! prov-name)
|
||||
(scope-pop! prov-name)
|
||||
result)
|
||||
|
||||
;; Fallback
|
||||
|
||||
Reference in New Issue
Block a user