2015-04-23 17 views
12

Tôi có ngữ pháp ANLTR đơn giản và Khách truy cập đi kèm. Mọi thứ hoạt động tốt, trừ khi đầu vào không hợp lệ. Nếu đầu vào không hợp lệ, lỗi sẽ bị nuốt và máy tính của tôi xuất hiện với đầu ra sai.Có thể ném ngoại lệ nếu đầu vào không hợp lệ không?

Tôi đã thử triển khai trình nghe lỗi, qua phương pháp Recover của từ khóa, và ... cũng ... nửa tá những thứ khác hiện nay. Ai đó có thể chỉ cho tôi cách chỉ đơn giản là ném một lỗi thay vì nuốt "thẻ" xấu? (. Tôi sử dụng dấu ngoặc kép bởi vì họ đang không tokens ở tất cả các ký tự được định nghĩa trong ngữ pháp của tôi.)

trị Input:

1 + 2 * 3 - 4

Invalid Input:

1 + 2 + 3 (4)

Tôi muốn ném ArgumentException nếu trình phân tích cú pháp/lexer xuất hiện trên dấu ngoặc đơn (hoặc bất kỳ ký tự không xác định nào khác). Hiện tại, các ký tự không hợp lệ dường như chỉ biến mất thành ête và trình phân tích cú pháp chỉ đơn giản như không có gì sai.

Nếu tôi chạy nó trong bảng điều khiển với lệnh grun, tôi nhận được kết quả sau, do đó, nó nhận ra các mã thông báo không hợp lệ ở một mức nào đó.

dòng 1: 9 lỗi thẻ nhận tại địa chỉ: '('

dòng 1:11 thẻ lỗi công nhận tại địa chỉ: ')'

và kết quả là cây phân tích cú pháp này.

enter image description here

BasicMath.g4

grammar BasicMath; 

/* 
* Parser Rules 
*/ 

compileUnit : expression+ EOF; 

expression : 
    expression MULTIPLY expression #Multiplication 
    | expression DIVIDE expression #Division 
    | expression ADD expression #Addition 
    | expression SUBTRACT expression #Subtraction 
    | NUMBER #Number 
    ; 

/* 
* Lexer Rules 
*/ 

NUMBER : INT; //Leave room to extend what kind of math we can do. 

INT : ('0'..'9')+; 
MULTIPLY : '*'; 
DIVIDE : '/'; 
SUBTRACT : '-'; 
ADD : '+'; 

WS : [ \t\r\n] -> channel(HIDDEN); 

Calculator:

public static class Calculator 
{ 
    public static int Evaluate(string expression) 
    { 
     var lexer = new BasicMathLexer(new AntlrInputStream(expression)); 
     var tokens = new CommonTokenStream(lexer); 
     var parser = new BasicMathParser(tokens); 

     var tree = parser.compileUnit(); 

     var visitor = new IntegerMathVisitor(); 

     return visitor.Visit(tree); 
    } 
} 
+0

Hãy xem câu trả lời này từ tác giả Antlr4cs: http://stackoverflow.com/a/18486405/2573395 – Alex

+0

Yup. Đã thử rằng @Alex. Tôi được kế thừa từ 'BaseErrorListener' và đính kèm nó vào trình phân tích cú pháp của tôi, nhưng không có phương thức nào được gọi. – RubberDuck

+0

Lưu ý về bản thân, khi cưỡi một thứ gì đó ở đây có thể hữu ích. Có vẻ như độ dài lớn đã biến mất để đảm bảo phân tích cú pháp hoàn thành khi tôi cần nó dừng lại. https://github.com/antlr/antlr4/blob/master/runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy.java – RubberDuck

Trả lời

5

@CoronA was right. The error happens in the lexer..Vì vậy, trong khi tôi vẫn nghĩ rằng việc tạo ra một ErrorStrategy sẽ là tốt hơn, đây là những gì thực sự làm việc cho tôi và mục tiêu của tôi ném một ngoại lệ cho đầu vào không xác định.

Trước tiên, tôi đã tạo ra một lớp được thừa kế được thừa kế từ BaseErrorListener cụ IAntlrErrorListener<T>. Phần thứ hai là vấn đề của tôi. Bởi vì khách truy cập của tôi được kế thừa từ FooBarBaseVistor<int>, trình xử lý lỗi của tôi cũng cần phải thuộc loại để đăng ký nó với lexer của tôi.

class ThrowExceptionErrorListener : BaseErrorListener, IAntlrErrorListener<int> 
{ 
    //BaseErrorListener implementation; not called in my test, but left it just in case 

    public override void SyntaxError(IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e) 
    { 
     throw new ArgumentException("Invalid Expression: {0}", msg, e); 
    } 

    //IAntlrErrorListener<int> implementation; this one actually gets called. 

    public void SyntaxError(IRecognizer recognizer, int offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e) 
    { 
     throw new ArgumentException("Invalid Expression: {0}", msg, e); 
    } 
} 

Và thay đổi lớp Calculator tôi để đính kèm listener lỗi tùy chỉnh của tôi vào lexer. Lưu ý rằng bạn không phải xóa ConsoleListener như tôi đã làm cho lỗi thực sự bị ném. Vì tôi không thực sự sử dụng nó, tôi đã tìm ra cách tốt nhất để tiếp tục và làm như vậy.

public static class Calculator 
{ 
    public static int Evaluate(string expression) 
    { 
     var lexer = new BasicMathLexer(new AntlrInputStream(expression)); 
     lexer.RemoveErrorListeners(); //removes the default console listener 
     lexer.AddErrorListener(new ThrowExceptionErrorListener()); 

     var tokens = new CommonTokenStream(lexer); 
     var parser = new BasicMathParser(tokens); 

     var tree = parser.compileUnit(); 

     var visitor = new IntegerMathVisitor(); 

     return visitor.Visit(tree); 
    } 
} 

Và đó là nó. Một ngoại lệ đối số được ném và thử nghiệm này hiện đã qua.

[TestMethod] 
    [ExpectedException(typeof(ArgumentException))] 
    public void BadInput() 
    { 
     var expr = "1 + 5 + 2(3)"; 
     int value = Calculator.Evaluate(expr); 
    } 

Một lưu ý cuối cùng. Nếu bạn ném một số RecognitionException vào đây, nó sẽ bị nuốt lại lần nữa. ParseCancelationException được khuyến nghị, vì nó không xuất phát từ RecognitionException, nhưng tôi chọn một ArgumentException vì tôi cảm thấy có ý nghĩa nhất đối với mã C# của khách hàng.

10

Trên thực tế mỗi thông báo lỗi là do một ngoại lệ. Ngoại lệ này bị bắt và trình phân tích cú pháp cố gắng khôi phục. Cây phân tích cú pháp là kết quả của quá trình khôi phục.

Vì lỗi xảy ra trong lexer (chỉ lexer không biết các ký tự ( hoặc )), việc xử lý lỗi phải được gắn vào lexer. Trong Java, giao diện này sẽ trông giống như:

lexer.addErrorListener(new BaseErrorListener() { 
     @Override 
     public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { 
      throw new RuntimeException(e); 
     } 
    }); 

Cú pháp C# không được cách xa điều đó. Tuy nhiên, tôi khuyên bạn không nên ném một ngoại lệ. Thu thập tốt hơn các lỗi vào một danh sách và báo cáo chúng sau khi lexer hoàn tất và không bắt đầu phân tích cú pháp nếu danh sách các lỗi không rỗng.

+0

'BailErrorStrategy' cũng không đưa ra bất kỳ ngoại lệ nào. Tôi nhận được kết quả tương tự với nó như tôi làm với 'DefaultErrorStrategy' – RubberDuck

+0

Tôi đã nhầm lẫn. Trên thực tế phân tích cú pháp và lexer được tách biệt hoàn toàn trong ANTLR, vì vậy giải pháp đầu tiên của tôi để sử dụng một ErrorStrategy trên trình phân tích cú pháp sẽ không hoạt động. Tuy nhiên, việc gắn một người nghe vào lexer sẽ làm điều đó. Tôi đã sửa chữa câu trả lời của mình để mô tả giải pháp – CoronA

+0

Giải quyết nó nhờ sự thúc đẩy của bạn đi đúng hướng. Cảm ơn nhiều. – RubberDuck

Các vấn đề liên quan