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:
2026-04-15 11:29:01 +00:00
parent a93e5924df
commit 4f02f82f4e
377 changed files with 9517 additions and 8694 deletions

View File

@@ -0,0 +1,24 @@
;; GraphQL playground API handler — returns raw text for innerHTML injection
(defhandler
gql-parse-demo
:path "/sx/(applications.(graphql.(api.parse-demo)))"
:method :get
:returns "text"
(&key source)
(if
(or (nil? source) (empty? source))
"<p class=\"text-sm text-gray-400 italic\">Enter a GraphQL query and click Parse.</p>"
(let
((doc (gql-parse source)))
(str
"<div class=\"space-y-4\">"
"<div><h4 class=\"text-xs font-semibold uppercase tracking-wider text-gray-500 mb-2\">AST</h4>"
"<pre class=\"bg-gray-900 text-green-400 p-4 rounded-lg text-sm overflow-x-auto\">"
(escape (sx-serialize doc))
"</pre></div>"
"<div><h4 class=\"text-xs font-semibold uppercase tracking-wider text-gray-500 mb-2\">Serialized</h4>"
"<pre class=\"bg-gray-900 text-amber-400 p-4 rounded-lg text-sm overflow-x-auto\">"
(escape (gql-serialize doc))
"</pre></div></div>"))))

138
sx/sx/handlers/htmx-api.sx Normal file
View File

@@ -0,0 +1,138 @@
;; htmx demo API handlers — return HTML fragments for hx-* demos
;; Click to load
(defhandler
ex-click
:path "/sx/(applications.(htmx.(api.click)))"
:method :get
:returns "element"
(&key)
(div
(~tw :tokens "space-y-2")
(p (~tw :tokens "text-stone-800 font-medium") "Content loaded!")
(p
(~tw :tokens "text-stone-500 text-sm")
"Fetched from the server via hx-get")))
;; Active search
(defhandler
ex-search
:path "/sx/(applications.(htmx.(api.search)))"
:method :get
:returns "element"
(&key q)
(let
((query (or q "")))
(let
((items (list {:desc "GET request" :name "hx-get"} {:desc "POST request" :name "hx-post"} {:desc "Swap strategy" :name "hx-swap"} {:desc "Target element" :name "hx-target"} {:desc "Event trigger" :name "hx-trigger"} {:desc "Confirm dialog" :name "hx-confirm"} {:desc "Progressive enhancement" :name "hx-boost"} {:desc "CSS selector filter" :name "hx-select"})))
(let
((filtered (if (= query "") (list) (filter (fn (item) (contains? (downcase (get item :name)) (downcase query))) items))))
(if
(empty? filtered)
(p
(~tw :tokens "text-gray-400 italic py-2")
(str "No results for \"" query "\""))
(ul
(~tw :tokens "divide-y divide-gray-100")
(map
(fn
(item)
(li
(~tw :tokens "py-2 px-1")
(span
(~tw :tokens "font-medium text-gray-800")
(get item :name))
(span
(~tw :tokens "text-sm text-gray-500 ml-2")
(get item :desc))))
filtered)))))))
;; Tabs
(defhandler
ex-tab
:path "/sx/(applications.(htmx.(api.tab)))"
:method :get
:returns "element"
(&key tab)
(let
((tab (or tab "overview")))
(cond
(= tab "overview")
(div
(~tw :tokens "p-4 space-y-2")
(h3 (~tw :tokens "font-semibold text-stone-800") "Overview")
(p
(~tw :tokens "text-stone-600 text-sm")
"htmx gives you access to AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes."))
(= tab "features")
(div
(~tw :tokens "p-4 space-y-2")
(h3 (~tw :tokens "font-semibold text-stone-800") "Features")
(ul
(~tw
:tokens "list-disc list-inside text-sm text-stone-600 space-y-1")
(li "Any element, any event")
(li "Multiple swap strategies")
(li "CSS transition support")
(li "Out-of-band swaps")))
(= tab "code")
(div
(~tw :tokens "p-4")
(pre
(~tw :tokens "text-sm bg-stone-100 rounded p-3")
(code "(button :hx-get \"/api\" :hx-target \"#out\"\n \"Load\")")))
true
(div (~tw :tokens "p-4 text-stone-400") "Unknown tab"))))
;; Delete item — returns empty fragment to remove the element
(defhandler
ex-delete
:path "/sx/(applications.(htmx.(api.delete)))"
:method :delete
:csrf false
:returns "element"
(&key)
(<>))
;; Form submission
(defhandler
ex-form
:path "/sx/(applications.(htmx.(api.form)))"
:method :post
:csrf false
:returns "element"
(&key name email)
(div
(~tw :tokens "p-4 bg-green-50 border border-green-200 rounded-lg")
(p
(~tw :tokens "text-green-800 font-medium")
(str "Hello, " (or name "stranger") "!"))
(p
(~tw :tokens "text-sm text-green-600 mt-1")
(str "Email: " (or email "not provided")))))
;; Slow endpoint for indicator demo
(defhandler
ex-slow
:path "/sx/(applications.(htmx.(api.slow)))"
:method :get
:returns "element"
(&key)
(div
(~tw :tokens "p-4 bg-amber-50 border border-amber-200 rounded-lg")
(p (~tw :tokens "text-amber-800 font-medium") "Slow response received")
(p
(~tw :tokens "text-sm text-amber-600 mt-1")
"This response was delayed")))
;; Append item (beforeend swap demo)
(defhandler
ex-append
:path "/sx/(applications.(htmx.(api.append)))"
:method :post
:csrf false
:returns "element"
(&key)
(div
(~tw :tokens "p-2 border-b border-gray-100 text-sm text-stone-700")
"New item appended"))

View File

@@ -1,7 +1,6 @@
;; _hyperscript playground API handler
(defhandler
hs-compile
:path "/sx/(applications.(hyperscript.(api.compile)))"
:method :post
:csrf false

View File

@@ -1,5 +1,4 @@
(defhandler
spec-detail
:path "/sx/api/spec-detail"
:method :get
:returns "element"