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歩はクリアだ。