2009-12-18 96 views
192

Tôi muốn bắt đầu với ANTLR, nhưng sau một vài giờ xem lại các ví dụ tại trang web antlr.org, tôi vẫn không thể hiểu rõ về ngữ pháp đối với quy trình Java.ANTLR: Có một ví dụ đơn giản không?

Có một số ví dụ đơn giản, một cái gì đó giống như một máy tính bốn hoạt động được thực hiện với ANTLR đi qua định nghĩa phân tích cú pháp và tất cả các cách để mã nguồn Java?

+2

Ví dụ chính xác đó được sử dụng làm hướng dẫn trên trang web của Antlr, cuối cùng tôi đã kiểm tra. –

+1

@Cory Petosky: bạn có thể cung cấp liên kết này không? – Eli

+2

Tôi cũng chia sẻ tìm kiếm của bạn. –

Trả lời

393

Trước tiên, bạn tạo ngữ pháp. Dưới đây là một ngữ pháp nhỏ mà bạn có thể sử dụng để đánh giá các biểu thức được xây dựng bằng cách sử dụng 4 toán tử toán cơ bản: +, -, * và /. Bạn cũng có thể nhóm các biểu thức bằng dấu ngoặc đơn. Lưu ý rằng ngữ pháp này chỉ là một văn bản rất cơ bản: nó không xử lý các toán tử đơn nhất (trừ: -1 + 9) hoặc số thập phân như 0,99 (không có số đầu), để đặt tên chỉ là hai thiếu sót. Đây chỉ là một ví dụ bạn có thể làm việc trên chính mình.

Đây là nội dung của file ngữ pháp Exp.g:

grammar Exp; 

/* This will be the entry point of our parser. */ 
eval 
    : additionExp 
    ; 

/* Addition and subtraction have the lowest precedence. */ 
additionExp 
    : multiplyExp 
     ('+' multiplyExp 
     | '-' multiplyExp 
     )* 
    ; 

/* Multiplication and division have a higher precedence. */ 
multiplyExp 
    : atomExp 
     ('*' atomExp 
     | '/' atomExp 
     )* 
    ; 

/* An expression atom is the smallest part of an expression: a number. Or 
    when we encounter parenthesis, we're making a recursive call back to the 
    rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */ 
atomExp 
    : Number 
    | '(' additionExp ')' 
    ; 

/* A number: can be an integer value, or a decimal value */ 
Number 
    : ('0'..'9')+ ('.' ('0'..'9')+)? 
    ; 

/* We're going to ignore all white space characters */ 
WS 
    : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} 
    ; 

(quy tắc Parser bắt đầu với một lá thư chữ thường, và các quy tắc lexer bắt đầu với một chữ cái viết hoa)

Sau tạo ngữ pháp, bạn sẽ muốn tạo một trình phân tích cú pháp và lexer từ nó. Tải xuống ANTLR jar và lưu trữ nó trong cùng thư mục với tệp ngữ pháp của bạn.

Thực hiện lệnh sau trên vỏ/command prompt của bạn:

java -cp antlr-3.2.jar org.antlr.Tool Exp.g 

Nó không nên tạo ra bất kỳ thông báo lỗi, và các tập tin ExpLexer.java, ExpParser.javaExp.tokens bây giờ sẽ được tạo ra.

Để xem nếu nó tất cả các hoạt động đúng, tạo lớp thử nghiệm này:

import org.antlr.runtime.*; 

public class ANTLRDemo { 
    public static void main(String[] args) throws Exception { 
     ANTLRStringStream in = new ANTLRStringStream("12*(5-6)"); 
     ExpLexer lexer = new ExpLexer(in); 
     CommonTokenStream tokens = new CommonTokenStream(lexer); 
     ExpParser parser = new ExpParser(tokens); 
     parser.eval(); 
    } 
} 

và biên dịch nó:

// *nix/MacOS 
javac -cp .:antlr-3.2.jar ANTLRDemo.java 

// Windows 
javac -cp .;antlr-3.2.jar ANTLRDemo.java 

và sau đó chạy nó:

// *nix/MacOS 
java -cp .:antlr-3.2.jar ANTLRDemo 

// Windows 
java -cp .;antlr-3.2.jar ANTLRDemo 

Nếu mọi việc suôn sẻ tốt, không có gì được in trên bảng điều khiển. Điều này có nghĩa là trình phân tích cú pháp không tìm thấy bất kỳ lỗi nào. Khi bạn thay đổi "12*(5-6)" vào "12*(5-6" và sau đó biên dịch lại và chạy nó, có nên được in như sau:

line 0:-1 mismatched input '<EOF>' expecting ')' 

Được rồi, bây giờ chúng tôi muốn thêm một chút mã Java để ngữ pháp để phân tích cú pháp thực sự làm điều gì đó hữu ích . Việc thêm mã có thể được thực hiện bằng cách đặt {} bên trong ngữ pháp của bạn với một số mã Java đơn giản bên trong nó.

Nhưng trước tiên: tất cả quy tắc trình phân tích cú pháp trong tệp ngữ pháp phải trả lại giá trị kép nguyên thủy.Bạn có thể làm điều đó bằng cách thêm returns [double value] sau mỗi quy tắc:

grammar Exp; 

eval returns [double value] 
    : additionExp 
    ; 

additionExp returns [double value] 
    : multiplyExp 
     ('+' multiplyExp 
     | '-' multiplyExp 
     )* 
    ; 

// ... 

mà cần ít lời giải thích: mọi quy tắc được dự kiến ​​sẽ trả về một giá trị gấp đôi. Bây giờ để "tương tác" với giá trị trả về double value (đó là KHÔNG bên trong một khối mã Java đơn giản {...}) từ bên trong một khối mã, bạn sẽ cần phải thêm một dấu đô la trước value:

grammar Exp; 

/* This will be the entry point of our parser. */ 
eval returns [double value]             
    : additionExp { /* plain code block! */ System.out.println("value equals: "+$value); } 
    ; 

// ... 

Dưới đây là ngữ pháp nhưng bây giờ với mã Java thêm:

grammar Exp; 

eval returns [double value] 
    : exp=additionExp {$value = $exp.value;} 
    ; 

additionExp returns [double value] 
    : m1=multiplyExp  {$value = $m1.value;} 
     ('+' m2=multiplyExp {$value += $m2.value;} 
     | '-' m2=multiplyExp {$value -= $m2.value;} 
     )* 
    ; 

multiplyExp returns [double value] 
    : a1=atomExp  {$value = $a1.value;} 
     ('*' a2=atomExp {$value *= $a2.value;} 
     | '/' a2=atomExp {$value /= $a2.value;} 
     )* 
    ; 

atomExp returns [double value] 
    : n=Number    {$value = Double.parseDouble($n.text);} 
    | '(' exp=additionExp ')' {$value = $exp.value;} 
    ; 

Number 
    : ('0'..'9')+ ('.' ('0'..'9')+)? 
    ; 

WS 
    : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} 
    ; 

và kể từ khi quy tắc eval chúng tôi bây giờ trả về một đôi, thay đổi ANTLRDemo.java của bạn vào đây:

import org.antlr.runtime.*; 

public class ANTLRDemo { 
    public static void main(String[] args) throws Exception { 
     ANTLRStringStream in = new ANTLRStringStream("12*(5-6)"); 
     ExpLexer lexer = new ExpLexer(in); 
     CommonTokenStream tokens = new CommonTokenStream(lexer); 
     ExpParser parser = new ExpParser(tokens); 
     System.out.println(parser.eval()); // print the value 
    } 
} 

Again (lại) tạo ra một lexer tươi và phân tích cú pháp từ ngữ pháp của bạn (1), biên dịch tất cả các lớp (2) và chạy ANTLRDemo (3):

// *nix/MacOS 
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1 
javac -cp .:antlr-3.2.jar ANTLRDemo.java  // 2 
java -cp .:antlr-3.2.jar ANTLRDemo   // 3 

// Windows 
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1 
javac -cp .;antlr-3.2.jar ANTLRDemo.java  // 2 
java -cp .;antlr-3.2.jar ANTLRDemo   // 3 

và bây giờ bạn sẽ thấy kết quả của biểu thức 12*(5-6) được in trên bảng điều khiển của bạn!

Một lần nữa: đây là giải thích rất ngắn gọn. Tôi khuyến khích bạn duyệt qua ANTLR wiki và đọc một số hướng dẫn và/hoặc chơi một chút với những gì tôi vừa mới đăng.

Chúc may mắn!

EDIT:

This post cho thấy làm thế nào để mở rộng ví dụ trên để cho một Map<String, Double> thể được cung cấp chứa các biến trong biểu thức cung cấp.

Và điều này Q&A chứng tỏ cách tạo trình phân tích cú pháp biểu thức đơn giản và bộ đánh giá bằng cách sử dụng ANTLR4.

Để làm cho mã này hoạt động với phiên bản Antlr hiện tại (tháng 6 năm 2014), tôi cần thực hiện một vài thay đổi. ANTLRStringStream cần để trở thành ANTLRInputStream, giá trị trả lại cần thiết để thay đổi từ parser.eval() thành parser.eval().value và tôi cần xóa mệnh đề WS ở cuối, vì các giá trị thuộc tính như $channel không còn được phép xuất hiện trong các hành động lexer.

+1

Các hiện thực của 'parser.eval()' xảy ra ở đâu? Điều đó không rõ ràng TẠI ĐÂY hoặc trên Wiki ANTLR3! –

+1

@Jarrod, err, xin lỗi, tôi không thực sự hiểu bạn. 'eval' là một quy tắc phân tích cú pháp trả về một' double'. Vì vậy, có một phương thức 'eval()' mà bạn có thể gọi trên một cá thể của một 'ExpParser', giống như tôi đã trình bày trong' ANTLRDemo.main (...) '. Sau khi tạo một lexer/parser, chỉ cần mở tệp 'ExpParser.java' và bạn sẽ thấy có một phương thức' eval() 'trả về' double'. –

+0

@Bart Tôi đã nghiên cứu điều này trong một tuần - đây là ví dụ đầu tiên thực sự chi tiết và đầy đủ để làm việc lần đầu tiên và tôi nghĩ rằng tôi hiểu. Tôi gần như đã từ bỏ. Cảm ơn! –

7

Đối với ANTLR 4 java quá trình sinh mã là dưới đây: -

java -cp antlr-4.5.3-complete.jar org.antlr.v4.Tool Exp.g 

Cập nhật tên jar của bạn trong classpath cho phù hợp.

1

Tại https://github.com/BITPlan/com.bitplan.antlr bạn sẽ tìm thấy thư viện java ANTLR với một số lớp trợ giúp hữu ích và một vài ví dụ hoàn chỉnh. Nó đã sẵn sàng để được sử dụng với maven và nếu bạn thích eclipse và maven.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/exp/Exp.g4

là một ngôn ngữ biểu thức đơn giản có thể làm nhân và thêm hoạt động. https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java có các bài kiểm tra đơn vị tương ứng cho nó.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 là một phân tích cú pháp IRI đã được chia thành ba phần:

  1. phân tích cú pháp văn phạm
  2. lexer ngữ pháp
  3. nhập khẩu LexBasic ngữ pháp

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java có các unit test cho nó.

Cá nhân tôi thấy đây là phần khó khăn nhất để có được quyền. Xem http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

https://github.com/BITPlan/com.bitplan.antlr/tree/master/src/main/antlr4/com/bitplan/expr

chứa nhiều ba ví dụ đã được tạo ra cho một vấn đề hiệu suất của ANTLR4 trong một phiên bản trước đó. Trong thời gian chờ đợi, sự cố này đã được khắc phục dưới dạng testcase https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java hiển thị.

3

Đối với tôi hướng dẫn này rất hữu ích: https://tomassetti.me/antlr-mega-tutorial

Nó có ví dụ ngữ pháp, ví dụ lượng khách truy cập bằng các ngôn ngữ khác nhau (Java, JavaScript, C# và Python) và nhiều thứ khác. Rat khuyen khich.

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