;; _hyperscript Language Specification
;; SX Implementation — Formal Reference
(defcomp
()
(~docs/page
:title "_hyperscript Language Specification"
(p
(~tw :tokens "text-lg text-gray-600 mb-8")
"This specification defines the _hyperscript language as implemented by the SX "
"compiler pipeline. _hyperscript source is compiled ahead-of-time to SX expressions "
"targeting the DOM platform primitives — there is no interpreter.")
(~docs/section
:title "1. Overview"
:id "overview"
(p "The _hyperscript compilation pipeline has three stages:")
(ol
(~tw :tokens "list-decimal list-inside space-y-1 text-gray-700 mb-4")
(li
(strong "Tokenize")
" — source string to token list ("
(code "hs-tokenize")
")")
(li
(strong "Parse")
" — token list to AST ("
(code "hs-parse")
", "
(code "hs-compile")
")")
(li
(strong "Compile")
" — AST to SX expressions ("
(code "hs-to-sx")
", "
(code "hs-to-sx-from-source")
")"))
(p
"The compiled SX targets runtime shims in "
(code "lib/hyperscript/runtime.sx")
" which delegate to "
(code "web/lib/dom.sx")
" platform primitives.")
(~docs/subsection
:title "1.1 Attribute Binding"
:id "attribute-binding"
(p
"Hyperscript is bound to DOM elements via the "
(code "_")
" attribute. At boot time, "
(code "hs-boot!")
" scans the document, "
"compiles each attribute value to an SX closure, and invokes it "
"with the element as "
(code "me")
". Dynamic content is activated via "
(code "hs-boot-subtree!")
".")))
(~docs/section
:title "2. Lexical Grammar"
:id "lexical-grammar"
(p
"The tokenizer produces a list of token dicts, each with "
(code ":type")
", "
(code ":value")
", and "
(code ":pos")
" keys.")
(~docs/subsection
:title "2.1 Token Types"
:id "token-types"
(table
(~tw :tokens "w-full text-sm border-collapse mb-6")
(thead
(~tw :tokens "bg-gray-50")
(tr
(th (~tw :tokens "text-left p-2 border-b font-semibold") "Type")
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Pattern")
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Examples")))
(tbody
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "keyword")
(td (~tw :tokens "p-2") "Reserved word from keyword set")
(td (~tw :tokens "p-2 font-mono") "on, set, add, if, for, def"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "ident")
(td (~tw :tokens "p-2") "Identifier (not a keyword)")
(td (~tw :tokens "p-2 font-mono") "count, x, myVar"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "number")
(td
(~tw :tokens "p-2")
"Integer or decimal, optional ms/s suffix")
(td (~tw :tokens "p-2 font-mono") "42, 3.14, 200ms, 2s"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "string")
(td
(~tw :tokens "p-2")
"Single or double quoted, backslash escapes")
(td (~tw :tokens "p-2 font-mono") "hello, world"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "template")
(td (~tw :tokens "p-2") "Backtick-delimited with interpolation")
(td (~tw :tokens "p-2 font-mono") "count is ..."))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "class")
(td
(~tw :tokens "p-2")
(code ".")
" followed by CSS class name")
(td (~tw :tokens "p-2 font-mono") ".active, .text-red-500"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "id")
(td (~tw :tokens "p-2") (code "#") " followed by identifier")
(td (~tw :tokens "p-2 font-mono") "#main, #output"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "attr")
(td (~tw :tokens "p-2") (code "@") " followed by identifier")
(td (~tw :tokens "p-2 font-mono") "@href, @disabled"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "style")
(td (~tw :tokens "p-2") (code "*") " followed by identifier")
(td (~tw :tokens "p-2 font-mono") "*opacity, *display"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "selector")
(td (~tw :tokens "p-2") "CSS selector in angle brackets")
(td (~tw :tokens "p-2 font-mono") ", <.item/>"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "local")
(td (~tw :tokens "p-2") (code ":") " followed by identifier")
(td (~tw :tokens "p-2 font-mono") ":count, :name"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "component")
(td (~tw :tokens "p-2") (code "~") " followed by identifier")
(td (~tw :tokens "p-2 font-mono") "~card, ~badge"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "op")
(td (~tw :tokens "p-2") "Operator")
(td (~tw :tokens "p-2 font-mono") "+, -, *, =, ==, !="))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "dot")
(td (~tw :tokens "p-2") "Property access operator")
(td (~tw :tokens "p-2 font-mono") "."))
(tr
(td (~tw :tokens "p-2 font-mono") "eof")
(td (~tw :tokens "p-2") "End of input")
(td (~tw :tokens "p-2") "—")))))
(~docs/subsection
:title "2.2 Comments"
:id "comments"
(p
"Line comments start with "
(code "//")
" and extend to end of line."))
(~docs/subsection
:title "2.3 Keywords (107 reserved words)"
:id "keywords"
(p
"on end set to put into before after add remove toggle "
"if else otherwise then from in of for until wait send "
"trigger call get take log hide show repeat while times "
"forever break continue return throw catch finally def "
"tell make fetch as with every or and not is no the my "
"me it its result true false null when between at by "
"queue elsewhere event target detail sender index "
"increment decrement append settle transition over "
"closest next previous first last random empty exists "
"matches contains do unless you your new init start go "
"js less than greater class anything install measure "
"behavior called render eval"))
(~docs/subsection
:title "2.4 Possessive Operator"
:id "possessive"
(p
"The token "
(code "'s")
" is the possessive operator — equivalent to "
"property access. Only tokenized when followed by whitespace or end-of-input; "
"otherwise "
(code "'")
" begins a string literal.")))
(~docs/section
:title "3. Syntactic Grammar"
:id "syntactic-grammar"
(p
"A program is a sequence of "
(em "features")
" — top-level declarations "
"that define element behavior.")
(~docs/subsection
:title "3.1 Features"
:id "features"
(table
(~tw :tokens "w-full text-sm border-collapse mb-6")
(thead
(~tw :tokens "bg-gray-50")
(tr
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Feature")
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Syntax")
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Description")))
(tbody
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "on")
(td (~tw :tokens "p-2 text-xs") "on event cmds end")
(td (~tw :tokens "p-2") "Event handler"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "on from")
(td (~tw :tokens "p-2 text-xs") "on event from target cmds end")
(td (~tw :tokens "p-2") "Listen on a different element"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "every")
(td (~tw :tokens "p-2 text-xs") "on every event cmds end")
(td
(~tw :tokens "p-2")
"No queuing — each fires independently"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "def")
(td (~tw :tokens "p-2 text-xs") "def name(params) cmds end")
(td (~tw :tokens "p-2") "Function definition"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "behavior")
(td (~tw :tokens "p-2 text-xs") "behavior Name features end")
(td (~tw :tokens "p-2") "Reusable behavior"))
(tr
(td (~tw :tokens "p-2 font-mono") "init")
(td (~tw :tokens "p-2 text-xs") "init cmds end")
(td (~tw :tokens "p-2") "Run at element boot")))))
(~docs/subsection
:title "3.2 Commands"
:id "commands"
(p
"Commands are imperative statements separated by "
(code "then")
" or newlines.")
(~docs/subsection
:title "Class Manipulation"
:id "cmd-class"
(p (code "add .cls to target") " — append class")
(p (code "remove .cls from target") " — remove class")
(p (code "toggle .cls on target") " — flip one class")
(p
(code "toggle between .a and .b on target")
" — alternate two classes")
(p (code "take .cls") " — add to target, remove from siblings"))
(~docs/subsection
:title "Assignment"
:id "cmd-assignment"
(p (code "set x to 42") " — assign to variable")
(p (code "set my innerHTML to value") " — assign to property")
(p
(code "put value into target")
" — insert content (also before/after)"))
(~docs/subsection
:title "Control Flow"
:id "cmd-control-flow"
(p (code "if condition ... else ... end") " — conditional")
(p (code "for item in collection ... end") " — iteration")
(p (code "repeat N times ... end") " — counted loop")
(p (code "repeat forever ... end") " — infinite loop"))
(~docs/subsection
:title "Events"
:id "cmd-events"
(p
(code "send eventName to target")
" — dispatch with optional detail")
(p (code "trigger eventName") " — dispatch on me")
(p (code "wait 200ms") " — pause execution")
(p (code "wait for transitionend") " — suspend until DOM event"))
(~docs/subsection
:title "DOM"
:id "cmd-dom"
(p (code "hide me") " — set display: none")
(p (code "show me") " — restore display")
(p (code "log value") " — console.log")
(p (code "go to url path") " — navigate"))
(~docs/subsection
:title "Other Commands"
:id "cmd-other"
(p (code "increment :count") " / " (code "decrement :count by 5"))
(p (code "tell target ... end") " — rebind me for block")
(p (code "append value to :list") " — add to collection")
(p (code "fetch url as json") " — HTTP request (json/text/html)")
(p (code "call myFunc(args)") " — function call")
(p (code "make a Set") " — create object")
(p (code "measure me") " — bounding rect")
(p
(code "transition *opacity to 0 over 300ms")
" — CSS animation")
(p (code "install Behavior") " — attach reusable behavior")
(p (code "return value") " — exit def")
(p (code "throw message") " — raise error")))
(~docs/subsection
:title "3.3 Expressions"
:id "expressions"
(~docs/subsection
:title "Literals"
:id "expr-literals"
(p (code "42") ", " (code "3.14") " — numbers")
(p (code "200ms") ", " (code "2s") " — durations")
(p "Single/double quoted strings, backtick template strings")
(p (code "true") ", " (code "false") ", " (code "null"))
(p (code "[1, 2, 3]") " — array literals"))
(~docs/subsection
:title "References"
:id "expr-references"
(table
(~tw :tokens "w-full text-sm border-collapse mb-4")
(thead
(~tw :tokens "bg-gray-50")
(tr
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Syntax")
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Meaning")
(th
(~tw :tokens "text-left p-2 border-b font-semibold")
"Compiles to")))
(tbody
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "me")
(td (~tw :tokens "p-2") "Current element")
(td (~tw :tokens "p-2 font-mono") "me"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "my prop")
(td (~tw :tokens "p-2") "Property of me")
(td (~tw :tokens "p-2 font-mono") "(. me prop)"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "it / result")
(td (~tw :tokens "p-2") "Last implicit result")
(td (~tw :tokens "p-2 font-mono") "it"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") ":name")
(td (~tw :tokens "p-2") "Element-scoped local")
(td (~tw :tokens "p-2 font-mono") ":name"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "@attr")
(td (~tw :tokens "p-2") "DOM attribute")
(td (~tw :tokens "p-2 font-mono") "(dom-get-attr me attr)"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "*style")
(td (~tw :tokens "p-2") "CSS style property")
(td (~tw :tokens "p-2 font-mono") "(dom-get-style me style)"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "#id")
(td (~tw :tokens "p-2") "Element by ID")
(td (~tw :tokens "p-2 font-mono") "(dom-query #id)"))
(tr
(~tw :tokens "border-b")
(td (~tw :tokens "p-2 font-mono") "