プログラミング Haskell 第8章 関数型パーサー その3

(>>=) と do 記法で少し寄り道してしまったので今日はさくさくパーサの組み合わせを読み進めていきます。

  • 連結(これは昨日まで読んだ >>=)
  • 選択(+++) p を使ってみてエラーだったら q を適用
(+++) :: Parser a -> Parser a -> Parser a
p +++ q = \inp -> case parse p inp of
                    [] -> parse q inp
                    [(v, out)] -> [(v, out)]
  • return, failure, item を組み合せてパーサを組み立てる
    • 条件 p を満たす1文字をパースする sat p
sat :: (Char -> Bool) -> Parser Char
sat p = do x <- item
           if p x then return x else failure
    • sat を用いて digit = sat isDigit のように典型的な文字判定パーサをたくさん派生できる
  • 文字列の一致を判定する string
string :: String -> Parser String
string [] = return []
string(x:xs) = do char x
                  string xs
                  return (x:xs)
    • string の定義は再帰と do 記法の組み合わせでちょっと「んっ?」となる。char, string の呼び出しは一致しているかどうかの判定をしていて、どこかで失敗するとそこで [] が返されて終わるだけ。なので生成器はない。全て受容されたら return (x:xs) まで到達して return で一致した文字列自体を返すパーサが返される。文字列を返すわけじゃないのがミソ(型に注目)
  • 引数に受け取ったパーサを繰り返し適用して結果をリストで返す many, many1
    • many は 0回以上の繰り返しを受容、many1 は少なくとも 1回は適用できないとエラー
    • many と many1 は相互再帰で定義される。many は many1 と return [] の選択、many1 は引数のパーサと many の連結
    • many, many1 と digit などの Char を判定するパーサを組み合わせて natural のようなパーサが構築できる

string の定義は少し考える必要がありました。簡単がパーサが組み合わさっていってだんだん使えるものになっていく様子が心地良いですね。第8章はもう少し残っています。あと1日か2日くらい必要そうです。