カスタムレキシカルアナライザを使用したエラー回復

エラーリカバリに関する説明はカスタムレキシカルアナライザと互換性が良好ですが、エラーリカバリはパーサーエラーからの回復のみをサポートし、レキシカルアナライザエラーからの回復はサポートしていません。このページでは、レキシカルアナライザエラーからの回復方法を紹介します。

エラーリカバリを有効にするには:

use lalrpop_util::ErrorRecovery;

- parser_definition<'err>(errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, &'static str>>);
+ parser_definition<'err>(errors: &'err mut Vec<ErrorRecovery<usize, Lexeme, LexicalError>>);

これでパーサーエラーに対するエラーリカバリが機能しますが、レキシカルアナライザが予期しないトークンに遭遇した場合、それはパーサーエラーではなくレキシカルアナライザエラーであるため、パーサーはそれから回復できません。

print (a - $);

// Err(User { error: InvalidToken })

これはレキシカルアナライザストリーム内の不整合によって発生します。なぜなら、lalrpopパーサーは常に正常なトークンストリームを期待しているからです。これを解決するには、レキシカルアナライザ部分に「エラーリカバリ」を処理するための回避策を追加する必要があります。例えば:

impl<'input> Iterator for Lexer<'input> {
    type Item = Spanned<Lexeme, usize, LexicalError>;

    fn next(&mut self) -> Option<Self::Item> {
        self.token_stream
        .next()
        .map(|(lexeme, span)|
            match lexeme {
                Ok(lexeme) => Ok((span.start, lexeme, span.end)),
                Err(_) => Ok((span.start, Lexeme::ErrorToken, span.end)),
                // または独自のレキシカルエラーをパースエラーとして指定
            }
        )
    }
}

そして、エラートークンを表現する(または独自のカスタムエラータイプ)ための新しいメンバーをLexeme列挙型に追加します。

pub enum Lexeme {
    // ...

    // カンマを忘れずに
+   ErrorToken
}

JavaScriptのNaNが数値であるように、我々は現在Lexeme::ErrorTokenを持っています。これはレキシカルアナライザ内のトークンです。このトークンはパーサーに渡すことができますが、パーサーグラマで使用されない場合、パースされません。これにより、レキシカルアナライザエラーが回復可能なパーサーエラーに変換され、この場合にエラーリカバリが可能になります。

また、独自のエラーをカスタマイズする必要があるかもしれません。詳細については、失敗可能なアクションのセクションを参照してください。

! => {
    let error = ErrorRecovery {
        error: ParseError::User {
            error: LexicalError::CustomErrorType,
        },
        dropped_tokens: Vec::new(), // またはドロップされたトークンを指定
    };
    errors.push(error);

    Expression::ErrorNode
}

タグ: lalrpop lexer parser error-recovery rust

8月19日 16:58 投稿