;; 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 (list :p-var "x")) (list :var "x")))
(hk-test
  "lambda multi-param"
  (hk-parse "\\x y -> x + y")
  (list
    :lambda
    (list (list :p-var "x") (list :p-var "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 (list :p-var "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 (list :p-var "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 (list :p-var "x") (list :int 1))
      (list :bind (list :p-var "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 (list :p-var "x") (list :int 1))
      (list :bind (list :p-var "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 (list :p-var "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 (list :p-var "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}
