;; Module header + imports. The parser switches from (:program DECLS)
;; to (:module NAME EXPORTS IMPORTS DECLS) as soon as a module header
;; or any `import` decl appears.

;; ── Module header ──
(hk-test
  "simple module, no exports"
  (hk-parse-top "module M where\n  f = 1")
  (list
    :module
    "M"
    nil
    (list)
    (list (list :fun-clause "f" (list) (list :int 1)))))

(hk-test
  "module with dotted name"
  (hk-parse-top "module Data.Map where\nf = 1")
  (list
    :module
    "Data.Map"
    nil
    (list)
    (list (list :fun-clause "f" (list) (list :int 1)))))

(hk-test
  "module with empty export list"
  (hk-parse-top "module M () where\nf = 1")
  (list
    :module
    "M"
    (list)
    (list)
    (list (list :fun-clause "f" (list) (list :int 1)))))

(hk-test
  "module with exports (var, tycon-all, tycon-with)"
  (hk-parse-top "module M (f, g, Maybe(..), List(Cons, Nil)) where\nf = 1\ng = 2")
  (list
    :module
    "M"
    (list
      (list :ent-var "f")
      (list :ent-var "g")
      (list :ent-all "Maybe")
      (list :ent-with "List" (list "Cons" "Nil")))
    (list)
    (list
      (list :fun-clause "f" (list) (list :int 1))
      (list :fun-clause "g" (list) (list :int 2)))))

(hk-test
  "module export list including another module"
  (hk-parse-top "module M (module Foo, f) where\nf = 1")
  (list
    :module
    "M"
    (list (list :ent-module "Foo") (list :ent-var "f"))
    (list)
    (list (list :fun-clause "f" (list) (list :int 1)))))

(hk-test
  "module export with operator"
  (hk-parse-top "module M ((+:), f) where\nf = 1")
  (list
    :module
    "M"
    (list (list :ent-var "+:") (list :ent-var "f"))
    (list)
    (list (list :fun-clause "f" (list) (list :int 1)))))

(hk-test
  "empty module body"
  (hk-parse-top "module M where")
  (list :module "M" nil (list) (list)))

;; ── Imports ──
(hk-test
  "plain import"
  (hk-parse-top "import Foo")
  (list
    :module
    nil
    nil
    (list (list :import false "Foo" nil nil))
    (list)))

(hk-test
  "qualified import"
  (hk-parse-top "import qualified Data.Map")
  (list
    :module
    nil
    nil
    (list (list :import true "Data.Map" nil nil))
    (list)))

(hk-test
  "import with alias"
  (hk-parse-top "import Data.Map as M")
  (list
    :module
    nil
    nil
    (list (list :import false "Data.Map" "M" nil))
    (list)))

(hk-test
  "import with explicit list"
  (hk-parse-top "import Foo (bar, Baz(..), Quux(X, Y))")
  (list
    :module
    nil
    nil
    (list
      (list
        :import
        false
        "Foo"
        nil
        (list
          :spec-items
          (list
            (list :ent-var "bar")
            (list :ent-all "Baz")
            (list :ent-with "Quux" (list "X" "Y"))))))
    (list)))

(hk-test
  "import hiding"
  (hk-parse-top "import Foo hiding (x, y)")
  (list
    :module
    nil
    nil
    (list
      (list
        :import
        false
        "Foo"
        nil
        (list
          :spec-hiding
          (list (list :ent-var "x") (list :ent-var "y")))))
    (list)))

(hk-test
  "qualified + alias + hiding"
  (hk-parse-top "import qualified Data.List as L hiding (sort)")
  (list
    :module
    nil
    nil
    (list
      (list
        :import
        true
        "Data.List"
        "L"
        (list :spec-hiding (list (list :ent-var "sort")))))
    (list)))

;; ── Combinations ──
(hk-test
  "module with multiple imports and a decl"
  (hk-parse-top "module M where\nimport Foo\nimport qualified Bar as B\nf = 1")
  (list
    :module
    "M"
    nil
    (list
      (list :import false "Foo" nil nil)
      (list :import true "Bar" "B" nil))
    (list (list :fun-clause "f" (list) (list :int 1)))))

(hk-test
  "headerless file with imports"
  (hk-parse-top "import Foo\nimport Bar (baz)\nf = 1")
  (list
    :module
    nil
    nil
    (list
      (list :import false "Foo" nil nil)
      (list
        :import
        false
        "Bar"
        nil
        (list :spec-items (list (list :ent-var "baz")))))
    (list (list :fun-clause "f" (list) (list :int 1)))))

(hk-test
  "plain program (no header, no imports) still uses :program"
  (hk-parse-top "f = 1\ng = 2")
  (list
    :program
    (list
      (list :fun-clause "f" (list) (list :int 1))
      (list :fun-clause "g" (list) (list :int 2)))))

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