Mutual Left Recursion ANTLR 4

2019-07-11 14:20发布

I'm sorry to ask yet another question on mutual left recursion, I feel like mine is unique to my situation, or at least I can't figure out enough to relate it to everyone else's grammars. I'm a bit new to the comp sci world (I'm self taught in java, which is my target language, and now ANTLR4) so if possible please describe things in layperson terms, not CS major terms.

I'm writing a program that requires algebra and symbolic derivatives, and of course that requires that things be parsed, and trees be operated upon but I'm not even going to worry about that yet because I thought ANTLR4 supported direct left recursion, but apparently it doesn't somehow. On the output, it keeps telling me that my method [expression] is mutually left recursive and apparently that's not allowed...? MY QUESTIONS:

1) Can someone explain left recursion/the difference between mutual and direct left recursion if there is one?

2) Explain what in my grammar is causing this recursive annoyance, and how to fix it? And I'm not sure if this is on topic:

3) People say things about alternatives and labeling alternatives (I think they mean the #label notation). What is that for?

grammar MathProcessor;
@header {package utils;}
END: ';';
EQUALS: '=';
SIN: 'sin(';
COS: 'cos(';
TAN: 'tan(';
SEC: 'sec(';
CSC: 'csc(';
COT: 'cot(';
LN: 'ln(';
EPOW: 'pow(';
RPAREN: '(';
LPAREN: ')';
EXP: '^';
MULT: '*';
DIV: '/';
ADD: '+';
SUBT: '-';
VAR: ('a'..'z'|'A'..'Z');
INT: ('0'..'9')+;
mathobj: ((equation|expression) END) EOF;
equation: (expression '=' expression);

expression: 
((RPAREN|SIN|COS|TAN|SEC|CSC|COT|LN|EPOW) expression (RPAREN)) #parenOps
| (expression EXP expression) #exponent
| (expression (MULT|DIV) expression) #multiplyDivide 
| (expression (ADD|SUBT) expression) #addSubtract
| (VAR|INT) #varInt
;

1条回答
相关推荐>>
2楼-- · 2019-07-11 14:56

Your grammar would actually work with ANTLR 4 if you simply removed several cases of unnecessary parentheses in your grammar. The left recursion elimination algorithm only works with direct left recursion, which appears in the following example:

a
  : B
  | a B  // the reference to 'a' here is direct left recursion
  ;

The primary other type of recursion is indirect left recursion, which is typically demonstrated using two separate rules.

a
  : B
  | c
  ;

c
  : a B // the reference back to 'a' is indirect left recursion
  ;

In the case of your grammar, ANTLR is treating (...) as an anonymous sub-rule, so code like the following is determined to be indirect left recursion. If you remove the parentheses from the example, it behaves like the first case.

a
  : B
  | (a B)
  ;

To answer your last question, the #label syntax changes the generated parse tree class for that alternative. For example, rather than generate a plain ExpressionContext object, the expression 3 will generate a VarIntContext. This allows you to distinguish between different alternatives in a rule in automatically generated listeners and visitors.

查看更多
登录 后发表回答