Parsec 入門


Text.Parsec.Char

Text.Parsec モジュール

Haskell を勉強するからには Parsec も使ってみたい。Parsec を使うためには Text.Parsec モジュールをインポートする。

Prelude> import Text.Parsec
Prelude Text.Parsec>

パーサ

Parsec の用語でパーサとは文字列のパターンを発見するための、プログラムの小片のことだ。実際には ParsecT s u m a 型のモナド値である。

たとえば、letter というパーサは英文字1文字にマッチするし、digit というパーサは数字1文字にマッチする。しかし、パーサを直接に文字列に関数適用することはできないので、parse 関数の第1引数としてパーサを与えることでパターンマッチが行われる。パターンを発見したい文字列は第3引数として与える。第2引数には文字列を与えるが、それはエラーメッセージのときに使われる。

Prelude Text.Parsec> parse letter "foo" "abc"
Right 'a'
Prelude Text.Parsec> parse digit "foo" "123"
Right '1'
Prelude Text.Parsec> parse digit "foo" "abc"
Left "foo" (line 1, column 1):
unexpected "a"
expecting digit

要するに、parse 関数にパーサと文字列を渡してやれば、文字列の先頭部分のパターンマッチが行われる。簡単だ。

1文字のパーサ

1文字のパターンマッチ用のパーサは Text.Parsec.Char モジュールに定義されている。

Prelude Text.Parsec> :browse Text.Parsec.Char
alphaNum :: Stream s m Char => ParsecT s u m Char
anyChar :: Stream s m Char => ParsecT s u m Char
char :: Stream s m Char => Char -> ParsecT s u m Char
crlf :: Stream s m Char => ParsecT s u m Char
digit :: Stream s m Char => ParsecT s u m Char
endOfLine :: Stream s m Char => ParsecT s u m Char
hexDigit :: Stream s m Char => ParsecT s u m Char
letter :: Stream s m Char => ParsecT s u m Char
lower :: Stream s m Char => ParsecT s u m Char
newline :: Stream s m Char => ParsecT s u m Char
noneOf :: Stream s m Char => [Char] -> ParsecT s u m Char
octDigit :: Stream s m Char => ParsecT s u m Char
oneOf :: Stream s m Char => [Char] -> ParsecT s u m Char
satisfy :: Stream s m Char => (Char -> Bool) -> ParsecT s u m Char
space :: Stream s m Char => ParsecT s u m Char
spaces :: Stream s m Char => ParsecT s u m ()
string :: Stream s m Char => String -> ParsecT s u m String
tab :: Stream s m Char => ParsecT s u m Char
upper :: Stream s m Char => ParsecT s u m Char

各パーサがどんなパターンにマッチするのかは、名前を見ればなんとなくわかる。たとえば、anyChar はどんな文字でもひとつの文字にマッチするし、newline は改行文字にマッチする。oneOf は文字列のリストのどれかにマッチするし、noneOf は文字列のリストに含まれない文字にマッチする。

Prelude Text.Parsec> parse (oneOf "ab") "foo" "abc"
Right 'a'
Prelude Text.Parsec> parse (noneOf "ab") "foo" "cde"
Right 'c'

パーサはモナド値なので do 記法で順次実行することもできる。

Prelude Text.Parsec> parse (do {letter; digit; letter}) "foo" "a1b"
Right 'b'

また、<- 記法で変数にモナドのコンテナの値を取り出したり、それを加工したりできる。

Prelude Text.Parsec> parse (do x <- letter; y <- digit; z <- letter;
 return (x:y:z:[])) "foo" "a1bcde"
Right "a1b"

Parsec で使用可能な 1 文字のパーサが Text.Parsec.Char を覗けば (:browse) 調べることができるし、パーサの本体はモナドで、実際のパースは parse 関数で行うということを理解すれば、Parsec活用の第1歩はクリアだ。

様々な parse 関数

Text.Parsec モジュールには次のように様々な parse 関数がある。

parse ::
  Stream s Data.Functor.Identity.Identity t =>
  Parsec s () a -> SourceName -> s -> Either ParseError a
parseTest ::
  (Stream s Data.Functor.Identity.Identity t, Show a) =>
  Parsec s () a -> s -> IO ()
runP ::
  Stream s Data.Functor.Identity.Identity t =>
  Parsec s u a -> u -> SourceName -> s -> Either ParseError a
runPT ::
  Stream s m t =>
  ParsecT s u m a -> u -> SourceName -> s -> m (Either ParseError a)
runParsecT ::
  Monad m =>
  ParsecT s u m a -> State s u -> m (Consumed (m (Reply s u a)))
runParser ::
  Stream s Data.Functor.Identity.Identity t =>
  Parsec s u a -> u -> SourceName -> s -> Either ParseError a
runParserT ::
  Stream s m t =>
  ParsecT s u m a -> u -> SourceName -> s -> m (Either ParseError a)

いずれも、基本的にはパーサ・モナドと入力文字列を引数にしてパースをおこなう関数だが、Parsec モナドには多くの機能が含まれているためそのどれを使うかで parse 関数が異なっている。

しかし、Parsec の基本的な機能を学習するためには parse 関数と parseTest 関数の使い方を覚えれば十分だ。