我使用AST树使用Antlr 3开发了一个复杂的语法。 ANTLR生成Lexer和Parser。问题是当用户输入例如无效的语法时,语法期望';'。用户不输入,然后在我的Eclipse IDE中得到以下异常:
line 1:24 mismatched input '<EOF>' expecting ';'
如何处理此异常,因为我尝试捕获此异常,但未捕获异常。这是一个例外吗?我似乎不明白为什么没有捕获此异常。我试图找出答案,但Antlr网站现在似乎已经停止了一段时间。
我查看了以下内容:ANTLR exception handling with "$", Java并按照该示例进行操作,但是当Lexer通过添加RuntimeException()生成代码时,我得到了无法访问的代码。
我不知道该怎么做。
当我尝试从解析器中获取语法错误的数量时,它显示为0.
编辑:
我找到了一个解决方案,通过查看:ANTLR not throwing errors on invalid input
但是,当我尝试获取Exception消息时,它为null。我是否正确设置了一切?请参阅示例语法:
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();
它返回null。
答案 0 :(得分:5)
尝试改为覆盖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
会产生消息“致命的不匹配令牌异常:预期';'但是&lt; EOF&gt;“。此异常由ANTLR直接生成。
输入SELECT FROM table;
会在“FROM table”中生成消息“其他异常:第1行:第7行:缺少'*'。”此异常由上面的代码生成。
答案 1 :(得分:1)
如果我理解正确,您希望处理语言语法错误。这就是我在项目中进行此设置的方法。
/**
* 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)设置解析器。从那里我们可以检查我们的自定义代码是否有错误。