Parsec try
try が必要な理由
string "hello" または string "howdy" にマッチするパーサを作りたいと思って <|> 演算子を使って次のように実験しても、思っていたような結果が出ない。Prelude Text.Parsec> parseTest (string "hello" <|> string "howdy") "howdy" parse error at (line 1, column 1): unexpected "o" expecting "hello"
これは入力の "howdy" と string "hello" のマッチが行われた際に先頭の 'h' が消費されてしまうため、次に string "howdy" とマッチを試みても入力バッファの文字列が "owdy" になってしまうからだ。
try
これを防ぐためには string "hello" のマッチが失敗したとき、入力バッファの状態を元に戻しておかなければならない。それを行うのが try p 関数である。try 関数を使うと、パーサ p のマッチが失敗したとき、マッチの過程で消費された文字をマッチの前の状態に復元してくれる。
したがって、string "hello" がマッチしないときは string "howdy" のマッチを試みるという意図のためには次のように記述するとうまくいく。
Prelude Text.Parsec> parseTest (try (string "hello") <|> string "howdy") "howdy" "howdy"
マッチが失敗しても文字(列)の消費は起きるのは Parsec の仕様であるが、注意が必要だ。
細かい仕様
Parsec でプログラムしていると、文字列や数値のような抽象的な概念でプログラムすることができるので、考えがそのままプログラムできているような錯覚を起こすが、Parsec といえどあくまでもコンピュータのプログラムなので、細かい仕様を知って活用する必要があるときもある。