HS: extend parser/runtime + new node test runner; ignore test-results/

- Parser: `--` line comments, `|` op, `result` → `the-result`, query-scoped
  `<sel> in <expr>`, `is a/an <type>` predicate, multi-`as` chaining with `|`,
  `match`/`precede` keyword aliases, `[attr]` add/toggle, between attr forms
- Runtime: per-element listener registry + hs-deactivate!, attr toggle
  variants, set-inner-html boots subtree, hs-append polymorphic on
  string/list/element, default? / array-set! / query-all-in / list-set
  via take+drop, hs-script idempotence guard
- Integration: skip reserved (me/it/event/you/yourself) when collecting vars
- Tokenizer: emit `--` comments and `|` op
- Test framework + conformance runner updates; new tests/hs-run-filtered.js
  (single-process Node runner using OCaml VM step-limit to bound infinite
  loops); generate-sx-conformance-dev.py improvements
- mcp_tree.ml + run_tests.ml: harness extensions
- .gitignore: top-level test-results/ (Playwright artifacts)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 07:11:07 +00:00
parent b2ae80fb21
commit 0515295317
20 changed files with 15224 additions and 8120 deletions

View File

@@ -18,11 +18,18 @@
;; 1. Test framework macros
;; --------------------------------------------------------------------------
(defmacro deftest (name &rest body)
`(let ((result (try-call (fn () ,@body))))
(if (get result "ok")
(report-pass ,name)
(report-fail ,name (get result "error")))))
(defmacro
deftest
(name &rest body)
(quasiquote
(when
(test-allowed? (unquote name))
(let
((result (try-call (fn () (splice-unquote body)))))
(if
(get result "ok")
(report-pass (unquote name))
(report-fail (unquote name) (get result "error")))))))
(defmacro defsuite (name &rest items)
`(do (push-suite ,name)

View File

@@ -4,7 +4,9 @@
"put into #id compiled"
(let
((sx (hs-to-sx-from-source "on click put \"foo\" into #d1")))
(assert= (serialize sx) "SHOW")))
(assert=
(serialize sx)
"(hs-on me \"click\" (fn (event) (hs-set-inner-html! (hs-query-first \"#d1\") \"foo\")))")))
(deftest
"put into #id works"
(let

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,7 @@
"set attribute"
(let
((sx (hs-to-sx-from-source "set @title to 'hello'")))
(assert= (quote dom-set-attr) (first sx))
(assert= (quote hs-set-attr!) (first sx))
(assert= "title" (nth sx 2))
(assert= "hello" (nth sx 3))))
(deftest
@@ -284,12 +284,16 @@
"increment attribute"
(let
((sx (hs-to-sx-from-source "increment @count")))
(assert= (quote dom-set-attr) (first sx))))
(assert= (quote let) (first sx))
(assert= (quote do) (first (nth sx 2)))
(assert= (quote dom-set-attr) (first (nth (nth sx 2) 1)))))
(deftest
"decrement attribute"
(let
((sx (hs-to-sx-from-source "decrement @count")))
(assert= (quote dom-set-attr) (first sx)))))
(assert= (quote let) (first sx))
(assert= (quote do) (first (nth sx 2)))
(assert= (quote dom-set-attr) (first (nth (nth sx 2) 1))))))
(defsuite
"hs-live-demo-toggle"

View File

@@ -59,44 +59,73 @@
)
;; ── collectionExpressions (12 tests) ──
(defsuite "hs-dev-collectionExpressions"
(deftest "filters an array by condition"
(let ((result (eval-hs "set arr to [{name: \"a\", active: true}, {name: \"b\", active: false}, {name: \"c\", active: true}] then return arr where its active")))
(defsuite
"hs-dev-collectionExpressions"
(deftest
"filters an array by condition"
(let
((result (eval-hs "set arr to [{name: \"a\", active: true}, {name: \"b\", active: false}, {name: \"c\", active: true}] then return arr where its active")))
(assert= (list "a" "c") (map (fn (x) (get x "name")) result))))
(deftest "filters with comparison"
(assert= (list 4 5) (eval-hs "set arr to [1, 2, 3, 4, 5] then return arr where it > 3"))
)
(deftest "sorts by a property"
(let ((result (eval-hs "set arr to [{name: \"Charlie\"}, {name: \"Alice\"}, {name: \"Bob\"}] then return arr sorted by its name")))
(assert= (list "Alice" "Bob" "Charlie") (map (fn (x) (get x "name")) result))))
(deftest "sorts descending"
(assert= (list 3 2 1) (eval-hs "set arr to [3, 1, 2] then return arr sorted by it descending"))
)
(deftest "sorts numbers by a computed key"
(let ((result (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")))
(deftest
"filters with comparison"
(assert=
(list 4 5)
(eval-hs "set arr to [1, 2, 3, 4, 5] then return arr where it > 3")))
(deftest
"sorts by a property"
(let
((result (eval-hs "set arr to [{name: \"Charlie\"}, {name: \"Alice\"}, {name: \"Bob\"}] then return arr sorted by its name")))
(assert=
(list "Alice" "Bob" "Charlie")
(map (fn (x) (get x "name")) result))))
(deftest
"sorts descending"
(assert=
(list 3 2 1)
(eval-hs "set arr to [3, 1, 2] then return arr sorted by it descending")))
(deftest
"sorts numbers by a computed key"
(let
((result (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")))
(assert= (list "a" "c" "b") (map (fn (x) (get x "name")) result))))
(deftest "maps to a property"
(assert= (list "Alice" "Bob") (eval-hs "set arr to [{name: \"Alice\"}, {name: \"Bob\"}] then return arr mapped to its name"))
)
(deftest "maps with an expression"
(assert= (list 2 4 6) (eval-hs "set arr to [1, 2, 3] then return arr mapped to (it * 2)"))
)
(deftest "where then mapped to"
(assert= (list "Alice" "Charlie") (eval-hs "set arr to [{name: \"Alice\", active: true}, {name: \"Bob\", active: false}, {name: \"Charlie\", active: true}] then return arr where its active mapped to its name"))
)
(deftest "sorted by then mapped to"
(assert= (list "Alice" "Charlie") (eval-hs "set arr to [{name: \"Charlie\", age: 30}, {name: \"Alice\", age: 20}] then return arr sorted by its age mapped to its name"))
)
(deftest "where then sorted by then mapped to"
(assert= (list "Bob" "Charlie") (eval-hs "set arr to [{name: \"Charlie\", active: true, age: 30}, {name: \"Alice\", active: false, age: 20}, {name: \"Bob\", active: true, age: 25}] then return arr where its active sorted by its age mapped to its name"))
)
(deftest "the result inside where refers to previous command result, not current element"
(assert= (list 4 5) (eval-hs "get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result"))
)
(deftest "where binds after property access"
(assert= (list 3 4) (eval-hs "obj.items where it > 2"))
)
)
(deftest
"maps to a property"
(assert=
(list "Alice" "Bob")
(eval-hs
"set arr to [{name: \"Alice\"}, {name: \"Bob\"}] then return arr mapped to its name")))
(deftest
"maps with an expression"
(assert=
(list 2 4 6)
(eval-hs "set arr to [1, 2, 3] then return arr mapped to (it * 2)")))
(deftest
"where then mapped to"
(assert=
(list "Alice" "Charlie")
(eval-hs
"set arr to [{name: \"Alice\", active: true}, {name: \"Bob\", active: false}, {name: \"Charlie\", active: true}] then return arr where its active mapped to its name")))
(deftest
"sorted by then mapped to"
(assert=
(list "Alice" "Charlie")
(eval-hs
"set arr to [{name: \"Charlie\", age: 30}, {name: \"Alice\", age: 20}] then return arr sorted by its age mapped to its name")))
(deftest
"where then sorted by then mapped to"
(assert=
(list "Bob" "Charlie")
(eval-hs
"set arr to [{name: \"Charlie\", active: true, age: 30}, {name: \"Alice\", active: false, age: 20}, {name: \"Bob\", active: true, age: 25}] then return arr where its active sorted by its age mapped to its name")))
(deftest
"the result inside where refers to previous command result, not current element"
(assert=
(list 4 5)
(eval-hs
"get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result")))
(deftest
"where binds after property access"
(assert= (list 3 4) (eval-hs "obj.items where it > 2" {:locals {:obj {:items (list 1 2 3 4)}}}))))
;; ── splitJoin (7 tests) ──
(defsuite "hs-dev-splitJoin"

View File

@@ -302,7 +302,7 @@
(let
((ast (hs-compile "if result is empty add .hidden end")))
(assert= (quote if) (first ast))
(assert= (list (quote empty?) (list (quote it))) (nth ast 1))))
(assert= (list (quote empty?) (quote the-result)) (nth ast 1))))
(deftest
"comparison is not"
(let
@@ -375,7 +375,7 @@
(let
((ast (hs-compile "call alert(\"hello\")")))
(assert= (quote call) (first ast))
(assert= "alert" (nth ast 1))
(assert= (list (quote ref) "alert") (nth ast 1))
(assert= "hello" (nth ast 2)))))
;; ── Full expressions (matching tokenizer conformance) ─────────────
@@ -462,7 +462,7 @@
"the as article skip"
(let
((ast (hs-compile "set the result to 5")))
(let ((tgt (nth ast 1))) (assert= (quote it) (first tgt))))))
(let ((tgt (nth ast 1))) (assert= (quote the-result) tgt)))))
(defsuite
"hs-parse-as-conversion"