ANTLR lexer rule consumes characters even if not matched?

antlr, antlr3, antlrworks

Solution

The `'0'` is discarded by the lexer and the following errors are produced:

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

This is because when the lexer encounters `'0.'`, it tries to create a `FLOAT` token, but can't. And since there is no other rule to fall back on to match `'0.'`, it produces the errors, discards `'0'` and creates a `DOT` token.

This is simply how ANTLR's lexer works: it will not backtrack to match an `INTEGER` followed by a `DDOTS` (note that `backtrack=true` only applies to parser rules!).

Inside the `FLOAT` rule, you must make sure that when a double `'.'` is ahead, you produce a `INTEGER` token instead. You can do that by adding a syntactic predicate (the `('..')=>` part) and produce `FLOAT` tokens only when a single `'.'` is followed by a digit (the `('.' DIGIT)=>` part). See the following 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');

Problem

I've got a strange side effect of an antlr lexer rule and I've created an (almost) minimal working example to demonstrate it. In this example I want to match the String `[0..1]` for example. But when I debug the grammar the token stream that reaches the parser only contains `[..1]`. The first integer, no matter how many digits it contains is always consumed and I've got no clue as to how that happens. If I remove the `FLOAT` rule everything is fine so I guess the mistake lies somewhere in that rule. But since it shouldn't match anything in `[0..1]` at all I'm quite puzzled. I'd be happy for any pointers where I might have gone wrong. This is my example: ``` 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'); ```

Original source