Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 45s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
182 lines
8.0 KiB
Plaintext
182 lines
8.0 KiB
Plaintext
;; infer.sx tests — Algorithm W: literals, vars, lambdas, application, let,
|
|
;; if, operators, tuples, lists, let-polymorphism.
|
|
|
|
(define hk-t (fn (src expected)
|
|
(hk-test (str "infer: " src) (hk-infer-type src) expected)))
|
|
|
|
;; ─── Literals ────────────────────────────────────────────────────────────────
|
|
(hk-t "1" "Int")
|
|
(hk-t "3.14" "Float")
|
|
(hk-t "\"hello\"" "String")
|
|
(hk-t "'x'" "Char")
|
|
(hk-t "True" "Bool")
|
|
(hk-t "False" "Bool")
|
|
|
|
;; ─── Arithmetic and boolean operators ────────────────────────────────────────
|
|
(hk-t "1 + 2" "Int")
|
|
(hk-t "3 * 4" "Int")
|
|
(hk-t "10 - 3" "Int")
|
|
(hk-t "True && False" "Bool")
|
|
(hk-t "True || False" "Bool")
|
|
(hk-t "not True" "Bool")
|
|
(hk-t "1 == 1" "Bool")
|
|
(hk-t "1 < 2" "Bool")
|
|
|
|
;; ─── Lambda ───────────────────────────────────────────────────────────────────
|
|
;; \x -> x (identity) should get t1 -> t1
|
|
(hk-test "infer: identity lambda" (hk-infer-type "\\x -> x") "t1 -> t1")
|
|
|
|
;; \x -> x + 1 : Int -> Int
|
|
(hk-test "infer: lambda add" (hk-infer-type "\\x -> x + 1") "Int -> Int")
|
|
|
|
;; \x -> not x : Bool -> Bool
|
|
(hk-test "infer: lambda not" (hk-infer-type "\\x -> not x") "Bool -> Bool")
|
|
|
|
;; \x y -> x + y : Int -> Int -> Int
|
|
(hk-test "infer: two-arg lambda" (hk-infer-type "\\x -> \\y -> x + y") "Int -> Int -> Int")
|
|
|
|
;; ─── Application ─────────────────────────────────────────────────────────────
|
|
(hk-t "not True" "Bool")
|
|
(hk-t "negate 1" "Int")
|
|
|
|
;; ─── If-then-else ─────────────────────────────────────────────────────────────
|
|
(hk-t "if True then 1 else 2" "Int")
|
|
(hk-t "if 1 == 2 then True else False" "Bool")
|
|
|
|
;; ─── Let bindings ─────────────────────────────────────────────────────────────
|
|
;; let x = 1 in x + 2
|
|
(hk-t "let x = 1 in x + 2" "Int")
|
|
|
|
;; let f x = x + 1 in f 5
|
|
(hk-t "let f x = x + 1 in f 5" "Int")
|
|
|
|
;; let-polymorphism: let id x = x in id 1
|
|
(hk-t "let id x = x in id 1" "Int")
|
|
|
|
;; ─── Tuples ───────────────────────────────────────────────────────────────────
|
|
(hk-t "(1, True)" "(Int, Bool)")
|
|
(hk-t "(1, 2, 3)" "(Int, Int, Int)")
|
|
|
|
;; ─── Lists ───────────────────────────────────────────────────────────────────
|
|
(hk-t "[1, 2, 3]" "[Int]")
|
|
(hk-t "[True, False]" "[Bool]")
|
|
|
|
;; ─── Polymorphic list functions ───────────────────────────────────────────────
|
|
(hk-t "length [1, 2, 3]" "Int")
|
|
(hk-t "null []" "Bool")
|
|
(hk-t "head [1, 2, 3]" "Int")
|
|
|
|
;; ─── hk-expr->brief ──────────────────────────────────────────────────────────
|
|
(hk-test "brief var" (hk-expr->brief (list "var" "x")) "x")
|
|
(hk-test "brief con" (hk-expr->brief (list "con" "Just")) "Just")
|
|
(hk-test "brief int" (hk-expr->brief (list "int" 42)) "42")
|
|
(hk-test "brief app" (hk-expr->brief (list "app" (list "var" "f") (list "var" "x"))) "(f x)")
|
|
(hk-test "brief op" (hk-expr->brief (list "op" "+" (list "int" 1) (list "int" 2))) "(1 + 2)")
|
|
(hk-test "brief lambda" (hk-expr->brief (list "lambda" (list) (list "var" "x"))) "(\\ ...)")
|
|
(hk-test "brief loc" (hk-expr->brief (list "loc" 3 7 (list "var" "x"))) "x")
|
|
|
|
;; ─── Type error messages ─────────────────────────────────────────────────────
|
|
;; Helper: catch the error and check it contains a substring.
|
|
(define hk-str-has? (fn (s sub) (>= (index-of s sub) 0)))
|
|
|
|
(define hk-te
|
|
(fn (label src sub)
|
|
(hk-test label
|
|
(guard (e (#t (hk-str-has? e sub)))
|
|
(begin (hk-infer-type src) false))
|
|
true)))
|
|
|
|
;; Unbound variable error includes the variable name.
|
|
(hk-te "error unbound name" "foo + 1" "foo")
|
|
(hk-te "error unbound unk" "unknown" "unknown")
|
|
|
|
;; Unification error mentions the conflicting types.
|
|
(hk-te "error unify int-bool-1" "1 + True" "Int")
|
|
(hk-te "error unify int-bool-2" "1 + True" "Bool")
|
|
|
|
;; ─── Loc node: passes through to inner (position decorates outer context) ────
|
|
(define hk-loc-err-msg
|
|
(fn ()
|
|
(guard (e (#t e))
|
|
(begin
|
|
(hk-reset-fresh)
|
|
(hk-w (hk-type-env0) (list "loc" 5 10 (list "var" "mystery")))
|
|
"no-error"))))
|
|
(hk-test "loc passes through to var error"
|
|
(hk-str-has? (hk-loc-err-msg) "mystery")
|
|
true)
|
|
|
|
;; ─── hk-infer-decl ───────────────────────────────────────────────────────────
|
|
;; Returns ("ok" name type) | ("err" msg)
|
|
(define hk-env0-t (hk-type-env0))
|
|
|
|
(define prog1 (hk-core "f x = x + 1"))
|
|
(define decl1 (first (nth prog1 1)))
|
|
(define res1 (hk-infer-decl hk-env0-t decl1))
|
|
(hk-test "decl result tag" (first res1) "ok")
|
|
(hk-test "decl result name" (nth res1 1) "f")
|
|
(hk-test "decl result type" (nth res1 2) "Int -> Int")
|
|
|
|
;; Error decl: result is ("err" "in 'g': ...")
|
|
(define prog2 (hk-core "g x = x + True"))
|
|
(define decl2 (first (nth prog2 1)))
|
|
(define res2 (hk-infer-decl hk-env0-t decl2))
|
|
(hk-test "decl error tag" (first res2) "err")
|
|
(hk-test "decl error has g" (hk-str-has? (nth res2 1) "g") true)
|
|
(hk-test "decl error has msg" (hk-str-has? (nth res2 1) "unify") true)
|
|
|
|
;; ─── hk-infer-prog ───────────────────────────────────────────────────────────
|
|
;; Returns list of ("ok"/"err" ...) tagged results.
|
|
(define prog3 (hk-core "double x = x + x\ntwice f x = f (f x)"))
|
|
(define results3 (hk-infer-prog prog3 hk-env0-t))
|
|
;; results3 = (("ok" "double" "Int -> Int") ("ok" "twice" "..."))
|
|
(hk-test "infer-prog count" (len results3) 2)
|
|
(hk-test "infer-prog double" (nth (nth results3 0) 2) "Int -> Int")
|
|
(hk-test "infer-prog twice" (nth (nth results3 1) 2) "(t3 -> t3) -> t3 -> t3")
|
|
|
|
(hk-t "let id x = x in id 1" "Int")
|
|
|
|
(hk-t "let id x = x in id True" "Bool")
|
|
|
|
(hk-t "let id x = x in (id 1, id True)" "(Int, Bool)")
|
|
|
|
(hk-t "let const x y = x in (const 1 True, const True 1)" "(Int, Bool)")
|
|
|
|
(hk-t "let f x = x in let g y = f y in (g 1, g True)" "(Int, Bool)")
|
|
|
|
(hk-t "let twice f x = f (f x) in twice (\x -> x + 1) 5" "Int")
|
|
|
|
(hk-t "not (not True)" "Bool")
|
|
|
|
(hk-t "negate (negate 1)" "Int")
|
|
|
|
(hk-t "\\x -> \\y -> x && y" "Bool -> Bool -> Bool")
|
|
|
|
(hk-t "\\x -> x == 1" "Int -> Bool")
|
|
|
|
(hk-t "let x = True in if x then 1 else 0" "Int")
|
|
|
|
(hk-t "let f x = not x in f True" "Bool")
|
|
|
|
(hk-t "let f x = (x, x + 1) in f 5" "(Int, Int)")
|
|
|
|
(hk-t "let x = 1 in let y = 2 in x + y" "Int")
|
|
|
|
(hk-t "let f x = x + 1 in f (f 5)" "Int")
|
|
|
|
(hk-t "if 1 < 2 then True else False" "Bool")
|
|
|
|
(hk-t "if True then 1 + 1 else 2 + 2" "Int")
|
|
|
|
(hk-t "(1 + 2, True && False)" "(Int, Bool)")
|
|
|
|
(hk-t "(1 == 1, 2 < 3)" "(Bool, Bool)")
|
|
|
|
(hk-t "length [True, False]" "Int")
|
|
|
|
(hk-t "null [1]" "Bool")
|
|
|
|
(hk-t "[True]" "[Bool]")
|
|
|
|
{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}
|