北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

rustのcombineで入力されたトークンによる場合分け

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 が正常処理というような意味はない。どちらでもいいから、入っているパーサーを実行するだけである。実際、 EitherParser トレイトの実装を見ると以下のようになっている。

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つのパーサの入出力が揃っていないとまずいことを表す。