#java #error-handling #antlr4
#java #обработка ошибок #antlr4
Вопрос:
Я создаю систему компьютерной алгебры, которая будет принимать алгебраическое выражение и упрощать или дифференцировать его.
Как вы можете видеть из следующего кода, пользовательский ввод принимается, но если это строка, которая не соответствует моим правилам грамматики, возникает ошибка, строка 1: 6 несоответствующий ввод «ожидание {‘(‘, INT, VAR}, и программа продолжает работать.
Как мне перехватить ошибку и остановить запуск программы? Заранее благодарю вас за любую помощь.
Класс контроллера:
public static void main(String[] args) throws IOException {
String userInput = "x*x*x ";
getAST(userInput);
}
public static AST getAST(String userInput) {
ParseTree tree = null;
ExpressionLexer lexer = null;
ANTLRInputStream input = new ANTLRInputStream(userInput);
try {
lexer = new ExpressionLexer(input);
}catch(Exception e) {
System.out.println("Incorrect grammar");
}
System.out.println("Lexer created");
CommonTokenStream tokens = new CommonTokenStream(lexer);
System.out.println("Tokens created");
ExpressionParser parser = new ExpressionParser(tokens);
System.out.println("Tokens parsed");
tree = parser.expr();
System.out.println("Tree created");
System.out.println(tree.toStringTree(parser)); // print LISP-style tree
Trees.inspect(tree, parser);
ParseTreeWalker walker = new ParseTreeWalker();
ExpressionListener listener = new buildAST();
walker.walk(listener, tree);
listener.printAST();
listener.extractExpression();
return new AST();
}
}
Моя Грамматика:
grammar Expression;
@header {
package exprs;
}
@members {
// This method makes the parser stop running if it encounters
// invalid input and throw a RuntimeException.
public void reportErrorsAsExceptions() {
//removeErrorListeners();
addErrorListener(new ExceptionThrowingErrorListener());
}
private static class ExceptionThrowingErrorListener extends BaseErrorListener {
@Override
public void syntaxError(Recognizer<?, ?> recognizer,
Object offendingSymbol, int line, int charPositionInLine,
String msg, RecognitionException e) {
throw new RuntimeException(msg);
}
}
}
@rulecatch {
// ANTLR does not generate its normal rule try/catch
catch(RecognitionException e) {
throw e;
}
}
expr : left=expr op=('*'|'/'|'^') right=expr
| left=expr op=(' '|'-') right=expr
| '(' expr ')'
| atom
;
atom : INT|VAR;
INT : ('0'..'9') ;
VAR : ('a' .. 'z') | ('A' .. 'Z') | '_';
WS : [ trn] -> skip ;
Комментарии:
1. Вы определили
reportErrorsAsException
метод, но я не вижу, чтобы вы его где-нибудь вызывали. В любом случае, вы действительно хотите прервать синтаксический анализ при первой синтаксической ошибке? Или вы просто хотите, чтобы ваш слушатель не выполнялся при наличии синтаксических ошибок?2. @sepp2k Я попытался поймать ошибку после различных строк в основном классе, но это не сработало. Я хочу полностью прервать работу, а затем снова запустить программу, запрашивая у пользователя допустимый ввод.
Ответ №1:
Типичный запуск синтаксического анализа с ANTLR4 состоит из 2 этапов:
- «Быстрый и грязный» запуск с режимом прогнозирования SLL, который отключается при первой обнаруженной синтаксической ошибке.
- Обычный запуск с использованием режима прогнозирования LL, который пытается восстановиться после ошибок синтаксического анализатора. Этот второй шаг необходимо выполнить только в том случае, если на первом шаге произошла ошибка.
Первый шаг — это своего рода свободный синтаксический анализ, который не устраняет определенные неоднозначности и, следовательно, может сообщать об ошибке, которой на самом деле не существует (при разрешении в режиме LL). Но первый шаг выполняется быстрее и обеспечивает более быстрый результат для синтаксически правильного ввода. Этот (JS) код показывает настройку:
this.parser.removeErrorListeners();
this.parser.addErrorListener(this.errorListener);
this.parser.errorHandler = new BailErrorStrategy();
this.parser.interpreter.setPredictionMode(PredictionMode.SLL);
try {
this.tree = this.parser.grammarSpec();
} catch (e) {
if (e instanceof ParseCancellationException) {
this.tokenStream.seek(0);
this.parser.reset();
this.parser.errorHandler = new DefaultErrorStrategy();
this.parser.interpreter.setPredictionMode(PredictionMode.LL);
this.tree = this.parser.grammarSpec();
} else {
throw e;
}
}
Чтобы избежать любых попыток устранения синтаксических ошибок на первом шаге, вы также должны установить BailErrorStrategy
. Эта стратегия просто выдает a ParseCancellationException
в случае синтаксической ошибки (аналогично тому, что вы делаете в своем коде). Вы можете добавить свою собственную обработку в предложение catch, чтобы запросить у пользователя правильный ввод и повторить этап синтаксического анализа.