Я использую 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
}
}
Все работает нормально, кроме одного случая. Я попробовал следующее выражение:
true OR false
Я ожидаю, что это выражение будет отклонено, и будет выдано исключение IllegalArgumentException, потому что токен «или» должен быть в нижнем регистре, а не в верхнем. Но оказалось, что Antlr4 не отклонил это выражение, и выражение токенизировано в «KW_TRUE IDENTIFIER KW_FALSE» (что и ожидалось, верхний регистр «ИЛИ» будет рассматриваться как 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 (поскольку «true» соответствует второй альтернативе «booleanLiteral»), но, поскольку она не равна ATN.INVALID_ALT_NUMBER, noViableAlt не выбрасывается немедленно. Согласно комментариям к Java: «Мы получим ошибку, несмотря ни на что, поэтому отложите до принятия решения», но, похоже, в конечном итоге ошибка не возникла.
Я действительно понятия не имею, как заставить Antlr сообщать об ошибке в этом случае, может ли кто-нибудь пролить на это свет? Любая помощь приветствуется, спасибо.
parse : booleanTerm EOF;
- person Bart Kiers   schedule 28.02.2013BailErrorStrategy
? - person Sam Harwell   schedule 28.02.2013