【问题标题】:Antlr Handling ExceptionsAntlr 处理异常
【发布时间】:2012-11-17 10:59:50
【问题描述】:

我使用 Antlr 3 和 AST 树开发了一个复杂的语法。 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

但是,当我尝试取回异常消息时,它为空。我是否正确设置了所有内容?请参阅示例语法:

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; 
}
}

所以当我输入以下内容时: 从表中选择 *

没有';'我得到一个 MismatchedTokenException:

catch(Exception e) {
     System.out.println("Exception : " + " " e);
}

当我尝试时:

e.getMessage();

它返回空值。

【问题讨论】:

    标签: java error-handling antlr


    【解决方案1】:

    尝试改写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 产生消息“致命的不匹配令牌异常:预期';'但是是”。这个异常是由 ANTLR 直接产生的。

    输入 SELECT FROM table; 产生消息“其他异常:第 1:7 行:在 'FROM table' 处缺少 '*'”。这个异常是由上面的代码产生的。

    【讨论】:

    • 这也不起作用。当我尝试取回消息时,我像往常一样返回 null。
    • 请参阅上面的编辑以及我现在拥有的内容。也许我在这里做错了什么。我不想跟踪异常但实际打印它们。
    • 不确定这是否有帮助:stackoverflow.com/questions/4627244/…
    • @user1646481 打印错误是跟踪它们的一种形式。我更新了答案以表明并非所有错误都可以恢复,您必须单独考虑它们。
    【解决方案2】:

    如果我理解正确,您想处理您的语言语法错误。这就是我在项目中进行此设置的方式。

    /**
     * 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) 设置了我们的解析器。从那里我们可以检查我们的自定义代码中是否有错误。

    【讨论】:

    • 我还设置了一个自定义 TreeAdaptor,可以从 Tree 中获取数据。通过上面显示的编辑,我可以获得异常类型,但不能返回消息详细信息。
    • 这个答案,似乎没有回答问题。不知道该怎么做,因为我可以获得异常类的名称,但无法取回消息详细信息。
    • 如果您查看“ASTErrorNode”,它包含有关 RecognitionException Type/Text/ExceptionType 的所有信息
    • 我会卡住的。有没有另一种方法来代替自定义方法?
    • 我似乎不明白这一点。在我上次编辑时,它有什么问题?为什么它不起作用?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多