HS: split type-check (predicate) from type-assert (:) — +5 comparisonOperator

Last commit's `hs-type-check` rewrite collapsed predicate and assertion
into one runtime fn that always raised on mismatch. That fixed `: Type`
but broke `is a Type` / `is not a Type` (which need a bool):

  null is a String       expected true,  got nil   (raised)
  null is not a String   expected false, got true  (default boolean)

Restored the split. Parser now emits `(type-assert ...)` for `:` and
keeps `(type-check ...)` for `is a` / `is not a`. Runtime adds:
- `hs-type-check`        — predicate, never raises (nil passes)
- `hs-type-check-strict` — predicate, false on nil
- `hs-type-assert`       — value or raises
- `hs-type-assert-strict` — value or raises (also raises on nil)
Compiler maps `type-assert` / `type-assert-strict` to the new runtime fns.

comparisonOperator 74/83 → 79/83 (+5: `is a/an`, `is not a/an` four tests
plus a fifth that depended on them). typecheck stays 2/5 (no regression).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 11:37:12 +00:00
parent 24dbc966e9
commit e4773ec336
6 changed files with 98 additions and 38 deletions

View File

@@ -1703,6 +1703,16 @@
(quote hs-type-check-strict)
(hs-to-sx (nth ast 1))
(nth ast 2)))
((= head (quote type-assert))
(list
(quote hs-type-assert)
(hs-to-sx (nth ast 1))
(nth ast 2)))
((= head (quote type-assert-strict))
(list
(quote hs-type-assert-strict)
(hs-to-sx (nth ast 1))
(nth ast 2)))
((= head (quote strict-eq))
(list
(quote hs-strict-eq)

View File

@@ -678,8 +678,8 @@
(when strict (adv!))
(if
strict
(list (quote type-check-strict) left type-name)
(list (quote type-check) left type-name)))))))
(list (quote type-assert-strict) left type-name)
(list (quote type-assert) left type-name)))))))
((and (= typ "keyword") (= val "of"))
(do
(adv!)

View File

@@ -769,28 +769,48 @@
(dom-set-style target prop (str to-val))
(when duration (hs-settle target))))
(define
hs-type-check
(fn
(value type-name)
(if
(nil? value)
value
(let
((matches (cond ((= type-name "Number") (number? value)) ((= type-name "String") (string? value)) ((= type-name "Boolean") (or (= value true) (= value false))) ((= type-name "Array") (list? value)) ((= type-name "Object") (dict? value)) ((= type-name "Element") (= (host-typeof value) "element")) ((= type-name "Node") (or (= (host-typeof value) "element") (= (host-typeof value) "text"))) (true (= (host-typeof value) (downcase type-name))))))
(if
matches
value
(raise (str "Typecheck failed! expected " type-name)))))))
(begin
(define
hs-type-check
(fn
(value type-name)
(if
(nil? value)
true
(cond
((= type-name "Number") (number? value))
((= type-name "String") (string? value))
((= type-name "Boolean") (or (= value true) (= value false)))
((= type-name "Array") (list? value))
((= type-name "Object") (dict? value))
((= type-name "Element") (= (host-typeof value) "element"))
((= type-name "Node")
(or
(= (host-typeof value) "element")
(= (host-typeof value) "text")))
(true (= (host-typeof value) (downcase type-name)))))))
(define
hs-type-assert
(fn
(value type-name)
(if
(hs-type-check value type-name)
value
(raise (str "Typecheck failed! expected " type-name)))))
(define
hs-type-assert-strict
(fn
(value type-name)
(if
(nil? value)
(raise (str "Typecheck failed! expected " type-name " but got nil"))
(hs-type-assert value type-name)))))
(define
hs-type-check-strict
(fn
(value type-name)
(if
(nil? value)
(raise (str "Typecheck failed! expected " type-name " but got nil"))
(hs-type-check value type-name))))
(if (nil? value) false (hs-type-check value type-name))))
(define
hs-strict-eq

View File

@@ -1703,6 +1703,16 @@
(quote hs-type-check-strict)
(hs-to-sx (nth ast 1))
(nth ast 2)))
((= head (quote type-assert))
(list
(quote hs-type-assert)
(hs-to-sx (nth ast 1))
(nth ast 2)))
((= head (quote type-assert-strict))
(list
(quote hs-type-assert-strict)
(hs-to-sx (nth ast 1))
(nth ast 2)))
((= head (quote strict-eq))
(list
(quote hs-strict-eq)

View File

@@ -678,8 +678,8 @@
(when strict (adv!))
(if
strict
(list (quote type-check-strict) left type-name)
(list (quote type-check) left type-name)))))))
(list (quote type-assert-strict) left type-name)
(list (quote type-assert) left type-name)))))))
((and (= typ "keyword") (= val "of"))
(do
(adv!)

View File

@@ -769,28 +769,48 @@
(dom-set-style target prop (str to-val))
(when duration (hs-settle target))))
(define
hs-type-check
(fn
(value type-name)
(if
(nil? value)
value
(let
((matches (cond ((= type-name "Number") (number? value)) ((= type-name "String") (string? value)) ((= type-name "Boolean") (or (= value true) (= value false))) ((= type-name "Array") (list? value)) ((= type-name "Object") (dict? value)) ((= type-name "Element") (= (host-typeof value) "element")) ((= type-name "Node") (or (= (host-typeof value) "element") (= (host-typeof value) "text"))) (true (= (host-typeof value) (downcase type-name))))))
(if
matches
value
(raise (str "Typecheck failed! expected " type-name)))))))
(begin
(define
hs-type-check
(fn
(value type-name)
(if
(nil? value)
true
(cond
((= type-name "Number") (number? value))
((= type-name "String") (string? value))
((= type-name "Boolean") (or (= value true) (= value false)))
((= type-name "Array") (list? value))
((= type-name "Object") (dict? value))
((= type-name "Element") (= (host-typeof value) "element"))
((= type-name "Node")
(or
(= (host-typeof value) "element")
(= (host-typeof value) "text")))
(true (= (host-typeof value) (downcase type-name)))))))
(define
hs-type-assert
(fn
(value type-name)
(if
(hs-type-check value type-name)
value
(raise (str "Typecheck failed! expected " type-name)))))
(define
hs-type-assert-strict
(fn
(value type-name)
(if
(nil? value)
(raise (str "Typecheck failed! expected " type-name " but got nil"))
(hs-type-assert value type-name)))))
(define
hs-type-check-strict
(fn
(value type-name)
(if
(nil? value)
(raise (str "Typecheck failed! expected " type-name " but got nil"))
(hs-type-check value type-name))))
(if (nil? value) false (hs-type-check value type-name))))
(define
hs-strict-eq