2012-04-13 10 views
5

Mam dziwny efekt uboczny zasady lexer Antlr i stworzyłem (prawie) minimalny przykład pracy, aby to zademonstrować. W tym przykładzie chcę na przykład dopasować ciąg [0..1]. Ale kiedy debuguję gramatykę, strumień tokenów docierający do parsera zawiera tylko [..1]. Pierwsza liczba całkowita, bez względu na to, ile cyfr zawiera, jest zawsze zużywana i nie mam pojęcia, jak to się dzieje. Jeśli usunę regułę, wszystko będzie dobrze, więc domyślam się, że błąd leży gdzieś w tej zasadzie. Ale ponieważ w ogóle nie pasuje do niczego w [0..1], jestem całkiem zaintrygowany.Reguła lexera ANTLR zużywa znaki, nawet jeśli nie są dopasowane?

Byłbym szczęśliwy z powodu wszelkich wskazówek, które mogły mi się nie udać. To jest mój przykład:

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'); 

Odpowiedz

6

'0' jest odrzucana przez lexer oraz następujące błędy są produkowane:

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

To dlatego, gdy lexer napotyka '0.', próbuje stworzyć FLOAT tokena, ale nie może. A ponieważ nie istnieje żadna inna reguła, która mogłaby się powtórzyć, aby pasowała do '0.', generuje ona błędy, odrzuca '0' i tworzy znacznik DOT.

To jest po prostu jak działa lexer antlr użytkownika: nie będzie wracać do dopasować INTEGER następnie przez DDOTS (zauważ, że backtrack=true odnosi się tylko do parsera zasady!).

Wewnątrz reguły FLOAT musisz upewnić się, że gdy podwójny '.' jest przedni, zamiast niego tworzony jest znacznik INTEGER. Możesz to zrobić, dodając predykat składniowy (część ('..')=>) i generując tokeny FLOAT tylko po pojedynczym '.' następuje cyfra (część ('.' DIGIT)=>). Zobacz następujące demo:

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

To było (przynajmniej dla mnie) nieoczekiwane zachowanie. Dziękuję za kompleksowy przykład, mam wszystko uruchomione teraz :-) – Lichtblitz

+0

@Lichtblitz, nie ma za co, i tak, tokenizacja '..' (w połączeniu z tokenami INT i FLOAT) jest trudna ! :) –

Powiązane problemy