Parsec 入門


連続する計算をパースするパーサ chainl1

chainl1

パーサコンビネータ chainl1 は連続する2項演算をパースするパーサコンビネータが chainl1 だ。つまり 1+2+3+4 のような計算をパースする。

chain の第1引数に扱う数の型のパーサ、第2引数に演算子をパースし演算を返すパーサを与えると上の式をパースして計算までしてくれるパーサを作ることができる。

説明がややこしいので実際にやってみる。まず chainl1 の型を見てみる。

Prelude Text.Parsec> :t chainl1
chainl1
:: Stream s m t =>
ParsecT s u m a -> ParsecT s u m (a -> a -> a) -> ParsecT s u m a

第1の引数は Int 型のデータを返すパーサを割り当て、第2の引数には演算そのものを割り当てるパーサを割り当てると良いようだ。

そこでまず整数をパースして Int 型の値を返すパーサ integer を作成する。

Prelude Text.Parsec> integer = ((read :: String -> Int) <$> many digit) ::
 Parsec String () Int

次に、'+' をパースして (+) (足し算の演算そのもの)を返すパーサ add を作成する。

Prelude Text.Parsec> add = (char '+' >> return (+)) :: Parsec String ()
 (Int -> Int -> Int)

この integer と add を chainl1 の引数に与えると、1+2+3+4 を計算するパーサができる。

Prelude Text.Parsec> parseTest (chainl1 integer add) "1+2+3+4"
10

便利なパーサコンビネータだ。

chair1

chainl1 は左結合の式に対応するが、chainr1 は右結合の式に対応する。

Prelude Text.Parsec> parseTest (chainr1 integer (char '^' >> return (^)))
 "3^2^2"
81

数式のパーサを書くのが楽しくなりそうだ。