Vị ngữ ngữ nghĩa trong ANTLR là gì?
Vị ngữ ngữ nghĩa trong ANTLR là gì?
Câu trả lời:
Đối với các vị từ trong ANTLR 4, hãy kiểm tra các câu hỏi & đáp về tràn ngăn xếp sau:
Vị từ ngữ nghĩa là một cách để thực thi các quy tắc bổ sung (ngữ nghĩa) dựa trên các hành động ngữ pháp bằng cách sử dụng mã thuần túy.
Có 3 loại vị từ ngữ nghĩa:
Giả sử bạn có một khối văn bản chỉ bao gồm các số được phân tách bằng dấu phẩy, bỏ qua bất kỳ khoảng trắng nào. Bạn muốn phân tích cú pháp đầu vào này để đảm bảo rằng các số có nhiều nhất là 3 chữ số "dài" (nhiều nhất là 999). Ngữ pháp sau ( Numbers.g
) sẽ thực hiện một điều như vậy:
grammar Numbers;
// entry point of this parser: it parses an input string consisting of at least
// one number, optionally followed by zero or more comma's and numbers
parse
: number (',' number)* EOF
;
// matches a number that is between 1 and 3 digits long
number
: Digit Digit Digit
| Digit Digit
| Digit
;
// matches a single digit
Digit
: '0'..'9'
;
// ignore spaces
WhiteSpace
: (' ' | '\t' | '\r' | '\n') {skip();}
;
Ngữ pháp có thể được kiểm tra với lớp sau:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");
NumbersLexer lexer = new NumbersLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
NumbersParser parser = new NumbersParser(tokens);
parser.parse();
}
}
Kiểm tra nó bằng cách tạo lexer và parser, biên dịch tất cả .java
các tệp và chạyMain
lớp:
java -cp antlr-3.2.jar org.antlr.Tool Numbers.g javac -cp antlr-3.2.jar * .java java -cp .: antlr-3.2.jar Main
Khi làm như vậy, không có gì được in ra bảng điều khiển, điều này cho thấy rằng không có gì sai. Hãy thử thay đổi:
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");
thành:
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7777 , 89");
và thực hiện kiểm tra lại: bạn sẽ thấy lỗi xuất hiện trên bảng điều khiển ngay sau chuỗi 777
.
Điều này đưa chúng ta đến các vị từ ngữ nghĩa. Giả sử bạn muốn phân tích cú pháp các số có độ dài từ 1 đến 10 chữ số. Một quy tắc như:
number
: Digit Digit Digit Digit Digit Digit Digit Digit Digit Digit
| Digit Digit Digit Digit Digit Digit Digit Digit Digit
/* ... */
| Digit Digit Digit
| Digit Digit
| Digit
;
sẽ trở nên cồng kềnh. Các vị từ ngữ nghĩa có thể giúp đơn giản hóa loại quy tắc này.
Một vị từ ngữ nghĩa xác thực không gì khác hơn là một khối mã được theo sau bởi một dấu chấm hỏi:
RULE { /* a boolean expression in here */ }?
Để giải quyết vấn đề ở trên bằng cách sử dụng một
vị từ ngữ nghĩa xác thực , hãy thay đổi number
quy tắc trong ngữ pháp thành:
number
@init { int N = 0; }
: (Digit { N++; } )+ { N <= 10 }?
;
Các phần { int N = 0; }
và { N++; }
là các câu lệnh Java thuần túy mà phần đầu tiên được khởi tạo khi trình phân tích cú pháp "nhập" number
quy tắc. Vị từ thực tế là:, { N <= 10 }?
khiến trình phân tích cú pháp ném ra
FailedPredicateException
bất cứ khi nào một số dài hơn 10 chữ số.
Kiểm tra nó bằng cách sử dụng như sau ANTLRStringStream
:
// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
mà không tạo ra ngoại lệ, trong khi sau đây không có ngoại lệ:
// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
Một vị từ ngữ nghĩa xác thực tương tự như một vị từ ngữ nghĩa xác thực , chỉ có phiên bản gated tạo ra lỗi cú pháp thay vì a FailedPredicateException
.
Cú pháp của một vị từ ngữ nghĩa gated là:
{ /* a boolean expression in here */ }?=> RULE
Thay vào đó, để giải quyết vấn đề trên bằng cách sử dụng các vị từ được điều chỉnh để khớp các số có độ dài lên đến 10 chữ số, bạn sẽ viết:
number
@init { int N = 1; }
: ( { N <= 10 }?=> Digit { N++; } )+
;
Kiểm tra lại với cả hai:
// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
và:
// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
và bạn sẽ thấy lần cuối cùng sẽ xuất hiện một lỗi.
Loại vị từ cuối cùng là một vị từ ngữ nghĩa phân biệt , trông giống như một vị từ xác thực ( {boolean-expression}?
), nhưng hoạt động giống như một vị từ ngữ nghĩa định nghĩa (không có ngoại lệ nào được ném ra khi biểu thức boolean đánh giá thành false
). Bạn có thể sử dụng nó ở đầu quy tắc để kiểm tra một số thuộc tính của quy tắc và để trình phân tích cú pháp có khớp với quy tắc đã nói hay không.
Giả sử ngữ pháp ví dụ tạo Number
mã thông báo (quy tắc lexer thay vì quy tắc phân tích cú pháp) sẽ khớp với các số trong phạm vi 0..999. Bây giờ trong trình phân tích cú pháp, bạn muốn phân biệt giữa số thấp và số cao (thấp: 0..500, cao: 501..999). Điều này có thể được thực hiện bằng cách sử dụng một vị từ ngữ nghĩa phân biệt nơi bạn kiểm tra mã thông báo tiếp theo trong luồng ( input.LT(1)
) để kiểm tra xem nó thấp hay cao.
Bản demo:
grammar Numbers;
parse
: atom (',' atom)* EOF
;
atom
: low {System.out.println("low = " + $low.text);}
| high {System.out.println("high = " + $high.text);}
;
low
: {Integer.valueOf(input.LT(1).getText()) <= 500}? Number
;
high
: Number
;
Number
: Digit Digit Digit
| Digit Digit
| Digit
;
fragment Digit
: '0'..'9'
;
WhiteSpace
: (' ' | '\t' | '\r' | '\n') {skip();}
;
Nếu bây giờ bạn phân tích cú pháp chuỗi "123, 999, 456, 700, 89, 0"
, bạn sẽ thấy kết quả sau:
low = 123
high = 999
low = 456
high = 700
low = 89
low = 0
input.LT(1)
là getCurrentToken()
:-)
Tôi luôn sử dụng tham chiếu ngắn gọn đến các vị từ ANTLR trên wincent.com làm hướng dẫn của mình.