Hyperscript conformance: 222 test fixtures from _hyperscript 0.9.14

Extract pure expression tests from the official _hyperscript test suite
and implement parser/compiler/runtime extensions to pass them.

Test infrastructure:
- 222 fixtures extracted from evalHyperScript calls (no DOM dependency)
- SX data format with eval-hs bridge and run-hs-fixture runner
- 24 suites covering expressions, comparisons, coercion, logic, etc.

Parser extensions (parser.sx):
- mod as infix arithmetic operator
- English comparison phrases (is less than, is greater than or equal to)
- is a/an Type typecheck syntax
- === / !== strict equality operators
- I as me synonym, am as is for comparisons
- does not exist/match/contain postfix
- some/every ... with quantifier expressions
- undefined keyword → nil

Compiler updates (compiler.sx):
- + emits hs-add (type-dispatching: string concat or numeric add)
- no emits hs-falsy? (HS truthiness: empty string is falsy)
- matches? emits hs-matches? (string regex in non-DOM context)
- New cases: not-in?, in?, type-check, strict-eq, some, every

Runtime additions (runtime.sx):
- hs-coerce: Int/Integer truncation via floor
- hs-add: string concat when either operand is string
- hs-falsy?: HS-compatible truthiness (nil, false, "" are falsy)
- hs-matches?: string pattern matching
- hs-type-check/hs-type-check!: lenient/strict type checking
- hs-strict-eq: type + value equality

Tokenizer (tokenizer.sx):
- Added keywords: I, am, does, some, mod, equal, equals, really,
  include, includes, contain, undefined, exist

Scorecard: 47/112 test groups passing. 0 non-HS regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 18:53:50 +00:00
parent 71d1ac9ce4
commit 2278443182
6 changed files with 1250 additions and 70 deletions

View File

@@ -57,10 +57,10 @@
(deftest
"addition passes through"
(let
((sx (hs-to-sx-from-source "set x to 1 + 2")))
((sx (hs-to-sx-from-source "1 + 2")))
(let
((val (nth sx 2)))
(assert= (quote +) (first val))
((val (first sx)))
(assert= (quote hs-add) (first val))
(assert= 1 (nth val 1))
(assert= 2 (nth val 2)))))
(deftest
@@ -287,4 +287,61 @@
"decrement attribute"
(let
((sx (hs-to-sx-from-source "decrement @count")))
(assert= (quote dom-set-attr) (first sx)))))
(assert= (quote dom-set-attr) (first sx)))))
(defsuite
"hs-live-demo-toggle"
(deftest
"toggle class on me compiles to single hs-on"
(let
((sx (hs-to-sx-from-source "on click toggle .bg-violet-600 on me then toggle .text-white on me")))
(assert= (quote hs-on) (first sx))
(assert= "click" (nth sx 2))
(let
((body (nth (nth sx 3) 2)))
(assert= (quote do) (first body))
(assert= 2 (len (rest body))))))
(deftest
"bounce: then chains wait and remove in same handler"
(let
((sx (hs-to-sx-from-source "on click add .animate-bounce to me then wait 1s then remove .animate-bounce from me")))
(assert= (quote hs-on) (first sx))
(assert= "click" (nth sx 2))
(let
((body (nth (nth sx 3) 2)))
(assert= (quote do) (first body))
(assert= 3 (len (rest body)))
(assert= (quote hs-wait) (first (nth body 2)))
(assert= 1000 (nth (nth body 2) 1)))))
(deftest
"count clicks: then chains increment and set in same handler"
(let
((sx (hs-to-sx-from-source "on click increment @data-count on me then set #click-counter's innerHTML to my @data-count")))
(assert= (quote hs-on) (first sx))
(assert= "click" (nth sx 2))
(let
((body (nth (nth sx 3) 2)))
(assert= (quote do) (first body))
(assert= 2 (len (rest body)))))))
(defsuite
"hs-wait-suspension"
(deftest
"wait in then chain keeps hs-wait (platform handles suspension)"
(let
((sx (hs-to-sx-from-source "on click add .bounce to me then wait 1s then remove .bounce from me")))
(assert= (quote hs-on) (first sx))
(let
((body (nth (nth sx 3) 2)))
(assert= (quote do) (first body))
(assert= 3 (len (rest body)))
(assert= (quote hs-wait) (first (nth body 2)))
(assert= 1000 (nth (nth body 2) 1)))))
(deftest
"wait preserves ms value in handler"
(let
((sx (hs-to-sx-from-source "on click add .a then wait 2s then add .b")))
(let
((body (nth (nth sx 3) 2)))
(assert= (quote hs-wait) (first (nth body 2)))
(assert= 2000 (nth (nth body 2) 1))))))