2012-11-29 74 views
7

我使用AST樹開發了一個使用Antlr 3的複雜語法。 ANTLR生成Lexer和Parser。問題是,當用戶輸入一個無效的語法時,語法期望爲';'。用戶不會在我的Eclipse IDE進入這個,然後我得到以下異常:Antlr處理異常

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

所以,當我輸入以下內容: SELECT * FROM表

無一個 ';'我得到的一個MismatchedTokenException異常:

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

當我嘗試:

e.getMessage(); 

返回null。

回答

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產生消息「致命錯配的令牌例外:預期‘;’但是是<EOF>「。這個例外是由ANTLR直接產生的。

輸入SELECT FROM table;產生消息 「的其他異常:第1行:7:錯過 '*' 在 'FROM表'」。這個例外是由上面的代碼產生的。

+0

這也行不通。當我嘗試重新獲取消息時,我照常收到null。 – user1646481

+0

請參閱上面的編輯,我現在有。也許我在這裏做錯了什麼。我不想跟蹤例外,但實際上打印它們。 – user1646481

+0

不知道這是否有助於:http://stackoverflow.com/questions/4627244/catching-errors-in-antlr-and-finding-parent – user1646481

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)解析器。從那裏我們可以檢查我們的自定義代碼是否有錯誤。

+0

我也有設置自定義TreeAdaptor,並能得到數據從樹回來。通過上面顯示的編輯,我可以獲取Exception的類型,但不能返回消息詳細信息。 – user1646481

+0

這個答案似乎並沒有回答這個問題。不知道該怎麼做,因爲我可以獲取Exception類的名稱,但無法獲取消息詳細信息。 – user1646481

+0

如果你看看「ASTErrorNode」它包含關於RecognitionException類型/文本/ ExceptionType它都在那裏 – Greg