連続する計算をパースするパーサ 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
数式のパーサを書くのが楽しくなりそうだ。