Правило ANTLR Lexer, кажется, работает только как часть правила синтаксического анализатора, а не как часть другого правила лексера - PullRequest
1 голос
/ 27 мая 2020

Если у меня есть следующая грамматика для анализа списка целых чисел, разделенных пробелами:

grammar TEST;

test
    : expression* EOF
    ;

expression
    : integerLiteral
    ;

integerLiteral
    : INTLITERAL
    ;

PLUS: '+';
MINUS: '-';

DIGIT: '0'..'9';
DIGITS: DIGIT+;
INTLITERAL: (PLUS|MINUS)? DIGITS;

WS: [ \t\r\n] -> skip;

Это не работает! Если я пропущу "100", я получу:

line 1:0 extraneous input '100' expecting {<EOF>, INTLITERAL}

Однако, если удалить правило лексера INTLITERAL и поместить его прямо под правило синтаксического анализатора integerLiteral, как это

integerLiteral
    : (PLUS|MINUS)? DIGITS
    ;

Теперь, похоже, он работает просто хорошо!

Я чувствую, что если я смогу понять, почему это так, я начну понимать некоторые идиосинкразии, которые у меня возникают.

1 Ответ

3 голосов
/ 27 мая 2020

Лексический анализатор создает токены следующим образом:

  1. попытаться сопоставить как можно больше символов для одного токена
  2. если два токена соответствуют одним и тем же символам, пусть один определил первый "выигрыш"

Учитывая информацию из 2 правил выше, вы увидите, что ваши правила:

DIGITS: DIGIT+;
INTLITERAL: (PLUS|MINUS)? DIGITS;

являются проблемой. Для ввода 100 создается токен DIGITS: здесь применяется правило 2: оба правила соответствуют 100, но, поскольку DIGITS определен до INTLITERAL, создается токен DIGITS.

Решение 1

Переместите INTLITERAL выше DIGITS:

INTLITERAL: (PLUS|MINUS)? DIGITS;
DIGIT: '0'..'9';
DIGITS: DIGIT+;

Но теперь обратите внимание, что DIGIT и DIGITS никогда не станут токенами сами по себе, потому что INTLITERAL всегда будет соответствовать первым. В этом случае вы можете сделать оба этих правила fragment s, и тогда не имеет значения, где вы их разместите, потому что правила fragment используются только внутри других правил лексера (не в правилах парсера)

Решение 2

Сделайте DIGIT и DIGITS фрагменты

fragment DIGIT: '0'..'9';
fragment DIGITS: DIGIT+;
INTLITERAL: (PLUS|MINUS)? DIGITS;

Решение 3

Или лучше, не приклеивайте оператор к INTLITERAL, а сопоставить его в унарном выражении:

expression
    : (MINUS | PLUS) expression
    | expression (MINUS | PLUS) expression
    | integerLiteral
    ;

integerLiteral
    : INTLITERAL
    ;

PLUS: '+';
MINUS: '-';

fragment DIGIT: '0'..'9';

INTLITERAL: DIGIT+;
...