Семантический предикат Antlr не нашел жизнеспособной альтернативы

Я не могу получить даже более простые семантические предикаты для работы с Antlr 4.6.6 для .net framework 4.8, грамматика ниже не может найти жизнеспособную альтернативу для ввода

"received:last week"

.

grammar test;

// Parser rules
parse
: expr (expr)* EOF
;

expr 
: {false}? received ':' lastweek
| received ':' text
| text
;   

received: RECEIVED;
lastWeek: LASTWEEK;
text: TEXT;

RECEIVED: 'received';

TEXT
: 
~(' ' | ':')+
;

LASTWEEK: 'last week';

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

ОБНОВЛЕНИЕ: это упрощение моей проблемы. Возможно ли иметь грамматику, которая может анализировать это значение: last week как полученное на прошлой неделе, только если последней неделе предшествует получено, но если, например, у меня есть тема: last week, которая будет проанализирована как subject на прошлой неделе.


person ekalchev    schedule 28.07.2020    source источник


Ответы (1)


Когда я запускаю этот код:

public static void main(String[] args) {
    String source = "received:last week";
    testLexer lexer = new testLexer(CharStreams.fromString(source));
    testParser parser = new testParser(new CommonTokenStream(lexer));
    System.out.println(parser.parse().toStringTree(parser));
}

ошибка line 1:0 no viable alternative at input 'received' выводится в STDERR. Когда я меняю {false}? на {true}?, ввод обрабатывается правильно (как и ожидалось).

Если вы ожидали, что ввод будет проанализирован как received ':' text из-за предиката {false}?, вы не понимаете, как работает лексер ANTLR. Лексер производит токены независимо от анализатора. Не имеет значения, что синтаксический анализатор пытается сопоставить токен TEXT, ваш ввод всегда токенизируется одинаково.

Лексер работает так:

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

Учитывая эти правила, ясно, что "received:last week" токенизируется как RECEIVED, ':' и LASTWEEK токен.

РЕДАКТИРОВАТЬ

Возможно ли иметь грамматику, которая может анализировать это полученное: на прошлой неделе как полученное на прошлой неделе, только если последней неделе предшествует полученное, но если, например, у меня есть тема: последняя неделя для анализа как тема на прошлой неделе

Вы можете сделать лексер несколько зависимым от контекста, используя лексические режимы. Затем вы должны создать отдельные грамматики лексера и синтаксического анализатора, которые могут выглядеть следующим образом:

TestLexer.g4

lexer grammar TestLexer;

RECEIVED : 'received' -> pushMode(RECEIVED_MODE);
SUBJECT  : 'subject';
TEXT     : ~[ :]+;
COLON    : ':';
SPACES   : SPACE+     -> skip;

fragment SPACE : [ \t\r\n];

mode RECEIVED_MODE;
  LASTWEEK            : 'last' SPACE+ 'week' -> popMode;
  RECEIVED_MODE_COLON : ':'                  -> type(COLON);
  RECEIVED_MODE_TEXT  : ~[ :]+               -> type(TEXT), popMode;

вы можете использовать приведенный выше лексер в грамматике парсера следующим образом:

TestParser.g4

parser grammar TestParser;

options {
  tokenVocab=TestLexer;
}

...

Теперь "received:last week" будет токенизироваться как:

'received'                `received`
COLON                     `:`
LASTWEEK                  `last week`
EOF                       `<EOF>`

и "subject:last week" будут токенизироваться как:

'subject'                 `subject`
COLON                     `:`
TEXT                      `last`
TEXT                      `week`
EOF                       `<EOF>`

РЕДАКТИРОВАТЬ II

Вы также можете переместить создание last week в парсер следующим образом:

received
 : RECEIVED ':' last_week
 ;

subject
 : SUBJECT ':' text
 ;

last_week
 : LAST WEEK
 ;

text
 : TEXT
 | LAST
 | WEEK
 ;

RECEIVED : 'received';
SUBJECT  : 'subject';
LAST     : 'last';
WEEK     : 'week';
TEXT     : ~[ :]+;
person Bart Kiers    schedule 28.07.2020
comment
Спасибо. Это упрощение моей проблемы. Возможно ли иметь грамматику, которая может анализировать это значение: last week как полученное на прошлой неделе, только если последней неделе предшествует получено, но если, например, у меня есть тема: last week, которая будет проанализирована как subject на прошлой неделе. Вы сказали, что лексер всегда будет соответствовать самой длинной строке, а это значит, что этого нельзя будет достичь. Я попытался использовать сематический предикат на рычаге лексера, и мне действительно удалось сопоставить те, что в простом примере выше, но у меня недостаточно контекста для проверки предыдущего полученного токена. - person ekalchev; 29.07.2020
comment
Это отличный ответ! Большое тебе спасибо! - person ekalchev; 29.07.2020
comment
Пожалуйста, @ekalchev. Я также добавил вторую правку для немного другого решения. - person Bart Kiers; 29.07.2020