2012-04-13 22 views
5

Tengo un extraño efecto secundario de una regla antlr lexer y he creado un ejemplo de trabajo (casi) mínimo para demostrarlo. En este ejemplo, quiero hacer coincidir la cadena [0..1], por ejemplo. Pero cuando depuro la gramática, la secuencia de token que llega al analizador solo contiene [..1]. El primer entero, no importa cuántos dígitos contenga, siempre se consume y no tengo ni idea de cómo sucede eso. Si elimino la regla FLOAT todo está bien, así que supongo que el error se encuentra en alguna parte de esa regla. Pero como no debería coincidir con nada en [0..1] en absoluto, estoy bastante perplejo.La regla ANTLR lexer consume caracteres incluso si no coincide?

Estaría feliz por cualquier apuntador en el que podría haberme equivocado. Este es mi ejemplo:

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

Respuesta

6

El '0' se descarta por el analizador léxico y los siguientes errores se producen:

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

Esto se debe a que cuando el analizador léxico se encuentra con '0.', se trata de crear un token FLOAT, pero no puedo Y dado que no hay otra regla a la que recurrir para hacer coincidir '0.', produce los errores, descarta '0' y crea un token DOT.

Esto es simplemente cómo funciona lexer de antlr: no va a dar marcha atrás para que coincida con un INTEGER seguido de un DDOTS (tenga en cuenta que sólo se aplica a backtrack=true parser reglas!).

Dentro de la regla FLOAT, debe asegurarse de que cuando un doble '.' está por delante, se produce una ficha INTEGER lugar. Puede hacerlo agregando un predicado sintáctico (la parte ('..')=>) y producir tokens FLOAT solo cuando a un solo '.' le sigue un dígito (la parte ('.' DIGIT)=>). Vea la siguiente demostración:

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

Ahora que fue (al menos para mí) un comportamiento inesperado. Gracias por el ejemplo completo, lo tengo todo listo para funcionar ahora :-) – Lichtblitz

+0

@Lichtblitz, de nada, y sí, tokenizar '..' (en combinación con INT- y FLOAT-tokens) es complicado ! :) –

Cuestiones relacionadas