;; Haskell expression parser tests. ;; hk-parse tokenises, runs layout, then parses. Output is an AST ;; whose head is a keyword tag (evaluates to its string name). ;; ── 1. Literals ── (hk-test "integer" (hk-parse "42") (list :int 42)) (hk-test "float" (hk-parse "3.14") (list :float 3.14)) (hk-test "string" (hk-parse "\"hi\"") (list :string "hi")) (hk-test "char" (hk-parse "'a'") (list :char "a")) ;; ── 2. Variables and constructors ── (hk-test "varid" (hk-parse "foo") (list :var "foo")) (hk-test "conid" (hk-parse "Nothing") (list :con "Nothing")) (hk-test "qvarid" (hk-parse "Data.Map.lookup") (list :var "Data.Map.lookup")) (hk-test "qconid" (hk-parse "Data.Map") (list :con "Data.Map")) ;; ── 3. Parens / unit / tuple ── (hk-test "parens strip" (hk-parse "(42)") (list :int 42)) (hk-test "unit" (hk-parse "()") (list :con "()")) (hk-test "2-tuple" (hk-parse "(1, 2)") (list :tuple (list (list :int 1) (list :int 2)))) (hk-test "3-tuple" (hk-parse "(x, y, z)") (list :tuple (list (list :var "x") (list :var "y") (list :var "z")))) ;; ── 4. Lists ── (hk-test "empty list" (hk-parse "[]") (list :list (list))) (hk-test "singleton list" (hk-parse "[1]") (list :list (list (list :int 1)))) (hk-test "list of ints" (hk-parse "[1, 2, 3]") (list :list (list (list :int 1) (list :int 2) (list :int 3)))) (hk-test "range" (hk-parse "[1..10]") (list :range (list :int 1) (list :int 10))) (hk-test "range with step" (hk-parse "[1, 3..10]") (list :range-step (list :int 1) (list :int 3) (list :int 10))) ;; ── 5. Application ── (hk-test "one-arg app" (hk-parse "f x") (list :app (list :var "f") (list :var "x"))) (hk-test "multi-arg app is left-assoc" (hk-parse "f x y z") (list :app (list :app (list :app (list :var "f") (list :var "x")) (list :var "y")) (list :var "z"))) (hk-test "app with con" (hk-parse "Just 5") (list :app (list :con "Just") (list :int 5))) ;; ── 6. Infix operators ── (hk-test "simple +" (hk-parse "1 + 2") (list :op "+" (list :int 1) (list :int 2))) (hk-test "precedence: * binds tighter than +" (hk-parse "1 + 2 * 3") (list :op "+" (list :int 1) (list :op "*" (list :int 2) (list :int 3)))) (hk-test "- is left-assoc" (hk-parse "10 - 3 - 2") (list :op "-" (list :op "-" (list :int 10) (list :int 3)) (list :int 2))) (hk-test ": is right-assoc" (hk-parse "a : b : c") (list :op ":" (list :var "a") (list :op ":" (list :var "b") (list :var "c")))) (hk-test "app binds tighter than op" (hk-parse "f x + g y") (list :op "+" (list :app (list :var "f") (list :var "x")) (list :app (list :var "g") (list :var "y")))) (hk-test "$ is lowest precedence, right-assoc" (hk-parse "f $ g x") (list :op "$" (list :var "f") (list :app (list :var "g") (list :var "x")))) ;; ── 7. Backticks (varid-as-operator) ── (hk-test "backtick operator" (hk-parse "x `mod` 3") (list :op "mod" (list :var "x") (list :int 3))) ;; ── 8. Unary negation ── (hk-test "unary -" (hk-parse "- 5") (list :neg (list :int 5))) (hk-test "unary - on application" (hk-parse "- f x") (list :neg (list :app (list :var "f") (list :var "x")))) (hk-test "- n + m → (- n) + m" (hk-parse "- 1 + 2") (list :op "+" (list :neg (list :int 1)) (list :int 2))) ;; ── 9. Lambda ── (hk-test "lambda single param" (hk-parse "\\x -> x") (list :lambda (list "x") (list :var "x"))) (hk-test "lambda multi-param" (hk-parse "\\x y -> x + y") (list :lambda (list "x" "y") (list :op "+" (list :var "x") (list :var "y")))) (hk-test "lambda body is full expression" (hk-parse "\\f -> f 1 + f 2") (list :lambda (list "f") (list :op "+" (list :app (list :var "f") (list :int 1)) (list :app (list :var "f") (list :int 2))))) ;; ── 10. if-then-else ── (hk-test "if basic" (hk-parse "if x then 1 else 2") (list :if (list :var "x") (list :int 1) (list :int 2))) (hk-test "if with infix cond" (hk-parse "if x == 0 then y else z") (list :if (list :op "==" (list :var "x") (list :int 0)) (list :var "y") (list :var "z"))) ;; ── 11. let-in ── (hk-test "let single binding" (hk-parse "let x = 1 in x") (list :let (list (list :bind "x" (list :int 1))) (list :var "x"))) (hk-test "let two bindings (multi-line)" (hk-parse "let x = 1\n y = 2\nin x + y") (list :let (list (list :bind "x" (list :int 1)) (list :bind "y" (list :int 2))) (list :op "+" (list :var "x") (list :var "y")))) (hk-test "let with explicit braces" (hk-parse "let { x = 1 ; y = 2 } in x + y") (list :let (list (list :bind "x" (list :int 1)) (list :bind "y" (list :int 2))) (list :op "+" (list :var "x") (list :var "y")))) ;; ── 12. Mixed / nesting ── (hk-test "nested application" (hk-parse "f (g x) y") (list :app (list :app (list :var "f") (list :app (list :var "g") (list :var "x"))) (list :var "y"))) (hk-test "lambda applied" (hk-parse "(\\x -> x + 1) 5") (list :app (list :lambda (list "x") (list :op "+" (list :var "x") (list :int 1))) (list :int 5))) (hk-test "lambda + if" (hk-parse "\\n -> if n == 0 then 1 else n") (list :lambda (list "n") (list :if (list :op "==" (list :var "n") (list :int 0)) (list :int 1) (list :var "n")))) ;; ── 13. Precedence corners ── (hk-test ". is right-assoc (prec 9)" (hk-parse "f . g . h") (list :op "." (list :var "f") (list :op "." (list :var "g") (list :var "h")))) (hk-test "== is non-associative (single use)" (hk-parse "x == y") (list :op "==" (list :var "x") (list :var "y"))) {:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}