2012-04-13 32 views
5

Tôi đã có một tác dụng phụ lạ của một quy tắc lexer antlr và tôi đã tạo ra một ví dụ làm việc tối thiểu (gần như) để chứng minh nó. Trong ví dụ này, tôi muốn khớp với chuỗi [0..1] chẳng hạn. Nhưng khi tôi gỡ lỗi ngữ pháp, luồng mã thông báo đến trình phân tích cú pháp chỉ chứa [..1]. Số nguyên đầu tiên, bất kể có bao nhiêu chữ số chứa luôn luôn được tiêu thụ và tôi không có đầu mối nào về cách điều đó xảy ra. Nếu tôi loại bỏ quy tắc FLOAT, mọi thứ đều ổn, vì vậy tôi đoán sai lầm nằm ở đâu đó trong quy tắc đó. Nhưng vì nó không phù hợp với bất cứ thứ gì trong số [0..1], tôi khá bối rối.Quy tắc lexer ANTLR tiêu thụ các ký tự ngay cả khi không khớp?

Tôi rất vui vì bất kỳ con trỏ nào mà tôi có thể đã sai. Đây là ví dụ của tôi:

grammar min; 
options{ 
language = Java; 
output = AST; 
ASTLabelType=CommonTree; 
backtrack = true; 
} 
tokens { 
    DECLARATION; 
} 

declaration : LBRACEVAR a=INTEGER DDOTS b=INTEGER RBRACEVAR -> ^(DECLARATION $a $b); 

EXP : 'e' | 'E'; 
LBRACEVAR: '['; 
RBRACEVAR: ']'; 
DOT: '.'; 
DDOTS: '..'; 

FLOAT 
    : INTEGER DOT POS_INTEGER 
    | INTEGER DOT POS_INTEGER EXP INTEGER 
    | INTEGER EXP INTEGER 
    ; 

INTEGER : POS_INTEGER | NEG_INTEGER; 
fragment NEG_INTEGER : ('-') POS_INTEGER; 
fragment POS_INTEGER : NUMBER+; 
fragment NUMBER: ('0'..'9'); 

Trả lời

6

Các '0' bị loại bỏ bởi lexer và các lỗi sau đây được tạo ra:

line 1:3 no viable alternative at character '.' 
line 1:2 extraneous input '..' expecting INTEGER 

Điều này là do khi lexer gặp '0.', nó sẽ cố gắng để tạo ra một mã thông báo FLOAT, nhưng không thể. Và vì không có quy tắc nào khác để quay lại để khớp với '0.', nó tạo ra lỗi, loại bỏ '0' và tạo mã thông báo DOT.

Đây chỉ đơn giản là cách thức hoạt động của từ khóa ANTLR: nó sẽ không quay lại để khớp với số INTEGER theo sau là DDOTS (lưu ý rằng backtrack=true chỉ áp dụng cho quy tắc phân tích cú pháp!).

Bên trong quy tắc FLOAT, bạn phải đảm bảo rằng khi một đôi '.' ở phía trước, thay vào đó, bạn tạo một mã thông báo INTEGER. Bạn có thể làm điều đó bằng cách thêm một thuộc tính cú pháp (phần ('..')=>) và chỉ tạo mã thông báo FLOAT khi một đơn '.' được theo sau bởi một chữ số (phần ('.' DIGIT)=>). Xem bản trình diễn sau:

declaration 
: LBRACEVAR INTEGER DDOTS INTEGER RBRACEVAR 
; 

LBRACEVAR : '['; 
RBRACEVAR : ']'; 
DOT  : '.'; 
DDOTS  : '..'; 

INTEGER 
: DIGIT+ 
; 

FLOAT 
: DIGIT+ (('.' DIGIT)=> '.' DIGIT+ EXP? 
      | ('..')=>  {$type=INTEGER;} // change the token here 
      |    EXP 
     ) 
; 

fragment EXP : ('e' | 'E') DIGIT+; 
fragment DIGIT : ('0'..'9'); 
+0

Hiện tại đó là hành vi không mong muốn (đối với tôi ít nhất). Cảm ơn ví dụ toàn diện, tôi đã có tất cả và chạy ngay bây giờ :-) – Lichtblitz

+0

@Lichtblitz, bạn được chào đón, và vâng, tokenizing '..' (kết hợp với với INT- và FLOAT-tokens) là khó khăn ! :) –

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