Re: Build Your Own Haskell Compiler #0.3


用了 readFile ,就脫離不了 IO 。於是只好把希望得到的,放在 IO 裡面:

readModule :: String -> IO (Maybe (Module SrcSpanInfo))

一開始先來個 do ,躲到 IO 裡面去:

readModule mod = do
  content <- readFile $ modName ++ ".hs"
  let res = parseModule content
  return $ case res of
    ParseOk mod -> Just mod
    ParseFailed _ msg -> Nothing

蒐集 AST 中 import 的 Module 時,要用到 readModule 的結果, IO 也跟著出現了:

collectModule :: Module SrcSpanInfo -> IO (M.Map String Bool)

這件事一直讓我想起 What Color is Your Function 。對了,別忘記 import Map

import Language.Haskell.Exts.Annotated
import qualified Data.Map.Strict as M

並在 my-project.cabal 中補上 containers

executable my-project-exe
  hs-source-dirs:      app
  main-is:             Main.hs
  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
  build-depends:       base
                     , my-project
                     , containers
                     , haskell-src-exts
  default-language:    Haskell2010

先試著蒐集不重複的 Module 就好,不遞迴讀檔:

collectModule mod = do
  retrun $ case mod of
    Module _ mModuleHead _ imports _ -> go M.empty imports where
      modName = case mModuleHead of
        Just (ModuleHead _ (ModuleName _ name) _ _) -> name
        Nothing -> "Main"
      go acc [] = acc
      go acc (m : ms) =
          (Module _ name) = importModule m
          case M.member name acc of
            False -> go (M.insert name True acc) ms
            True -> go acc ms
    _ -> M.empty

回到 main ,可以這樣印出單一個檔案中 import 的東西:

main = do
  mMod <- readModule "A"
  res <- case mMod of
    Just mod -> collectModule mod
    Nothing -> return M.empty
  putStrLn $ show res
Isaac Huang 發佈於