Parsec notFollowedBy

パーサ notFollowedBy の動作も理解が難しい。このパーサは、引き数のパターンが見つからないときは、Right () を返し、見つかれば Left err を返す。

Prelude> :m Text.ParserCombinators.Parsec
Prelude Text.ParserCombinators.Parsec> :set prompt "ghci> "
ghci> parseTest (notFollowedBy (char 'a')) "bcd"
()
ghci> parseTest (notFollowedBy (char 'a')) "abc"
parse error at (line 1, column 1):
unexpected "a"

また、このパーサは次の例のように文字列を消費しない。

ghci> parseTest (do notFollowedBy (char 'a'); letter) "bcd"
'b'

notFollowedBy の引き数は、char 以外のパーサも取ることができるが、工夫が必要だ。単純に他のパーサ例えば string を使うと、型エラーになってしまう。

ghci> parseTest (notFollowedBy (string "abc")) "bcd"

:1:26:
    Couldn't match expected type `Char' against inferred type `[Char]'
      Expected type: GenParser Char () Char
      Inferred type: CharParser () String
    In the first argument of `notFollowedBy', namely `(string "abc")'
    In the first argument of `parseTest', namely
      `(notFollowedBy (string "abc"))'

こんな時は string "abc" の後に >> return undefined をつけて、未定義型を返してやればうまくいく。(しかし、反則技らしい。)

ghci> parseTest (notFollowedBy (string "abc" >> return undefined)) "bcd"
()

こんなパーサを何に利用するのだろうと思ったが、予約後 let の検出に使う例が出ていた。

ghci> let keywordLet = try (do { string "let"; notFollowedBy alphaNum})
ghci> parseTest keywordLet "let a = b"
()
ghci> parseTest keywordLet "leta = b"
parse error at (line 1, column 1):
unexpected "a"

Parsec でパターンマッチだけしても仕方がないと思うかもしれないが、まず、パターンマッチに慣れることが大切だ。自在にパターンを作れるようになったら、パターンの中にアクションを記述することができる。Parsecの習得の戦略としては、パターンの記述と、アクションのプログラミングを分けて考えると見通しがよくなる。
[PR]
by tnomura9 | 2011-12-05 20:25 | Haskell | Comments(0)
<< Parsec newline ... Parsec fmap >>