combine を使ったときに、例えば識別子のパース時に予約語を弾く方法。
最初は and_then
を使うのかと思ったのだけど、 エラーの型指定がえらく面倒 だったので諦め。というか、なんか絶対違うだろうと思って別の方法を模索。
Parser のドキュメントに以下の例がある。これだ。
digit() .then(|d| { if d == '9' { value(9).left() } else { unexpected_any(d).message("Not a nine").right() } })
ということで、 then
を使うと Parser を返す処理を書けるので、ここで unexpected_any
を使って失敗させてやればよい。
ちょっとこのコードを見てぎょっとするのは left()
と right()
だが、これは if
で分岐するパーサの戻り値が違うため。 rust ではよくみるやつだが、 combine のパーサコンビネータは Parser
トレイトを持つメソッドごとの独自の型 Value<I, T>
や Unexpected<I, T>
を返す。ということで Either
にまとめれば型を混ぜて1つにできてハッピーなのだが、次に、どっちを left
にしてどっちを right
にするのがいいのかという疑問が出てくる。
正解は実はどちらでもよくて、これはエラーを表す Either
ではないので、 right
が正常処理というような意味はない。どちらでもいいから、入っているパーサーを実行するだけである。実際、 Either
の Parser
トレイトの実装を見ると以下のようになっている。
impl<L, R> Parser for Either<L, R> where L: Parser, R: Parser<Input = L::Input, Output = L::Output>, { ..snip.. fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult<Self::Output, Self::Input> { match *self { Either::Left(ref mut x) => x.parse_lazy(input), Either::Right(ref mut x) => x.parse_lazy(input), } } ..snip.. }
R: Parser<Input = L::Input, Output = L::Output>
の制約は、2つのパーサの入出力が揃っていないとまずいことを表す。