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 関数の使い方を覚えれば十分だ。