;; Strict evaluator tests. Each test parses, desugars, and evaluates ;; either an expression (hk-eval-expr-source) or a full program ;; (hk-eval-program → look up a named value). (define hk-prog-val (fn (src name) (hk-deep-force (get (hk-eval-program (hk-core src)) name)))) ;; ── Literals ── (hk-test "int literal" (hk-eval-expr-source "42") 42) (hk-test "float literal" (hk-eval-expr-source "3.14") 3.14) (hk-test "string literal" (hk-eval-expr-source "\"hi\"") "hi") (hk-test "char literal" (hk-eval-expr-source "'a'") "a") (hk-test "negative literal" (hk-eval-expr-source "- 5") -5) ;; ── Arithmetic ── (hk-test "addition" (hk-eval-expr-source "1 + 2") 3) (hk-test "precedence" (hk-eval-expr-source "1 + 2 * 3") 7) (hk-test "parens override precedence" (hk-eval-expr-source "(1 + 2) * 3") 9) (hk-test "subtraction left-assoc" (hk-eval-expr-source "10 - 3 - 2") 5) ;; ── Comparison + Bool ── (hk-test "less than is True" (hk-eval-expr-source "3 < 5") (list "True")) (hk-test "equality is False" (hk-eval-expr-source "1 == 2") (list "False")) (hk-test "&& shortcuts" (hk-eval-expr-source "(1 == 1) && (2 == 2)") (list "True")) ;; ── if / otherwise ── (hk-test "if True" (hk-eval-expr-source "if True then 1 else 2") 1) (hk-test "if comparison branch" (hk-eval-expr-source "if 5 > 3 then \"yes\" else \"no\"") "yes") (hk-test "otherwise is True" (hk-eval-expr-source "otherwise") (list "True")) ;; ── let ── (hk-test "let single binding" (hk-eval-expr-source "let x = 5 in x + 1") 6) (hk-test "let two bindings" (hk-eval-expr-source "let x = 1; y = 2 in x + y") 3) (hk-test "let recursive: factorial 5" (hk-eval-expr-source "let f n = if n == 0 then 1 else n * f (n - 1) in f 5") 120) ;; ── Lambdas ── (hk-test "lambda apply" (hk-eval-expr-source "(\\x -> x + 1) 5") 6) (hk-test "lambda multi-arg" (hk-eval-expr-source "(\\x y -> x * y) 3 4") 12) (hk-test "lambda with constructor pattern" (hk-eval-expr-source "(\\(Just x) -> x + 1) (Just 7)") 8) ;; ── Constructors ── (hk-test "0-arity constructor" (hk-eval-expr-source "Nothing") (list "Nothing")) (hk-test "1-arity constructor applied" (hk-eval-expr-source "Just 5") (list "Just" 5)) (hk-test "True / False as bools" (hk-eval-expr-source "True") (list "True")) ;; ── case ── (hk-test "case Just" (hk-eval-expr-source "case Just 7 of Just x -> x ; Nothing -> 0") 7) (hk-test "case Nothing" (hk-eval-expr-source "case Nothing of Just x -> x ; Nothing -> 99") 99) (hk-test "case literal pattern" (hk-eval-expr-source "case 0 of 0 -> \"zero\" ; n -> \"other\"") "zero") (hk-test "case tuple" (hk-eval-expr-source "case (1, 2) of (a, b) -> a + b") 3) (hk-test "case wildcard fallback" (hk-eval-expr-source "case 5 of 0 -> \"z\" ; _ -> \"nz\"") "nz") ;; ── List literals + cons ── (hk-test "list literal as cons spine" (hk-eval-expr-source "[1, 2, 3]") (list ":" 1 (list ":" 2 (list ":" 3 (list "[]"))))) (hk-test "empty list literal" (hk-eval-expr-source "[]") (list "[]")) (hk-test "cons via :" (hk-eval-expr-source "1 : []") (list ":" 1 (list "[]"))) (hk-test "++ concatenates lists" (hk-eval-expr-source "[1, 2] ++ [3]") (list ":" 1 (list ":" 2 (list ":" 3 (list "[]"))))) ;; ── Tuples ── (hk-test "2-tuple" (hk-eval-expr-source "(1, 2)") (list "Tuple" 1 2)) (hk-test "3-tuple" (hk-eval-expr-source "(\"a\", 5, True)") (list "Tuple" "a" 5 (list "True"))) ;; ── Sections ── (hk-test "right section (+ 1) applied" (hk-eval-expr-source "(+ 1) 5") 6) (hk-test "left section (10 -) applied" (hk-eval-expr-source "(10 -) 4") 6) ;; ── Multi-clause top-level functions ── (hk-test "multi-clause: factorial" (hk-prog-val "fact 0 = 1\nfact n = n * fact (n - 1)\nresult = fact 6" "result") 720) (hk-test "multi-clause: list length via cons pattern" (hk-prog-val "len [] = 0\nlen (x:xs) = 1 + len xs\nresult = len [10, 20, 30, 40]" "result") 4) (hk-test "multi-clause: Maybe handler" (hk-prog-val "fromMaybe d Nothing = d\nfromMaybe _ (Just x) = x\nresult = fromMaybe 0 (Just 9)" "result") 9) (hk-test "multi-clause: Maybe with default" (hk-prog-val "fromMaybe d Nothing = d\nfromMaybe _ (Just x) = x\nresult = fromMaybe 0 Nothing" "result") 0) ;; ── User-defined data and matching ── (hk-test "custom data with pattern match" (hk-prog-val "data Color = Red | Green | Blue\nname Red = \"red\"\nname Green = \"green\"\nname Blue = \"blue\"\nresult = name Green" "result") "green") (hk-test "custom binary tree height" (hk-prog-val "data Tree = Leaf | Node Tree Tree\nh Leaf = 0\nh (Node l r) = 1 + max (h l) (h r)\nmax a b = if a > b then a else b\nresult = h (Node (Node Leaf Leaf) Leaf)" "result") 2) ;; ── Currying ── (hk-test "partial application" (hk-prog-val "add x y = x + y\nadd5 = add 5\nresult = add5 7" "result") 12) ;; ── Higher-order ── (hk-test "higher-order: function as arg" (hk-prog-val "twice f x = f (f x)\ninc x = x + 1\nresult = twice inc 10" "result") 12) ;; ── Error built-in ── (hk-test "error short-circuits via if" (hk-eval-expr-source "if True then 1 else error \"unreachable\"") 1) ;; ── Laziness: app args evaluate only when forced ── (hk-test "second arg never forced" (hk-eval-expr-source "(\\x y -> x) 1 (error \"never\")") 1) (hk-test "first arg never forced" (hk-eval-expr-source "(\\x y -> y) (error \"never\") 99") 99) (hk-test "constructor argument is lazy under wildcard pattern" (hk-eval-expr-source "case Just (error \"deeply\") of Just _ -> 7 ; Nothing -> 0") 7) (hk-test "lazy: const drops its second argument" (hk-prog-val "const x y = x\nresult = const 5 (error \"boom\")" "result") 5) (hk-test "lazy: head ignores tail" (hk-prog-val "myHead (x:_) = x\nresult = myHead (1 : (error \"tail\") : [])" "result") 1) (hk-test "lazy: Just on undefined evaluates only on force" (hk-prog-val "wrapped = Just (error \"oh no\")\nresult = case wrapped of Just _ -> True ; Nothing -> False" "result") (list "True")) ;; ── not / id built-ins ── (hk-test "not True" (hk-eval-expr-source "not True") (list "False")) (hk-test "not False" (hk-eval-expr-source "not False") (list "True")) (hk-test "id" (hk-eval-expr-source "id 42") 42) {:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}