Я полный новичок ANTLR4, поэтому, пожалуйста, простите мое невежество. Я столкнулся с этой презентацией, где определена очень простая арифметическая грамматика выражения. Это выглядит так:
grammar Expressions;
start : expr ;
expr : left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
INT : ('0'..'9')+ ;
WS : [ \t\r\n]+ -> skip ;
Это здорово, потому что оно создаст очень простое двоичное дерево, которое может быть пройдено с использованием шаблона посетителя, как описано в слайдах, например, здесь функция, которая посещает expr
:
public Integer visitOpExpr(OpExprContext ctx) {
int left = visit(ctx.left);
int right = visit(ctx.right);
String op = ctx.op.getText();
switch (op.charAt(0)) {
case '*': return left * right;
case '/': return left / right;
case '+': return left + right;
case '-': return left - right;
default: throw new IllegalArgumentException("Unkown opeator " + op);
}
}
Следующее, что я хотел бы добавить, это поддержка скобок. Поэтому я изменил expr
следующим образом:
expr : '(' expr ')' #opExpr
| left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
К сожалению, приведенный выше код не сработает, поскольку при столкновении с круглыми скобками три атрибута op
, left
и right
равны нулю (сбой NPE).
Я думаю, что смогу обойти это, указав новый атрибут, например parenthesized='(' expr ')'
, а затем обработайте его в коде посетителя. Однако мне кажется излишним иметь целый дополнительный node тип для представления выражения в круглых скобках. Более простым, но уродливым решением является добавление следующей строки кода в начале метода visitOpExpr
:
if (ctx.op == null) return visit(ctx.getChild(1)); // 0 and 2 are the parentheses!
Мне не нравится это вообще, потому что он очень хрупкий и сильно зависит от структуры грамматики.
Мне интересно, есть ли способ сказать ANTLR просто "съесть" круглые скобки и обработать выражение, подобное ребенку. Здесь? Есть лучший способ сделать это?
Примечание. Моя конечная цель - расширить пример, чтобы включить булевы выражения, которые могут сами содержать арифметические выражения, например, (2+4*3)/10 >= 11
, то есть отношение (<, > , ==, ~ = и т.д.). между арифметическими выражениями может быть определено атомное булево выражение. Это прямолинейно, и у меня уже есть грамматика, но у меня такая же проблема с круглыми скобками, т.е. Мне нужно писать такие вещи, как (я также добавлю поддержку переменных):
((2+4*x)/10 >= 11) | ( x>1 & x<3 )
EDIT. Исправлено приоритет выражения в скобках, скобки всегда имеют более высокий приоритет.