Antlr Обработка исключений
Я разработал сложную грамматику, используя Antlr 3, используя дерево AST. ANTLR генерирует Lexer и Parser. Проблема в том, что когда пользователь вводит синтаксис, который, например, недействителен, грамматика ожидает ";". Пользователь не вводит это, тогда в моей Eclipse IDE я получаю следующее исключение:
line 1:24 mismatched input '<EOF>' expecting ';'
Как это исключение может быть обработано, потому что я пытаюсь поймать это исключение, но исключение не перехватывается. Это вообще исключение? Я не понимаю, почему это исключение не пойман. Я пытался это выяснить, однако веб-сайт Antlr, похоже, не работает в течение некоторого времени.
Я посмотрел на следующее: обработка исключений ANTLR с помощью "$", Java и следовал этому примеру, но когда Lexer генерирует код, добавляя RuntimeException (), я получаю недоступный код.
Я не уверен что делать.
Когда я пытаюсь получить количество синтаксических ошибок от парсера, отображается 0.
РЕДАКТИРОВАТЬ:
Я нашел решение, которое работает, глядя на: ANTLR не выбрасывает ошибки на неверный ввод
Однако, когда я пытаюсь вернуть сообщение об исключении, оно становится пустым. Я все правильно настроил? Пожалуйста, посмотрите образец грамматики:
grammar i;
options {
output=AST;
}
@header {
package com.data;
}
@rulecatch {
catch(RecognitionException e) {
throw e;
}
}
// by having these below it makes no difference
/**@parser::members {
@Override
public void reportError(RecognitionException e) {
throw new RuntimeException("Exception : " + " " + e.getMessage());
}
}
@lexer::members {
@Override
public void reportError(RecognitionException e) {
throw new RuntimeException("Exception : " + " " + e.getMessage());
}
}*/
РЕДАКТИРОВАТЬ:
Пожалуйста, посмотрите, что я имею до сих пор:
grammar i;
options {
output=AST;
}
@header {
package com.data;
}
@rulecatch {
// ANTLR does not generate its normal rule try/catch
catch(RecognitionException e) {
throw e;
}
}
@parser::members {
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
throw new RuntimeException(hdr + ":" + msg);
}
}
@lexer::members {
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
throw new RuntimeException(hdr + ":" + msg);
}
}
operatorLogic : 'AND' | 'OR';
value : STRING;
query : (select)*;
select : 'SELECT'^ functions 'FROM table' filters?';';
operator : '=' | '!=' | '<' | '>' | '<=' | '>=';
filters : 'WHERE'^ conditions;
members : STRING operator value;
conditions : (members (operatorLogic members)*);
functions : '*';
STRING : ('a'..'z'|'A'..'Z')+;
WS : (' '|'\t'|'\f'|'\n'|'\r')+ {skip();}; // handle white space between keywords
public class Processor {
public Processor() {
}
/**
* This method builds the MQL Parser.
* @param args the args.
* @return the built IParser.
*/
private IParser buildMQLParser(String query) {
CharStream cs = new ANTLRStringStream(query);
// the input needs to be lexed
ILexer lexer = new ILexer(cs);
CommonTokenStream tokens = new CommonTokenStream();
IParser parser = new IParser(tokens);
tokens.setTokenSource(lexer);
// use the ASTTreeAdaptor so that the grammar is aware to build tree in AST format
parser.setTreeAdaptor((TreeAdaptor) new ASTTreeAdaptor().getASTTreeAdaptor());
return parser;
}
/**
* This method parses the MQL query.
* @param query the query.
*/
public void parseMQL(String query) {
IParser parser = buildMQLParser(query);
CommonTree commonTree = null;
try {
commonTree = (CommonTree) parser.query().getTree();
}
catch(Exception e) {
System.out.println("Exception :" + " " + e.getMessage());
}
}
}
public class ASTTreeAdaptor {
public ASTTreeAdaptor() {
}
/**
* This method is used to create a TreeAdaptor.
* @return a treeAdaptor.
*/
public Object getASTTreeAdaptor() {
TreeAdaptor treeAdaptor = new CommonTreeAdaptor() {
public Object create(Token payload) {
return new CommonTree(payload);
}
};
return treeAdaptor;
}
}
Поэтому, когда я ввожу следующее: SELECT * FROM table
без ';' Я получаю исключение MismatchedTokenException:
catch(Exception e) {
System.out.println("Exception : " + " " e);
}
Когда я пытаюсь:
e.getMessage();
он возвращает ноль.
2 ответа
Попробуйте переопределить displayRecognitionError
вместо:
@parser::members {
...
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
throw new RuntimeException(hdr + ":" + msg);
}
...
}
//same code in @lexer::members
Если вы хотите отслеживать ошибки, а не прерывать их, вы можете создать интерфейс обработчика для отслеживания их:
@parser::members {
...
private YourErrorTrackerInterface errorTracker;
//getter/setter for errorTracker here
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
if (errorTracker != null){
errorTracker.addError(e, tokenNames, hdr, msg);
}
}
...
}
//same code in @lexer::members
Затем средство отслеживания ошибок может решить, выдать ли исключение или продолжить.
Приведенный выше код позволяет отслеживать "исправимые" ошибки, ошибки, которые ANTLR может пропустить. Есть все еще сценарии, которые производят неисправимые ошибки, такие как SELECT * FROM table
(без окончания ;
). В этом случае вам придется ловить исключения в parseMQL
или где-то там. (Вы можете попробовать написать свой собственный код восстановления, но я бы не рекомендовал это делать.)
Вот модифицированный parseMQL
это показывает два разных типа ошибок синтаксического анализа. Обратите внимание, что я удалил вызов getMessage
потому что не все исключения получены из RecognitionException
заполните это.
public void parseMQL(String query) {
iParser parser = buildMQLParser(query);
CommonTree commonTree = null;
try {
commonTree = (CommonTree) parser.query().getTree();
} catch (MismatchedTokenException e){
//not production-quality code, just forming a useful message
String expected = e.expecting == -1 ? "<EOF>" : iParser.tokenNames[e.expecting];
String found = e.getUnexpectedType() == -1 ? "<EOF>" : iParser.tokenNames[e.getUnexpectedType()];
System.out.println("Fatal mismatched token exception: expected " + expected + " but was " + found);
} catch (RecognitionException e) {
System.out.println("Fatal recognition exception " + e.getClass().getName()
+ " : " + e);
} catch (Exception e) {
System.out.println("Other exception : " + e.getMessage());
}
}
вход SELECT * FROM table
выдает сообщение "Исключительная фатальная ошибка несоответствия токена: ожидается"; но был
вход SELECT FROM table;
выдает сообщение "Другое исключение: строка 1:7: отсутствует" * "в таблице" ИЗ ". Это исключение было создано с помощью кода выше.
Если я правильно понимаю, вы хотите обрабатывать синтаксические ошибки вашего языка. Вот как у меня есть эта настройка в моем проекте.
/**
* Adapter need for ANTL to recognize our custom nodes
*
* @author Greg
*/
public class PhantomTreeAdaptor extends CommonTreeAdaptor{
@Override
public Object create(Token payload){
return new ASTNode(payload);
}
@Override
public Object dupNode(Object old){
return (old == null) ? null : ((ASTNode) old).dupNode();
}
@Override
public Object errorNode(TokenStream input, Token start, Token stop, RecognitionException e){
return new ASTErrorNode(input, start, stop, e);
}
}
Вот узел ошибки
/**
* This is our custom Error node used by the adapter.
*
* @author Greg
*/
public class ASTErrorNode extends ASTNode {
org.antlr.runtime.tree.CommonErrorNode delegate;
public ASTErrorNode(TokenStream input, Token start, Token stop, RecognitionException e) {
delegate = new CommonErrorNode(input, start, stop, e);
}
public boolean isNil() {
return delegate.isNil();
}
public int getType() {
return delegate.getType();
}
public String getText() {
return delegate.getText();
}
public String toString() {
return delegate.toString();
}
}
И вот как все это склеено.
final PhantomSQLLexer lex = new PhantomSQLLexer(input);
final CommonTokenStream tokens = new CommonTokenStream(lex);
final PhantomSQLParser g = new PhantomSQLParser(tokens);
g.setTreeAdaptor(new PhantomTreeAdaptor());
final start_rule_return r = g.start_rule();
if (g.getNumberOfSyntaxErrors() == 0) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("tree=" + ((Tree) r.tree).toStringTree());
LOGGER.debug("-------------------------------------------");
}
final ASTNode root = r.tree;
exec(root);
}
else {
LOGGER.debug("Error parsing input");
}
Мы просто создали наш лексер и анализатор, а затем настроили наш анализатор с помощью специального адаптера дерева (PhantomTreeAdaptor). Оттуда мы можем проверить, есть ли у нас ошибки в нашем пользовательском коде.