HS parser: fix number+comparison keyword collision, eval-hs uses hs-compile
Parser: skip unit suffix when next ident is a comparison keyword (starts, ends, contains, matches, is, does, in, precedes, follows). Fixes "123 starts with '12'" returning "123starts" instead of true. eval-hs: use hs-compile directly instead of hs-to-sx-from-source with "return " prefix, which was causing the parser to consume the comparison as a string suffix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
sx/sx/geography/reactive-runtime/machine/index.sx
Normal file
33
sx/sx/geography/reactive-runtime/machine/index.sx
Normal file
@@ -0,0 +1,33 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; L2: State Machines
|
||||
;; ---------------------------------------------------------------------------
|
||||
(defcomp ()
|
||||
(~docs/page :title "Layer 2: State Machines"
|
||||
|
||||
(p (~tw :tokens "text-stone-500 text-sm italic mb-8")
|
||||
"Modal state management where machine state IS a signal. "
|
||||
"Composes naturally with computed and effects.")
|
||||
|
||||
(~docs/section :title "Live Demo" :id "demo"
|
||||
(p "A traffic light built from a signal and a transitions dict. Auto-advance uses an effect with " (code "set-timeout") " — when state changes, the effect re-runs and schedules the next transition.")
|
||||
(~reactive-runtime/demo-machine)
|
||||
(~docs/code :src (highlight "(defisland ~demo-machine ()\n (let ((state (signal \"red\"))\n (transitions (dict \"red\" \"green\" \"green\" \"yellow\" \"yellow\" \"red\"))\n (auto (signal false)))\n ;; Auto-advance: effect depends on both state and auto\n (effect (fn ()\n (when (deref auto)\n (let ((delay (if (= (deref state) \"yellow\") 1000 2500)))\n (let ((id (set-timeout\n (fn () (reset! state (get transitions (deref state))))\n delay)))\n (fn () (clear-timeout id)))))))\n ;; Display: reactive class on each light\n (div :class (str \"...\" (if (= (deref state) \"red\")\n \"bg-red-500\" \"bg-red-900/30\")) ...)))" "lisp"))
|
||||
(p "The machine state is a signal — " (code "(deref state)") " in the effect body subscribes, so when the timeout fires and resets state, the effect re-runs with the new delay. The " (code "make-machine") " function wraps this pattern with transition tables, guards, and enter/exit hooks."))
|
||||
|
||||
(~docs/section :title "API" :id "machine-api"
|
||||
|
||||
(~docs/code :src (highlight
|
||||
"(define drawing-tool (make-machine\n {:initial :idle\n :states {:idle {:on {:pointer-down (fn (ev)\n {:state :drawing\n :actions (list (fn () (start-shape! ev)))})}}\n :drawing {:on {:pointer-move (fn (ev)\n {:state :drawing\n :actions (list (fn () (update-shape! ev)))})\n :pointer-up (fn (ev)\n {:state :idle\n :actions (list (fn () (finish-shape! ev)))})}}}}))\n\n;; Send events\n(machine-send! drawing-tool :pointer-down event)\n\n;; Read state (reactive — triggers re-render)\n(deref (machine-state drawing-tool)) ;; => :drawing\n(machine-matches? drawing-tool :idle) ;; => false"
|
||||
"lisp"))
|
||||
|
||||
(p (code "make-machine") " returns a dict with a state signal, transitions map, and send function. "
|
||||
"Handlers return " (code "{:state :new-state :actions [fn ...]}") " — "
|
||||
"actions are called after the state transition."))
|
||||
|
||||
(~docs/section :title "Implementation Sketch" :id "machine-impl"
|
||||
|
||||
(~docs/code :src (highlight
|
||||
"(define make-machine (fn (config)\n (let ((current (signal (get config :initial)))\n (states (get config :states)))\n (dict\n \"__machine\" true\n \"state\" current\n \"states\" states))))\n\n(define machine-state (fn (m) (get m \"state\")))\n\n(define machine-matches? (fn (m s)\n (= (deref (get m \"state\")) s)))\n\n(define machine-send! (fn (m event &rest data)\n (let ((current-state (deref (get m \"state\")))\n (state-config (get (get m \"states\") current-state))\n (handlers (get state-config :on))\n (handler (get handlers event)))\n (when handler\n (let ((result (apply handler data)))\n (when (get result :state)\n (reset! (get m \"state\") (get result :state)))\n (when (get result :actions)\n (for-each (fn (action) (action))\n (get result :actions))))))))"
|
||||
"lisp"))
|
||||
|
||||
(p "~200 lines with guard conditions, context data, and enter/exit hooks."))))
|
||||
Reference in New Issue
Block a user