2012-04-13 5 views
5

У меня странный побочный эффект от правила lexer от antlr, и я создал (почти) минимальный рабочий пример, чтобы продемонстрировать его. В этом примере я хочу, например, сопоставить String [0..1]. Но когда я отлаживаю грамматику, поток токенов, который достигает синтаксического анализа, содержит только [..1]. Первое целое число, независимо от того, сколько цифр оно содержит, всегда потребляется, и я не знаю, как это происходит. Если я удалю правило FLOAT, все будет хорошо, поэтому я предполагаю, что ошибка лежит где-то в этом правиле. Но, поскольку он не должен ничего соответствовать в [0..1], я совершенно озадачен.ANTLR lexer rule потребляет символы, даже если они не совпадают?

Я был бы рад за любые указатели, где я, возможно, ошибся. Это мой пример:

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

ответ

6

'0' отбрасывается лексером и следующие ошибки производятся:

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

Это происходит потому, что когда лексический встречает '0.', он пытается создать FLOAT маркер, но не может. И поскольку нет другого правила, чтобы вернуться к следующему '0.', он производит ошибки, отбрасывает '0' и создает токен DOT.

Это просто, как работает лексер Antlr: он не будет возвращаться назад, чтобы совпадать с INTEGER с последующим DDOTS (обратите внимание, что backtrack=true относится только к парсеру правил!).

В правиле FLOAT вы должны убедиться, что, когда двойной '.' впереди, вместо этого вы создаете токен INTEGER. Вы можете сделать это, добавив синтаксический предикат (часть ('..')=>) и произнесите токены FLOAT только тогда, когда за одним '.' следует цифра (часть ('.' DIGIT)=>). См. Следующую демонстрационную версию:

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

Теперь это было (для меня как минимум) неожиданным поведением. Спасибо за исчерпывающий пример: у меня все в порядке и сейчас :-) – Lichtblitz

+0

@ Lichtblitz, приветствую вас, да, токенизация '..' (в сочетании с INT- и FLOAT-токенами) сложна ! :) –

Смежные вопросы