2012-11-29 61 views
7

Tôi đã phát triển một ngữ pháp phức tạp bằng cách sử dụng Antlr 3 sử dụng cây AST. ANTLR tạo ra Lexer và Parser. Vấn đề là khi người dùng nhập một cú pháp không hợp lệ ví dụ, ngữ pháp đang chờ ';'. Người dùng không nhập mã này, sau đó trong IDE Eclipse của tôi, tôi nhận được Ngoại lệ sau:Ngoại lệ xử lý Antlr

line 1:24 mismatched input '<EOF>' expecting ';' 

Làm thế nào ngoại lệ này có thể được xử lý vì tôi cố bắt ngoại lệ này, nhưng ngoại lệ không bị bắt. Đây có phải là ngoại lệ không? Tôi dường như không hiểu tại sao ngoại lệ này không bị bắt. Tôi đã cố gắng để tìm hiểu, tuy nhiên trang web Antlr dường như xuống một thời gian ngay bây giờ.

Tôi đã xem như sau: ANTLR exception handling with "$", Java và làm theo ví dụ đó, nhưng khi Lexer tạo mã bằng cách thêm RuntimeException(), tôi nhận được mã không thể truy cập được.

Tôi không biết phải làm gì.

Khi tôi cố gắng để có được số lượng các lỗi cú pháp từ phân tích cú pháp nó sẽ hiển thị 0.

EDIT:

Tôi đã tìm thấy một giải pháp mà hoạt động bằng cách nhìn vào: ANTLR not throwing errors on invalid input

Tuy nhiên, khi Tôi cố gắng để có được thông điệp ngoại lệ trở lại, nó là null. Tôi đã thiết lập mọi thứ chính xác chưa? Xin vui lòng xem mẫu ngữ pháp:

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()); 
    } 
}*/ 

EDIT:

Hãy xem những gì tôi có cho đến nay:

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

Vì vậy, khi tôi nhập như sau: SELECT * FROM tên_bảng

không a ';' Tôi nhận được một MismatchedTokenException:

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

Khi tôi cố gắng:

e.getMessage(); 

nó sẽ trả về null.

Trả lời

5

Hãy thử trọng displayRecognitionError thay vì:

@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 

Nếu bạn muốn theo dõi lỗi chứ không phải hủy bỏ, bạn có thể tạo một giao diện điều khiển để theo dõi chúng:

@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 

Các theo dõi lỗi sau đó có thể quyết định xem để ném một ngoại lệ hoặc tiếp tục.


Mã trên cho phép bạn theo dõi lỗi "có thể khôi phục", lỗi mà ANTLR có thể bỏ qua. Vẫn có các tình huống tạo ra lỗi không thể khôi phục, chẳng hạn như SELECT * FROM table (không có kết thúc ;). Trong trường hợp đó, bạn sẽ phải nắm bắt các ngoại lệ trong parseMQL hoặc ở đâu đó xung quanh. (Bạn có thể thử viết mã khôi phục của riêng mình, nhưng tôi không khuyên bạn nên làm.)

Đây là một sửa đổi parseMQL cho thấy hai loại lỗi phân tích cú pháp khác nhau. Lưu ý rằng tôi loại bỏ các cuộc gọi đến getMessage bởi vì không phải tất cả các trường hợp ngoại lệ bắt nguồn tắt của RecognitionException điền nó trong

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

Input SELECT * FROM table sản xuất thông báo "Fatal không phù hợp ngoại lệ mã thông báo:. Mong đợi ';' nhưng là <EOF> ". Ngoại lệ này được sản xuất trực tiếp bởi ANTLR.

Nhập SELECT FROM table; sản xuất thông báo "Ngoại lệ khác: dòng 1: 7: thiếu '*' tại 'FROM table'". Ngoại lệ này được tạo ra bởi mã ở trên.

+0

Điều này không hoạt động. Khi tôi cố gắng lấy lại tin nhắn, tôi sẽ lấy lại giá trị như bình thường. – user1646481

+0

Vui lòng xem chỉnh sửa ở trên với những gì tôi có ngay bây giờ. Có lẽ tôi đang làm điều gì sai ở đây. Tôi không muốn theo dõi các ngoại lệ nhưng thực sự in chúng. – user1646481

+0

Không chắc chắn nếu điều này giúp: http://stackoverflow.com/questions/4627244/catching-errors-in-antlr-and-finding-parent – user1646481

1

Nếu tôi hiểu chính xác bạn muốn xử lý lỗi cú pháp ngôn ngữ của mình. Đây là cách tôi có thiết lập này trên dự án của tôi.

/** 
* 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); 
    } 
} 

Đây là Lỗi Node

/** 
* 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(); 
    } 

} 

Và đây là cách này là tất cả dán lại với nhau.

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

Chúng tôi chỉ cần tạo trình phân tích cú pháp và trình phân tích cú pháp sau đó chúng tôi thiết lập trình phân tích cú pháp với bộ điều hợp cây tùy chỉnh (PhantomTreeAdaptor). Từ đó, chúng tôi có thể kiểm tra xem chúng tôi có lỗi trong mã tùy chỉnh của chúng tôi hay không.

+0

Tôi cũng đã thiết lập TreeAdaptor tùy chỉnh và có thể lấy lại dữ liệu từ Cây. Với chỉnh sửa của tôi được hiển thị ở trên, tôi có thể nhận được loại Ngoại lệ, nhưng không thể trả lại chi tiết tin nhắn. – user1646481

+0

Câu trả lời này dường như không trả lời được vấn đề. Bạn không chắc chắn phải làm gì vì tôi có thể lấy tên của lớp Ngoại lệ, nhưng không thể lấy lại chi tiết thư. – user1646481

+0

Nếu bạn nhìn 'ASTErrorNode', nó chứa tất cả thông tin liên quan đến Loại nhận dạng ngoại lệ/Kiểu/Ngoại lệ của loại nhận dạng tất cả trong đó – Greg