;; 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}