sx: step 9 — parser feature registry
Add `_hs-feature-registry` dict and `hs-register-feature!` to `lib/hyperscript/parser.sx`. Replace `parse-feat`'s hardcoded `cond` on feature names with a registry lookup; the paren-open and default-expression branches remain as fallthroughs. Each parse-fn receives a `ctx` dict (built per call by `parse-feat-ctx`) exposing parser internals (`:adv!`, `:tp-val`, `:tp-type`, `:at-end?`, `:parse-cmd-list`, `:parse-expr`) and the per-feature handlers (`:parse-on-feat` … `:parse-socket-feat`). All nine builtins (`on`, `init`, `def`, `behavior`, `live`, `when`, `worker`, `bind`, `socket`) are registered at file load time, so plugins added later via `hs-register-feature!` persist across `hs-parse` calls. Worker stub still raises identically. Mirror `shared/static/wasm/sx/hs-parser.sx` copied byte-identical. OCaml: 4545/1339, JS: 2591/2465 — both match baseline, zero regressions. First piece of plans/designs/hs-plugin-system.md (Steps 10/11 follow).
This commit is contained in:
@@ -3,6 +3,17 @@
|
||||
;; Input: list of {:type T :value V :pos P} tokens from hs-tokenize
|
||||
;; Output: SX AST forms that map to runtime primitives
|
||||
|
||||
;; ── Feature plugin registry ───────────────────────────────────────
|
||||
;; Plugins call (hs-register-feature! "name" parse-fn) at load time.
|
||||
;; parse-fn is (fn (ctx) ...) where ctx is a dict exposing parser
|
||||
;; helpers (:adv! :tp-val :tp-type :parse-cmd-list ...) and the
|
||||
;; built-in parse-X-feat dispatch fns.
|
||||
(begin
|
||||
(define _hs-feature-registry {})
|
||||
(define
|
||||
hs-register-feature!
|
||||
(fn (name parse-fn) (dict-set! _hs-feature-registry name parse-fn))))
|
||||
|
||||
;; ── Parser entry point ────────────────────────────────────────────
|
||||
(define
|
||||
hs-parse
|
||||
@@ -3231,6 +3242,24 @@
|
||||
(do
|
||||
(match-kw "end")
|
||||
(list (quote socket) name-path url timeout on-message))))))))))
|
||||
(define
|
||||
parse-feat-ctx
|
||||
(fn
|
||||
()
|
||||
{:adv! adv!
|
||||
:tp-val tp-val
|
||||
:tp-type tp-type
|
||||
:at-end? at-end?
|
||||
:parse-cmd-list parse-cmd-list
|
||||
:parse-expr parse-expr
|
||||
:parse-on-feat parse-on-feat
|
||||
:parse-init-feat parse-init-feat
|
||||
:parse-def-feat parse-def-feat
|
||||
:parse-behavior-feat parse-behavior-feat
|
||||
:parse-live-feat parse-live-feat
|
||||
:parse-when-feat parse-when-feat
|
||||
:parse-bind-feat parse-bind-feat
|
||||
:parse-socket-feat parse-socket-feat}))
|
||||
(define
|
||||
parse-feat
|
||||
(fn
|
||||
@@ -3261,29 +3290,23 @@
|
||||
((unit (tp-val)))
|
||||
(do (adv!) (list (quote string-postfix) inner unit)))
|
||||
inner))))
|
||||
((= val "on") (do (adv!) (parse-on-feat)))
|
||||
((= val "init") (do (adv!) (parse-init-feat)))
|
||||
((= val "def") (do (adv!) (parse-def-feat)))
|
||||
((= val "behavior") (do (adv!) (parse-behavior-feat)))
|
||||
((= val "live") (do (adv!) (parse-live-feat)))
|
||||
((= val "when") (do (adv!) (parse-when-feat)))
|
||||
((= val "worker")
|
||||
(error
|
||||
"worker plugin is not installed — see https://hyperscript.org/features/worker"))
|
||||
((= val "bind") (do (adv!) (parse-bind-feat)))
|
||||
((= val "socket") (do (adv!) (parse-socket-feat)))
|
||||
(true
|
||||
(if
|
||||
(= (tp-type) "keyword")
|
||||
(parse-cmd-list)
|
||||
(let
|
||||
((saved-p p))
|
||||
(let
|
||||
((expr (guard (_e (true nil)) (parse-expr))))
|
||||
(if
|
||||
(and expr (at-end?))
|
||||
expr
|
||||
(do (set! p saved-p) (parse-cmd-list)))))))))))
|
||||
(let
|
||||
((reg-fn (dict-get _hs-feature-registry val)))
|
||||
(if
|
||||
reg-fn
|
||||
(reg-fn (parse-feat-ctx))
|
||||
(if
|
||||
(= (tp-type) "keyword")
|
||||
(parse-cmd-list)
|
||||
(let
|
||||
((saved-p p))
|
||||
(let
|
||||
((expr (guard (_e (true nil)) (parse-expr))))
|
||||
(if
|
||||
(and expr (at-end?))
|
||||
expr
|
||||
(do (set! p saved-p) (parse-cmd-list)))))))))))))
|
||||
(define
|
||||
coll-feats
|
||||
(fn
|
||||
@@ -3326,3 +3349,36 @@
|
||||
(let
|
||||
((result (hs-parse (hs-tokenize src) src)))
|
||||
(do (set! hs-span-mode false) result)))))
|
||||
|
||||
;; ── Built-in feature registrations ────────────────────────────────
|
||||
;; These mirror the original parse-feat cond branches. Registering at
|
||||
;; load time means plugins can override or extend; ctx exposes the
|
||||
;; parser internals each fn needs.
|
||||
(begin
|
||||
(hs-register-feature!
|
||||
"on"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-on-feat)))))
|
||||
(hs-register-feature!
|
||||
"init"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-init-feat)))))
|
||||
(hs-register-feature!
|
||||
"def"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-def-feat)))))
|
||||
(hs-register-feature!
|
||||
"behavior"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-behavior-feat)))))
|
||||
(hs-register-feature!
|
||||
"live"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-live-feat)))))
|
||||
(hs-register-feature!
|
||||
"when"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-when-feat)))))
|
||||
(hs-register-feature!
|
||||
"worker"
|
||||
(fn (ctx) (error "worker plugin is not installed — see https://hyperscript.org/features/worker")))
|
||||
(hs-register-feature!
|
||||
"bind"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-bind-feat)))))
|
||||
(hs-register-feature!
|
||||
"socket"
|
||||
(fn (ctx) (begin ((dict-get ctx :adv!)) ((dict-get ctx :parse-socket-feat))))))
|
||||
|
||||
Reference in New Issue
Block a user