Fix hyperscript conformance: 54/112 passing (was 31/81 baseline)

Runtime visibility fix:
- eval-hs now injects runtime helpers (hs-add, hs-falsy?, hs-strict-eq,
  hs-type-check, hs-matches?, hs-contains?, hs-coerce) via outer let
  binding so the tree-walker evaluator can resolve them

Parser fixes:
- null/undefined: return (null-literal) AST node instead of bare nil
  (nil was indistinguishable from "no parse result" sentinel)
- === / !== tokenized as single 3-char operators
- mod operator: emit (modulo) instead of (%) — modulo is a real primitive

Compiler fixes:
- null-literal → nil
- % → modulo
- contains? → hs-contains? (avoids tree-walker primitive arity conflict)

Runtime additions:
- hs-contains?: wraps list membership + string containment

Tokenizer:
- Added keywords: a, an (removed — broke all tokenization), exist
- Triple operators: === and !== now tokenized correctly

Scorecard: 54/112 test groups passing, +23 from baseline.
Unlocked: really-equals, english comparisons, is-in, null is empty,
null exists, type checks, strict equality, mod.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 19:46:42 +00:00
parent 2278443182
commit 1f7f47b4c1
6 changed files with 471 additions and 779 deletions

View File

@@ -266,6 +266,7 @@
(let (let
((head (first ast))) ((head (first ast)))
(cond (cond
((= head (quote null-literal)) nil)
((= head (quote me)) (quote me)) ((= head (quote me)) (quote me))
((= head (quote it)) (quote it)) ((= head (quote it)) (quote it))
((= head (quote event)) (quote event)) ((= head (quote event)) (quote event))
@@ -328,7 +329,7 @@
(hs-to-sx (nth ast 2)))) (hs-to-sx (nth ast 2))))
((= head pct-sym) ((= head pct-sym)
(list (list
pct-sym (quote modulo)
(hs-to-sx (nth ast 1)) (hs-to-sx (nth ast 1))
(hs-to-sx (nth ast 2)))) (hs-to-sx (nth ast 2))))
((= head (quote empty?)) ((= head (quote empty?))
@@ -342,16 +343,16 @@
(quote hs-matches?) (quote hs-matches?)
(hs-to-sx (nth ast 1)) (hs-to-sx (nth ast 1))
(hs-to-sx (nth ast 2)))) (hs-to-sx (nth ast 2))))
((= head (quote contains?)) ((= head (quote hs-contains?))
(list (list
(quote contains?) (quote hs-contains?)
(hs-to-sx (nth ast 1)) (hs-to-sx (nth ast 1))
(hs-to-sx (nth ast 2)))) (hs-to-sx (nth ast 2))))
((= head (quote as)) ((= head (quote as))
(list (quote hs-coerce) (hs-to-sx (nth ast 1)) (nth ast 2))) (list (quote hs-coerce) (hs-to-sx (nth ast 1)) (nth ast 2)))
((= head (quote in?)) ((= head (quote in?))
(list (list
(quote contains?) (quote hs-contains?)
(hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1)))) (hs-to-sx (nth ast 1))))
((= head (quote of)) ((= head (quote of))
@@ -575,12 +576,12 @@
(list (list
(quote not) (quote not)
(list (list
(quote contains?) (quote hs-contains?)
(hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1))))) (hs-to-sx (nth ast 1)))))
((= head (quote in?)) ((= head (quote in?))
(list (list
(quote contains?) (quote hs-contains?)
(hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1)))) (hs-to-sx (nth ast 1))))
((= head (quote type-check)) ((= head (quote type-check))

View File

@@ -112,9 +112,9 @@
((and (= typ "keyword") (= val "true")) (do (adv!) true)) ((and (= typ "keyword") (= val "true")) (do (adv!) true))
((and (= typ "keyword") (= val "false")) (do (adv!) false)) ((and (= typ "keyword") (= val "false")) (do (adv!) false))
((and (= typ "keyword") (or (= val "null") (= val "nil"))) ((and (= typ "keyword") (or (= val "null") (= val "nil")))
(do (adv!) nil)) (do (adv!) (list (quote null-literal))))
((and (= typ "keyword") (= val "undefined")) ((and (= typ "keyword") (= val "undefined"))
(do (adv!) nil)) (do (adv!) (list (quote null-literal))))
((and (= typ "keyword") (= val "not")) ((and (= typ "keyword") (= val "not"))
(do (adv!) (list (quote not) (parse-expr)))) (do (adv!) (list (quote not) (parse-expr))))
((and (= typ "keyword") (= val "no")) ((and (= typ "keyword") (= val "no"))

View File

@@ -172,13 +172,7 @@
(n thunk) (n thunk)
(define (define
do-repeat do-repeat
(fn (fn (i) (when (< i n) (thunk) (do-repeat (+ i 1)))))
(i)
(when
(< i n)
(log (str "[hs-repeat] iteration " i " of " n))
(thunk)
(do-repeat (+ i 1)))))
(do-repeat 0))) (do-repeat 0)))
;; Repeat forever (until break — relies on exception/continuation). ;; Repeat forever (until break — relies on exception/continuation).
@@ -310,4 +304,13 @@
(if (if
(string? target) (string? target)
(if (= pattern ".*") true (string-contains? target pattern)) (if (= pattern ".*") true (string-contains? target pattern))
false))) false)))
(define
hs-contains?
(fn
(collection item)
(cond
((list? collection) (some (fn (x) (= x item)) collection))
((string? collection) (string-contains? collection item))
(true false))))

View File

@@ -477,8 +477,13 @@
(< (+ pos 1) src-len) (< (+ pos 1) src-len)
(= (hs-peek 1) "=")) (= (hs-peek 1) "="))
(do (do
(hs-emit! "op" (str ch "=") start) (if
(hs-advance! 2) (and
(or (= ch "=") (= ch "!"))
(< (+ pos 2) src-len)
(= (hs-peek 2) "="))
(do (hs-emit! "op" (str ch "==") start) (hs-advance! 3))
(do (hs-emit! "op" (str ch "=") start) (hs-advance! 2)))
(scan!)) (scan!))
(and (and
(= ch "'") (= ch "'")

View File

@@ -57,12 +57,10 @@
(deftest (deftest
"addition passes through" "addition passes through"
(let (let
((sx (hs-to-sx-from-source "1 + 2"))) ((val (hs-to-sx-from-source "1 + 2")))
(let (assert= (quote hs-add) (first val))
((val (first sx))) (assert= 1 (nth val 1))
(assert= (quote hs-add) (first val)) (assert= 2 (nth val 2))))
(assert= 1 (nth val 1))
(assert= 2 (nth val 2)))))
(deftest (deftest
"comparison emits correctly" "comparison emits correctly"
(let (let

File diff suppressed because it is too large Load Diff