Я хотел бы проанализировать два типа выражения с логическим значением:
- первым будет выражение инициализации с логическим значением типа: 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 ошибка(и)
ПОСТРОИТЬ НЕУДАЧНО"
@ sepp2k, я отредактировал свой пост с лучшим объяснением того, что я хотел бы попробовать сделать, и с минимальным воспроизводимым примером, как вы просили.
Пробовали ли вы переместить семантические предикаты с самого начала во что-то другое? Кроме того, я думаю, что его можно переписать вообще без семантических предикатов.
По-видимому, поддержка 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 для каждого ограничения в разных правилах.
Но вы правы, писать код с действиями и предикатами хлопотно... Я посмотрю, читабелен мой код или нет. В любом случае, вы мне очень помогли, и теперь это работает. Большое спасибо.
Сообщение об ошибке кажется немного вводящим в заблуждение (утверждается, что существует взаимная левая рекурсия, когда левая рекурсия на самом деле прямая), но похоже, что поддержка левой рекурсии ANTLR4 не работает с предикатом. Можете ли вы объяснить, для чего предназначен предикат и когда
vConstraintType
меняет свое значение? Также опубликуйте минимальный воспроизводимый пример.