;; case-of and do-notation parser tests.
;; Covers the minimal patterns needed to make these meaningful: var,
;; wildcard, literal, constructor (with and without args), tuple, list.

;; ── Patterns (in case arms) ──
(hk-test
  "wildcard pat"
  (hk-parse "case x of _ -> 0")
  (list
    :case
    (list :var "x")
    (list (list :alt (list :p-wild) (list :int 0)))))

(hk-test
  "var pat"
  (hk-parse "case x of y -> y")
  (list
    :case
    (list :var "x")
    (list
      (list :alt (list :p-var "y") (list :var "y")))))

(hk-test
  "0-arity constructor pat"
  (hk-parse "case x of\n  Nothing -> 0\n  Just y -> y")
  (list
    :case
    (list :var "x")
    (list
      (list :alt (list :p-con "Nothing" (list)) (list :int 0))
      (list
        :alt
        (list :p-con "Just" (list (list :p-var "y")))
        (list :var "y")))))

(hk-test
  "int literal pat"
  (hk-parse "case n of\n  0 -> 1\n  _ -> n")
  (list
    :case
    (list :var "n")
    (list
      (list :alt (list :p-int 0) (list :int 1))
      (list :alt (list :p-wild) (list :var "n")))))

(hk-test
  "string literal pat"
  (hk-parse "case s of\n  \"hi\" -> 1\n  _ -> 0")
  (list
    :case
    (list :var "s")
    (list
      (list :alt (list :p-string "hi") (list :int 1))
      (list :alt (list :p-wild) (list :int 0)))))

(hk-test
  "tuple pat"
  (hk-parse "case p of (a, b) -> a")
  (list
    :case
    (list :var "p")
    (list
      (list
        :alt
        (list
          :p-tuple
          (list (list :p-var "a") (list :p-var "b")))
        (list :var "a")))))

(hk-test
  "list pat"
  (hk-parse "case xs of\n  [] -> 0\n  [a] -> a")
  (list
    :case
    (list :var "xs")
    (list
      (list :alt (list :p-list (list)) (list :int 0))
      (list
        :alt
        (list :p-list (list (list :p-var "a")))
        (list :var "a")))))

(hk-test
  "nested constructor pat"
  (hk-parse "case x of\n  Just (a, b) -> a\n  _ -> 0")
  (list
    :case
    (list :var "x")
    (list
      (list
        :alt
        (list
          :p-con
          "Just"
          (list
            (list
              :p-tuple
              (list (list :p-var "a") (list :p-var "b")))))
        (list :var "a"))
      (list :alt (list :p-wild) (list :int 0)))))

(hk-test
  "constructor with multiple var args"
  (hk-parse "case t of Pair a b -> a")
  (list
    :case
    (list :var "t")
    (list
      (list
        :alt
        (list
          :p-con
          "Pair"
          (list (list :p-var "a") (list :p-var "b")))
        (list :var "a")))))

;; ── case-of shapes ──
(hk-test
  "case with explicit braces"
  (hk-parse "case x of { Just y -> y ; Nothing -> 0 }")
  (list
    :case
    (list :var "x")
    (list
      (list
        :alt
        (list :p-con "Just" (list (list :p-var "y")))
        (list :var "y"))
      (list :alt (list :p-con "Nothing" (list)) (list :int 0)))))

(hk-test
  "case scrutinee is a full expression"
  (hk-parse "case f x + 1 of\n  y -> y")
  (list
    :case
    (list
      :op
      "+"
      (list :app (list :var "f") (list :var "x"))
      (list :int 1))
    (list (list :alt (list :p-var "y") (list :var "y")))))

(hk-test
  "case arm body is full expression"
  (hk-parse "case x of\n  Just y -> y + 1")
  (list
    :case
    (list :var "x")
    (list
      (list
        :alt
        (list :p-con "Just" (list (list :p-var "y")))
        (list :op "+" (list :var "y") (list :int 1))))))

;; ── do blocks ──
(hk-test
  "do with two expressions"
  (hk-parse "do\n  putStrLn \"hi\"\n  return 0")
  (list
    :do
    (list
      (list
        :do-expr
        (list :app (list :var "putStrLn") (list :string "hi")))
      (list
        :do-expr
        (list :app (list :var "return") (list :int 0))))))

(hk-test
  "do with bind"
  (hk-parse "do\n  x <- getLine\n  putStrLn x")
  (list
    :do
    (list
      (list :do-bind (list :p-var "x") (list :var "getLine"))
      (list
        :do-expr
        (list :app (list :var "putStrLn") (list :var "x"))))))

(hk-test
  "do with let"
  (hk-parse "do\n  let y = 5\n  print y")
  (list
    :do
    (list
      (list
        :do-let
        (list (list :bind (list :p-var "y") (list :int 5))))
      (list
        :do-expr
        (list :app (list :var "print") (list :var "y"))))))

(hk-test
  "do with multiple let bindings"
  (hk-parse "do\n  let x = 1\n      y = 2\n  print (x + y)")
  (list
    :do
    (list
      (list
        :do-let
        (list
          (list :bind (list :p-var "x") (list :int 1))
          (list :bind (list :p-var "y") (list :int 2))))
      (list
        :do-expr
        (list
          :app
          (list :var "print")
          (list :op "+" (list :var "x") (list :var "y")))))))

(hk-test
  "do with bind using constructor pat"
  (hk-parse "do\n  Just x <- getMaybe\n  return x")
  (list
    :do
    (list
      (list
        :do-bind
        (list :p-con "Just" (list (list :p-var "x")))
        (list :var "getMaybe"))
      (list
        :do-expr
        (list :app (list :var "return") (list :var "x"))))))

(hk-test
  "do with explicit braces"
  (hk-parse "do { x <- a ; y <- b ; return (x + y) }")
  (list
    :do
    (list
      (list :do-bind (list :p-var "x") (list :var "a"))
      (list :do-bind (list :p-var "y") (list :var "b"))
      (list
        :do-expr
        (list
          :app
          (list :var "return")
          (list :op "+" (list :var "x") (list :var "y")))))))

;; ── Mixing case/do inside expressions ──
(hk-test
  "case inside let"
  (hk-parse "let f = \\x -> case x of\n                 Just y -> y\n                 _ -> 0\nin f 5")
  (list
    :let
    (list
      (list
        :bind
        (list :p-var "f")
        (list
          :lambda
          (list (list :p-var "x"))
          (list
            :case
            (list :var "x")
            (list
              (list
                :alt
                (list :p-con "Just" (list (list :p-var "y")))
                (list :var "y"))
              (list :alt (list :p-wild) (list :int 0)))))))
    (list :app (list :var "f") (list :int 5))))

(hk-test
  "lambda containing do"
  (hk-parse "\\x -> do\n  y <- x\n  return y")
  (list
    :lambda
    (list (list :p-var "x"))
    (list
      :do
      (list
        (list :do-bind (list :p-var "y") (list :var "x"))
        (list
          :do-expr
          (list :app (list :var "return") (list :var "y")))))))

{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}
