ANTLR(7) - Lexerのエラー処理

  • @rulecatchでcatch節を書くと、全体に共通するエラー処理を書ける。
    • @lexer::rulecatch はないみたい
  • 確認したエラー
    • LexerでTokenizeできない先頭文字を見つけると、NoViableAltException
    • 2文字目以降がmatchできないと MismatchedTokenException
    • 'a'..'z'などの範囲がmatchできないと MismatchedRangeException
    • (...)+ が0回の繰り返しだと EarlyExitException
    • ('hoge'|'foo') がヒットしないと MismatchedSetException
    • ~(...) がヒットしないと MismatchedNotSetException
  • 通常のエラー処理
    1. reportError(RecognitionException) を呼び出してコンソールに表示
    2. recover(RecognitionException) を呼び出して一文字スキップ
    3. 一部のエラーはrecoverが2回以上呼び出される。対策が必要になるかも。

Irenkaではコンソールにエラーを表示されても困るので、reportError(RecognitionException), recover(RecognitionException)を上書きしてエラー情報を収集する方向で。

ASTの生成

ANTLR(7) - Mini Irenka Query - しげるメモ を部分的に書き換え。

@lexer::header {
package research;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
}

@lexer::members {
private List<RecognitionException> exceptions;

@Override
public void reportError(RecognitionException e) {
    // skip
}

@Override
public void recover(RecognitionException re) {
    if (exceptions == null) {
        exceptions = new ArrayList<RecognitionException>();
    }
    // avoid re-recovering
    if (!exceptions.isEmpty() && exceptions.get(exceptions.size() - 1) == re) {
        return;
    }
    exceptions.add(re);
    input.consume();
    // skip identifier
    if (Character.isJavaIdentifierPart(re.c)) {
        while (true) {
            int next = input.LA(1);
            if (!(next >= 0 && Character.isJavaIdentifierPart(next))) {
                break;
            }
            input.consume();
        }
    }
}
}

エラーを表示するのではなく、リストにため込んでおくイメージで。そしてエラーリカバリは一文字または一識別子スキップ。ただ、エラーの限界個数を設定できるようにして、それ超えたらRuntimeExceptionの子で大域脱出した方がよさげ。