Parsec --- 2.5 Sequences and separators (1)
「単語」をパースするパーサwordを考えてみます.ここで「単語」というのは一つ以上の「大文字・小文字のアルファベット」から構成されます.
word:: Parser String word = do { c <- letter ; do { cs <- word; return (c:cs) ;} <|> return [c]; }
do記法で,{}を使ってますが,こう書かないと入れ子構造のせいか,<|> return [c]が適切に解釈されませんでした(cがスコープ内にないとかいわれてしまう).
これはよく見ればわかりますけど,読みにくいです.まず,最初のletterパーサで一文字とります.そして,二文字目(二つ目のdo記法)以降に行こうとするのですが,二文字目以降がなければ,return [c]で一文字目だけで構成されるStringを返します.
二文字目以降がある場合は,再帰的にwordパーサを呼び出し,それが返した値をcsに束縛して,一文字目のcと合わせて(c:cs)を返します.
具体的に考えると,例えば,``cat''という文字列をパースすると,
word "cat" c <- 'c' word "at" c <- a word "t" c <- t return "t" return "at" return "cat"
ということです.しかし,これがたくさんあるとあまりにつらいので,many1というパーサコンビネータが用意されています.これは他のパーサを引数として,そのパーサが「1回以上」ということを表現します.つまり,上のwordパーサは
word:: Parser String word = many1 letter
と書くことが出来ます.manyというパーサコンビネータも用意されており,これは「0回以上」を意味します.
word:: Parser String word = many1 letter *Main> parseTest word "haskell" "haskell" word':: Parser String word' = many letter *Main> parseTest word' "98haskell" "" word''::Parser String word''=many1 (letter <|> digit) *Main> parseTest word'' "Haskell98Haskell" "Haskell98Haskell"