## parseModule

> 其實他很邪惡，要用到時才開始讀，雖然他叫 lazy ，但是跟 lazy evaluation 沒有關係。 m*******m 很不喜歡。

先開個新檔案 `sample/A.hs` ：

```Haskell
module Main where

import Prelude ()

main = putStrLn "hello, world"
```

讀檔可以用 `Prelude` 中的 `getContents` ，它會還給你一個 `IO String` ，可以用在 `main` 的 do notation 裡面：

```Haskell
main = do
  inputStr <- getContents
  putStrLn inputStr
```

這樣子輸入一行就能看到程式輸出一行。

把 `inputStr` 餵給來自 Language.Haskell.Exts.Annotated 的 `parseModule` ，可以得到 [`ParseResult`][ParseResult] ，再以 `case ... of` 看看結果是 `ParseOk` 還是 `ParseFailed` ，從裡面拆出 AST ：

```Haskell
main = do
  inputStr <- getContents
  let res = parseModule inputStr
  putStrLn $ case res of
    ParseOk mod -> show mod
    ParseFailed _ msg -> msg
```

Language.Haskell.Exts.Annotated 吐出來的 AST 包含了描述程式碼位置的 `SrcSpanInfo` ，直接印出來大概像：

```Haskell
Module (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 1 1 6 1, srcInfoPoints = [SrcSpan "<unknown>.hs" 1 1 1 1,SrcSpan "<unknown>.hs" 1 1 1 1,SrcSpan "<unknown>.hs" 3 1 3 1,SrcSpan "<unknown>.hs" 5 1 5 1,SrcSpan "<unknown>.hs" 6 1 6 1,SrcSpan "<unknown>.hs" 6 1 6 1]}) (Just (ModuleHead (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 1 1 1 18, srcInfoPoints = [SrcSpan "<unknown>.hs" 1 1 1 7,SrcSpan "<unknown>.hs" 1 13 1 18]}) (ModuleName (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 1 8 1 12, srcInfoPoints = []}) "Main") Nothing Nothing)) [] [ImportDecl {importAnn = SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 3 1 3 18, srcInfoPoints = [SrcSpan "<unknown>.hs" 3 1 3 7]}, importModule = ModuleName (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 3 8 3 15, srcInfoPoints = []}) "Prelude", importQualified = False, importSrc = False, importSafe = False, importPkg = Nothing, importAs = Nothing, importSpecs = Just (ImportSpecList (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 3 16 3 18, srcInfoPoints = [SrcSpan "<unknown>.hs" 3 16 3 17,SrcSpan "<unknown>.hs" 3 17 3 18]}) False [])}] [PatBind (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 1 5 31, srcInfoPoints = []}) (PVar (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 1 5 5, srcInfoPoints = []}) (Ident (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 1 5 5, srcInfoPoints = []}) "main")) (UnGuardedRhs (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 6 5 31, srcInfoPoints = [SrcSpan "<unknown>.hs" 5 6 5 7]}) (App (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 8 5 31, srcInfoPoints = []}) (Var (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 8 5 16, srcInfoPoints = []}) (UnQual (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 8 5 16, srcInfoPoints = []}) (Ident (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 8 5 16, srcInfoPoints = []}) "putStrLn"))) (Lit (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 17 5 31, srcInfoPoints = []}) (String (SrcSpanInfo {srcInfoSpan = SrcSpan "<unknown>.hs" 5 17 5 31, srcInfoPoints = []}) "hello, world" "hello, world")))) Nothing]
```

如果覺得自己在操作 AST 時，會被 `SrcSpanInfo` 干擾，可以加上 `fmap (const ())` ：

```Haskell
main = do
  inputStr <- getContents
  let res = parseModule inputStr
  putStrLn $ case res of
    ParseOk mod -> show (fmap (const ()) mod)
    ParseFailed _ msg -> msg
```

`fmap` 讓 `const ()` 作用在整個 AST 上（前面提到的 `ann` 和 `amap` 則作用在單一個 node 上），把全部的 `SrcSpanInfo` 清光光，變成：

```Haskell
Module () (Just (ModuleHead () (ModuleName () "Main") Nothing Nothing)) [] [ImportDecl {importAnn = (), importModule = ModuleName () "Prelude", importQualified = False, importSrc = False, importSafe = False, importPkg = Nothing, importAs = Nothing, importSpecs = Just (ImportSpecList () False [])}] [PatBind () (PVar () (Ident () "main")) (UnGuardedRhs () (App () (Var () (UnQual () (Ident () "putStrLn"))) (Lit () (String () "hello, world" "hello, world")))) Nothing]
```

在還沒有真正的 pretty print 前，手動排版後，看起來是：

```Haskell
Module
  ()
  (Just (ModuleHead () (ModuleName () "Main") Nothing Nothing))
  []
  [ ImportDecl
      { importAnn = ()
      , importModule = ModuleName () "Prelude"
      , importQualified = False
      , importSrc = False
      , importSafe = False
      , importPkg = Nothing
      , importAs = Nothing
      , importSpecs = Just (ImportSpecList () False [])
      }
  ]
  [ PatBind
      ()
      (PVar () (Ident () "main"))
      (UnGuardedRhs
        ()
        (App
          ()
          (Var () (UnQual () (Ident () "putStrLn")))
          (Lit () (String () "hello, world" "hello, world"))))
      Nothing
  ]
```

對我來說，在習慣查文件前，可以對整個 AST 長什麼樣子，有個概念。

[ParseResult]: https://hackage.haskell.org/package/haskell-src-exts-1.17.1/docs/Language-Haskell-Exts-Parser.html#t:ParseResult
