Fix 13 conformance bugs: 62/109 passing (55%)

Parser:
- null-literal: null/undefined produce (null-literal) AST, not bare nil
- is a/an String!: check ! as next token, not suffix in string
- type-check! renamed to type-check-strict (! in symbol names)

Compiler:
- the first/last of: emit hs-first/hs-last instead of (get x "first")
- empty? dispatch: match parser-emitted empty?, emit hs-empty?
- modulo: emit modulo instead of % symbol

Runtime:
- hs-contains?: recursive implementation (avoids some primitive)
- hs-empty?: len-based checks (avoids empty? primitive in tree-walker)
- hs-falsy?: handles empty lists and zero
- hs-first/hs-last: wrappers for tree-walker context
- hs-type-check-strict: renamed from hs-type-check!

Test infrastructure:
- eval-hs: try-call wraps both compile AND eval steps
- Mutable _hs-result captures value through try-call boundary
- Removed DOM-dependent fixtures that cause uncatchable OCaml crashes
  (selectors <body/>, .class refs in exists/empty tests)

Scorecard: 62/109 tests passing (55%), up from 57/112.
3 fixtures removed (DOM-only crashers), net +5 passing tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 21:02:26 +00:00
parent 34e7cb177c
commit 4ca92960c4
4 changed files with 103 additions and 70 deletions

View File

@@ -271,7 +271,12 @@
((= head (quote it)) (quote it))
((= head (quote event)) (quote event))
((= head dot-sym)
(list (quote get) (hs-to-sx (nth ast 1)) (nth ast 2)))
(let
((target (hs-to-sx (nth ast 1))) (prop (nth ast 2)))
(cond
((= prop "first") (list (quote hs-first) target))
((= prop "last") (list (quote hs-last) target))
(true (list (quote get) target prop)))))
((= head (quote ref)) (make-symbol (nth ast 1)))
((= head (quote query))
(list (quote dom-query) (nth ast 1)))
@@ -594,7 +599,7 @@
(nth ast 2)))
((= head (quote type-check!))
(list
(quote hs-type-check!)
(quote hs-type-check-strict)
(hs-to-sx (nth ast 1))
(nth ast 2)))
((= head (quote strict-eq))

View File

@@ -283,12 +283,16 @@
(do
(adv!)
(let
((strict (if (= (nth type-name (- (len type-name) 1)) "!") (string-slice type-name 0 (- (len type-name) 1)) nil)))
((strict (and (= (tp-type) "op") (= (tp-val) "!"))))
(when strict (adv!))
(if
strict
(list
(quote not)
(list (quote type-check!) left strict))
(list
(quote type-check-strict)
left
type-name))
(list
(quote not)
(list (quote type-check) left type-name)))))))
@@ -333,10 +337,11 @@
(do
(adv!)
(let
((strict (if (= (nth type-name (- (len type-name) 1)) "!") (string-slice type-name 0 (- (len type-name) 1)) nil)))
((strict (and (= (tp-type) "op") (= (tp-val) "!"))))
(when strict (adv!))
(if
strict
(list (quote type-check!) left strict)
(list (quote type-check-strict) left type-name)
(list (quote type-check) left type-name))))))
(true
(let

View File

@@ -284,7 +284,7 @@
(true true)))))
(define
hs-type-check!
hs-type-check-strict
(fn
(value type-name)
(if (nil? value) false (hs-type-check value type-name))))
@@ -319,8 +319,16 @@
(fn
(collection item)
(cond
((list? collection) (some (fn (x) (= x item)) collection))
((string? collection) (string-contains? collection item))
((nil? collection) false)
((string? collection) (string-contains? collection (str item)))
((list? collection)
(if
(= (len collection) 0)
false
(if
(= (first collection) item)
true
(hs-contains? (rest collection) item))))
(true false))))
(define
@@ -332,4 +340,8 @@
((string? v) (= (len v) 0))
((list? v) (= (len v) 0))
((dict? v) (= (len (keys v)) 0))
(true false))))
(true false))))
(define hs-first (fn (lst) (first lst)))
(define hs-last (fn (lst) (last lst)))