правило лексера antlr, совпадающее с префиксом другого правила - PullRequest
2 голосов
/ 03 апреля 2012

Я не уверен, что проблема на самом деле в префиксах, но здесь идет.

У меня есть эти два правила в моей грамматике (среди многих других)

DOT_T  : '.' ;
AND_T  : '.AND.'  | '.and.'  ;

и мне нужно разобрать строки следующим образом:

a.eq.b.and.c.ne.d
c.append(b)

это должно быть выражено как:

ID[a] EQ_T ID[b] AND_T ID[c] NE_T ID[d]
ID[c] DOT_T ID[append] LPAREN_T ID[b] RPAREN_T

ошибка, которую я получаю для второй строки:

line 1:3 mismatched character "p"; expecting "n"

Он не означает . как DOT_T, но вместо этого пытается сопоставить .and., потому что он видит a после ..

Есть идеи, что мне нужно сделать, чтобы сделать эту работу?

UPDATE

Я добавил следующее правило и подумал, что буду использовать тот же трюк

NUMBER_T
    : DIGIT+
        ( (DECIMAL)=> DECIMAL 
        | (KIND)=>    KIND
        )?
    ;

fragment DECIMAL
    : '.' DIGIT+ ;
fragment KIND
    : '.' DIGIT+ '_' (ALPHA+ | DIGIT+) ;

но когда я пытаюсь разобрать это:

lda.eq.3.and.dim.eq.3

выдает следующую ошибку:

line 1:9 no viable alternative at character "a"

во время лексизации 3. Поэтому я предполагаю, что происходит то же самое, что и выше, но решение в этом случае не работает: S Теперь я правильно запутался ...

1 Ответ

2 голосов
/ 03 апреля 2012

Да, это из-за префикса '.' -s.

Всякий раз, когда лексер натыкается на ".a", он пытается создать токен AND_T. Если символы "nd" не могут быть найдены, лексер пытается создать еще один токен, который начинается с ".a", которого нет (и ANTLR выдает ошибку). Таким образом, лексер не вернет символ "a" и отступит, чтобы создать токен DOT_T (а затем токен ID)! Так работает ANTLR.

Что вы можете сделать, это при желании сопоставить эти AND_T, EQ_T, ... внутри правила DOT_T. Но все же вам нужно будет немного «помочь» лексеру, добавив некоторые синтаксические предикаты, которые заставляют лексера смотреть в поток символов, чтобы убедиться, что он может соответствовать этим токенам.

Демонстрация:

grammar T;  

parse
 : (t=. {System.out.printf("\%-10s '\%s'\n", tokenNames[$t.type], $t.text);})* EOF
 ;

DOT_T  
 : '.' ( (AND_T)=> AND_T {$type=AND_T;}
       | (EQ_T)=>  EQ_T  {$type=EQ_T; }
       | (NE_T)=>  NE_T  {$type=NE_T; }
       )?
 ;

ID
 : ('a'..'z' | 'A'..'Z')+
 ;

LPAREN_T
 : '('
 ;

RPAREN_T
 : ')'
 ;

SPACE
 : (' ' | '\t' | '\r' | '\n')+ {skip();}
 ;

NUMBER_T
 : DIGIT+ ((DECIMAL)=> DECIMAL)?
 ;

fragment DECIMAL : '.' DIGIT+ ;
fragment AND_T   : ('AND' | 'and') '.' ;
fragment EQ_T    : ('EQ'  | 'eq' ) '.' ;
fragment NE_T    : ('NE'  | 'ne' ) '.' ;
fragment DIGIT   : '0'..'9';

А если вы передаете сгенерированный парсер, то ввод:

a.eq.b.and.c.ne.d
c.append(b)

будет напечатан следующий вывод:

ID         'a'
EQ_T       '.eq.'
ID         'b'
AND_T      '.and.'
ID         'c'
NE_T       '.ne.'
ID         'd'
ID         'c'
DOT_T      '.'
ID         'append'
LPAREN_T   '('
ID         'b'
RPAREN_T   ')'

А для ввода:

lda.eq.3.and.dim.eq.3

печатается следующее:

ID         'lda'
EQ_T       '.eq.'
NUMBER_T   '3'
AND_T      '.and.'
ID         'dim'
EQ_T       '.eq.'
NUMBER_T   '3'

EDIT

Тот факт, что DECIMAL и KIND оба начинаются с '.' DIGIT+, не годится. Попробуйте что-то вроде этого:

NUMBER_T
 : DIGIT+ ((DECIMAL)=> DECIMAL ((KIND)=> KIND)?)?
 ;

fragment DECIMAL : '.' DIGIT+;
fragment KIND    : '_' (ALPHA+ | DIGIT+); // removed ('.' DIGIT+) from this fragment

Обратите внимание, что правило NUMBER_T теперь никогда не будет выдавать токены DECIMAL или KIND. Если вы хотите, чтобы это произошло, вам нужно изменить тип:

NUMBER_T
 : DIGIT+ ((DECIMAL)=> DECIMAL {/*change type*/} ((KIND)=> KIND {/*change type*/})?)?
 ;
...