私は Antlr4 を使用しています。これが私が書いた簡略化された文法です。
grammar BooleanExpression;
/*******************************
* Parser Rules
*******************************/
booleanTerm
: booleanLiteral (KW_OR booleanLiteral)+
| booleanLiteral
;
id
: IDENTIFIER
;
booleanLiteral
: KW_TRUE
| KW_FALSE
;
/*******************************
* Lexer Rules
*******************************/
KW_TRUE
: 'true'
;
KW_FALSE
: 'false'
;
KW_OR
: 'or'
;
IDENTIFIER
: (SIMPLE_LATIN)+
;
fragment
SIMPLE_LATIN
: 'A' .. 'Z'
| 'a' .. 'z'
;
WHITESPACE
: [ \t\n\r]+ -> skip
;
以下のように BailErrorStategy と BailLexer を使用しました。
public class BailErrorStrategy extends DefaultErrorStrategy {
/**
* Instead of recovering from exception e, rethrow it wrapped in a generic
* IllegalArgumentException so it is not caught by the rule function catches.
* Exception e is the "cause" of the IllegalArgumentException.
*/
@Override
public void recover(Parser recognizer, RecognitionException e) {
throw new IllegalArgumentException(e);
}
/**
* Make sure we don't attempt to recover inline; if the parser successfully
* recovers, it won't throw an exception.
*/
@Override
public Token recoverInline(Parser recognizer) throws RecognitionException {
throw new IllegalArgumentException(new InputMismatchException(recognizer));
}
/** Make sure we don't attempt to recover from problems in subrules. */
@Override
public void sync(Parser recognizer) {
}
@Override
protected Token getMissingSymbol(Parser recognizer) {
throw new IllegalArgumentException(new InputMismatchException(recognizer));
}
}
public class BailLexer extends BooleanExpressionLexer {
public BailLexer(CharStream input) {
super(input);
//removeErrorListeners();
//addErrorListener(new ConsoleErrorListener());
}
@Override
public void recover(LexerNoViableAltException e) {
throw new IllegalArgumentException(e); // Bail out
}
@Override
public void recover(RecognitionException re) {
throw new IllegalArgumentException(re); // Bail out
}
}
1 つのケースを除いて、すべて正常に動作します。次の表現を試しました。
true OR false
「or」トークンは大文字ではなく小文字にする必要があるため、この式は拒否され、IllegalArgumentException がスローされると予想されます。しかし、Antlr4 はこの式を拒否せず、式は "KW_TRUE IDENTIFIER KW_FALSE" にトークン化されていることが判明しました (これは予想されます。大文字の 'OR' は IDENTIFIER と見なされます)。このトークン ストリームを処理し、「true」のみを含むツリーに解析し、残りの「IDENTIFIER KW_FALSE」トークンを破棄しました。さまざまな予測モードを試しましたが、すべて上記のように機能しました。なぜこのように動作し、デバッグを行ったのかわかりませんが、最終的に Antlr の次のコードにつながりました。
ATNConfigSet reach = computeReachSet(previous, t, false);
if ( reach==null ) {
// if any configs in previous dipped into outer context, that
// means that input up to t actually finished entry rule
// at least for SLL decision. Full LL doesn't dip into outer
// so don't need special case.
// We will get an error no matter what so delay until after
// decision; better error message. Also, no reachable target
// ATN states in SLL implies LL will also get nowhere.
// If conflict in states that dip out, choose min since we
// will get error no matter what.
int alt = getAltThatFinishedDecisionEntryRule(previousD.configs);
if ( alt!=ATN.INVALID_ALT_NUMBER ) {
// return w/o altering DFA
return alt;
}
throw noViableAlt(input, outerContext, previous, startIndex);
}
コード「int alt = getAltThatFinishedDecisionEntryRule(previousD.configs);」booleanTerm で 2 番目の選択肢を返しました (「true」は 2 番目の選択肢「booleanLiteral」と一致するため) が、ATN.INVALID_ALT_NUMBER と等しくないため、noViableAlt はすぐにはスローされません。そこにある Java のコメントによると、「どうしてもエラーが発生するので、決定後まで遅らせてください」とありますが、最終的にエラーはスローされなかったようです。
この場合、Antlrがエラーを報告する方法が本当にわかりません。これについて誰かが私に光を当てることができますか? どんな助けでも大歓迎です、ありがとう。