Ryan Kennedy
Ryan Kennedy

Reputation: 3625

ANTLR unit is also an identifier

I'm trying to create a LESS parser in ANTLR, and I'm using Twitter Bootstrap's LESS files for testing. I've got it mostly working, but there's one part that's stumping me, and I think it's because I don't completely grok ANTLR.

In less/mixins.less, there is a mixin declaration as follows:

.directional(@start-color: #555; @end-color: #333; @deg: 45deg) {
  background-repeat: repeat-x;
  background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+
  background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
}

The thing that's hanging up my parser is that there's a variable with the same name as a unit. The @deg token, instead of getting parsed as a variable, gets parsed as Asperand unit instead (because deg is a unit). Since this is invalid, the parser quits. I've tried rearranging the order of my declarations in my ANTLR file, but it still won't work.

Subset of my grammar:

varAssignment
    : variable w Colon w expr
    ;

expr
    : expr w term w
    | expr w Comma w term w
    | term w
    ;

term
    : variable
    | measurement
    ;

variable
    : Asperand Identifier
    ;

// Insignificant whitespace.
w: (W | Comment)*;

Identifier
    : NameStart NameChar*
    ;

Ampersand: '&';
Asperand:  '@';
Comma:     ',';
Semi:      ';';
Colon:     ':';
Ellipsis:  '...';

OpenBlock:  '{';
CloseBlock: '}';
OpenParen:  '(';
CloseParen: ')';
OpenSquare: '[';
CloseSquare:']';

W: (' ' | '\t' | '\n' | '\r' | '\f' )+;


number: Number;
Number: Dgt+ | Dgt* '.' Dgt+;

fragment NameChar
    : NameStart
    | Dgt
    ;
fragment NameStart
    : 'a'..'z' | 'A'..'Z' | '-'
    ;
fragment Dgt
    : '0'..'9'
    ;


unit:'%'
    |'px'
    |'em'
    |'ex'
    |'in'
    |'cm'
    |'mm'
    |'pc'
    |'deg'
    |'rad'
    |'grad'
    ;


measurement
    : Number unit?
    ;

How would I have to rearrange my declarations so that @deg parses as a variable, but 45deg parses as a measurement?

I know I could cheat and define unit: Identifier | '%' ; but that would be pretty atrocious.

Upvotes: 0

Views: 83

Answers (1)

Sam Harwell
Sam Harwell

Reputation: 99879

You need to redefine your unit rule as you described at the end of the question. It's not "cheating". In fact, as long as your resulting grammar is ambiguous, it greatly improves your ability to extend the grammar in the future to accept additional types of units.

You can validate the specific Identifier for a unit in a listener or visitor that executes after the initial parse is complete.

Upvotes: 1

Related Questions