Как исправить ошибку в левой рекурсии, используемой с семантическими предикатами?

Я хотел бы проанализировать два типа выражения с логическим значением:
- первым будет выражение инициализации с логическим значением типа: init : false
- и последним будет производное выражение с логическим значением, например: derive : !express or (express and (amount >= 100))

Моя идея состоит в том, чтобы поместить семантические предикаты в набор правил, цель состоит в том, что когда я анализирую логическое выражение, начинающееся со слова «init», тогда оно должно перейти только к одному предложенному альтернативному правилу, которое является буллитеральный, последней альтернативой в логическое выражение. И если это выражение, начинающееся со слова «вывести», то оно может иметь доступ ко всем альтернативам логическое выражение.

Я знаю, что могу создать два типа boolExpression без семантических предикатов, таких как boolExpressionInit и boolExpressionDerive... Но я хотел бы попробовать со своей идеей, может ли она работать только с одним boolExpression с семантическими предикатами.

Вот моя грамматика

grammar TestExpression;

@header
{
package testexpressionparser;
}

@parser::members {
                    int vConstraintType;
                 }

/* SYNTAX RULES */
textInput       : initDefinition 
                | derDefinition ;

initDefinition  : t=INIT {vConstraintType = $t.type;} ':' boolExpression ;

derDefinition   : t=DERIVE {vConstraintType = $t.type;} ':' boolExpression ;

boolExpression  : {vConstraintType != INIT || vConstraintType == DERIVE}? boolExpression (boolOp|relOp) boolExpression 
                | {vConstraintType != INIT || vConstraintType == DERIVE}? NOT boolExpression
                | {vConstraintType != INIT || vConstraintType == DERIVE}? '(' boolExpression ')' 
                | {vConstraintType != INIT || vConstraintType == DERIVE}? attributeName
                | {vConstraintType != INIT || vConstraintType == DERIVE}? numliteral
                | {vConstraintType == INIT || vConstraintType == DERIVE}? boolliteral
                ;

boolOp          : OR | AND ;
relOp           : EQ | NEQ | GT | LT | GEQT | LEQT ;
attributeName   : WORD;
numliteral      : intliteral | decliteral;
intliteral      : INT ;
decliteral      : DEC ;
boolliteral     : BOOLEAN;


/* LEXICAL RULES */
INIT            : 'init';
DERIVE          : 'derive';
BOOLEAN         : 'true' | 'false' ;
BRACKETSTART    : '(' ;
BRACKETSTOP     : ')' ;
BRACESTART      : '{' ;
BRACESTOP       : '}' ;
EQ              : '=' ;
NEQ             : '!=' ;
NOT             : '!' ;
GT              : '>' ;
LT              : '<' ;
GEQT            : '>=' ;
LEQT            : '<=' ;
OR              : 'or' ;
AND             : 'and' ;
DEC             : [0-9]* '.' [0-9]* ;
INT             : ZERO | POSITIF;
ZERO            : '0';
POSITIF         : [1-9] [0-9]* ;
WORD            : [a-zA-Z] [_0-9a-zA-Z]* ;
WS              : (SPACE | NEWLINE)+ -> skip ;
SPACE           : [ \t] ;                       /* Space or tab */
NEWLINE         : '\r'? '\n' ;                  /* Carriage return and new line */

Я за исключением того, что грамматика будет работать успешно, но я получаю следующее: «ошибка (119): TestExpression.g4 ::: Следующие наборы правил взаимно леворекурсивны [boolExpression]
1 ошибка(и) ПОСТРОИТЬ НЕУДАЧНО"

Сообщение об ошибке кажется немного вводящим в заблуждение (утверждается, что существует взаимная левая рекурсия, когда левая рекурсия на самом деле прямая), но похоже, что поддержка левой рекурсии ANTLR4 не работает с предикатом. Можете ли вы объяснить, для чего предназначен предикат и когда vConstraintType меняет свое значение? Также опубликуйте минимальный воспроизводимый пример.

sepp2k 29.05.2019 16:29

@ sepp2k, я отредактировал свой пост с лучшим объяснением того, что я хотел бы попробовать сделать, и с минимальным воспроизводимым примером, как вы просили.

talohsa 30.05.2019 12:12

Пробовали ли вы переместить семантические предикаты с самого начала во что-то другое? Кроме того, я думаю, что его можно переписать вообще без семантических предикатов.

Ivan Kochurkin 30.05.2019 13:21
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
149
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По-видимому, поддержка ANTLR4 (прямой) левой рекурсии не работает, когда предикат появляется перед вызовом леворекурсивного правила. Таким образом, вы можете исправить ошибку, переместив предикат после первого boolExpression в леворекурсивных альтернативах.

Тем не менее, похоже, что предикаты вообще не нужны - по крайней мере, не в примере, который вы нам показали (или в том, что было до вашего редактирования, насколько я мог судить). Поскольку boolExpression с типом ограничения INIT, по-видимому, может соответствовать только boolLiteral, вы можете просто изменить initDefinition следующим образом:

initDefinition  : t=INIT ':' boolLiteral ;

Тогда boolExpression всегда будет иметь тип ограничения DERIVE и предикаты больше не нужны.

Как правило, если вы хотите разрешить разные альтернативы в нетерминальном x в зависимости от того, был ли он вызван y или z, вы должны просто иметь несколько версий x, а затем вызывать одну из y, а другую из z. Обычно это намного проще, чем засорять код действиями и предикатами.

Точно так же может иметь смысл иметь правило, которое соответствует большему количеству совпадений, чем должно, а затем обнаруживать недопустимые выражения на более позднем этапе, вместо того, чтобы пытаться отклонить их на уровне синтаксиса. В частности, новички часто пытаются писать грамматики, которые допускают только правильно типизированные выражения (отклоняя что-то вроде 1+true с синтаксической ошибкой), и это никогда не работает хорошо.

Это правда, что я мог бы сделать просто как ваш пример без предикатов. Но я ограничил тип ограничения init в моем примере, а также упростил правило boolExpression. На самом деле, я пытаюсь разобрать 4 типа ограничений с разными типами логических выражений. Вот почему я пытался использовать семантические предикаты только в одном правиле boolExpression, которые описывают несколько вариантов логического выражения, чем писать несколько версий boolExpression для каждого ограничения в разных правилах.

talohsa 30.05.2019 14:55

Но вы правы, писать код с действиями и предикатами хлопотно... Я посмотрю, читабелен мой код или нет. В любом случае, вы мне очень помогли, и теперь это работает. Большое спасибо.

talohsa 30.05.2019 14:55

Другие вопросы по теме