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"