Ошибка предиката ANTLR4 игнорирует следующую альтернативу?

У меня проблемы с семантическими предикатами в ANTLR 4. Моя грамматика синтаксически неоднозначна, и для устранения двусмысленности нужно смотреть вперед на один токен.

В качестве примера я хочу проанализировать «19 января 2012 г. до 9 вечера» как дату «19 января 2012 г.», оставив следующий токен анализатора на «до». И я хочу проанализировать «19 января, 7 до 9 вечера» как дату «19 января» со следующим токеном парсера в «7».

Поэтому мне нужно посмотреть на 3-й жетон и либо взять его, либо оставить.

Мой фрагмент грамматики:

date
    :    month d=INTEGER  { isYear(getCurrentToken().getText())}?  y=INTEGER
                                    {//handle date, use $y for year}
    |    month d=INTEGER            {//handle date, use 2013 for year}
    ;                                             

Когда синтаксический анализатор запускается на любом примере ввода, я получаю это сообщение:

line 1:9 rule date failed predicate: { isYear(getCurrentToken().getText())}?

Он никогда не доходит до 2-го альтернативного правила, потому что (я предполагаю) он уже прочитал один дополнительный токен.

Может ли кто-нибудь показать мне, как это сделать?


person Ken Homer    schedule 24.07.2013    source источник
comment
Одним из решений было бы использование двух синтаксических анализаторов, первый такой же, как и выше, без предиката. Он выводит $DATE(2012-1-19) до 9 вечера для первого ввода и $DATE(2013-1-19) с 7 до 9 вечера для второго ввода. Но если возникают другие подобные неоднозначности, это становится решением n-парсера.   -  person Ken Homer    schedule 24.07.2013


Ответы (1)


В правилах парсера ANTLR 4 использует предикаты только на левом краю при принятии решения. Встроенные предикаты, подобные показанному выше, только проверяются.

Следующая модификация заставит ANTLR оценивать предикат во время принятия решения, но, очевидно, вам нужно будет изменить его, чтобы использовать правильный токен просмотра вместо вызова getCurrentToken().

date
  : {isYear(getCurrentToken().getText())}? month d=INTEGER y=INTEGER
                                {//handle date, use $y for year}
  | month d=INTEGER             {//handle date, use 2013 for year}
  ;

PS: если month всегда имеет длину ровно один токен, то _input.LT(3) должен предоставить нужный вам токен.

person Sam Harwell    schedule 24.07.2013
comment
Спасибо. Я думаю, вы имели в виду LT (3), LA (3) дает мне синтаксическую ошибку. Это решает мою проблему. - person Ken Homer; 25.07.2013