我是ANTLR的新手,我正在尝试扩展一个简单的计算器here的例子。具体来说,我尝试添加一些简单的函数,负数等,以熟悉自己的ANTLR。但是,我在尝试实现“隐式”乘法时遇到了一些问题(例如,3cos(2)sin(2)将被解释为3 * cos(2)* sin(2))。
我在Stack Overflow上发现了一个同样问题(here)的问题。解决这个问题的一般形式看起来就像我自己找到的那样,所以我不确定我的问题在哪里。
我的语法如下。如果没有| p2 = signExpr {$value *= $p2.value;}
行(multiplicationExpr
中的最后一行),根据我的测试,一切似乎都能正常工作。当我添加此行并通过antlr
运行时,我收到以下错误:
error(211): calculator.g:24:3: [fatal] rule multiplicationExpr has non-LL(*) decision due to recursive rule invocations reachable from alts 3,4. Resolve by left-factoring or using syntactic predicates or using backtrack=true option.
warning(200): calculator.g:24:3: Decision can match input such as "'-' FLOAT" using multiple alternatives: 3, 4
As a result, alternative(s) 4 were disabled for that input
启用backtrack
导致我的一些(通常正常工作的)测试表达式计算错误。此外,警告会讨论multiplicationExpr
的备选方案3和4,但我只有三个备选方案,让我感到困惑。
有人能够指出我的语法中的错误,如下所示?
grammar calculator;
eval returns [double value]
: exp = additionExpr {$value = $exp.value;}
;
additionExpr returns [double value]
: m1 = multiplicationExpr {$value = $m1.value;}
( '+' m2 = multiplicationExpr {$value += $m2.value;}
| '-' m2 = multiplicationExpr {$value -= $m2.value;}
)*
;
multiplicationExpr returns [double value]
: p1 = signExpr {$value = $p1.value;}
( '*' p2 = signExpr {$value *= $p2.value;}
| '/' p2 = signExpr {$value /= $p2.value;}
| p2 = signExpr {$value *= $p2.value;}
)*
;
signExpr returns [double value]
: ( '-' a = funcExpr {$value = -1*$a.value;}
) | ( a = funcExpr {$value = $a.value;}
)
;
funcExpr returns [double value]
: ( 'cos' s = signExpr {$value = Math.cos($s.value);}
) | ( 'sin' s = signExpr {$value = Math.sin($s.value);}
) | ( s = powExpr {$value = s;}
)
;
powExpr returns [double value]
: s1 = atomExpr {$value = $s1.value;}
( '^' s2 = signExpr {$value = Math.pow($value, $s2.value);}
)?
;
atomExpr returns [double value]
: f = FLOAT {$value = Double.parseDouble($f.text);}
| '(' exp = additionExpr ')' {$value = $exp.value;}
;
FLOAT
: ('0'..'9')+ ('.' ('0'..'9')*)? EXPONENT?
| '.' ('0'..'9')+ EXPONENT?
;
WS : ( ' '
| '\t'
| '\r'
| '\n'
) {$channel=HIDDEN;}
;
fragment
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
答案 0 :(得分:5)
新年,新版本: - )
由于您是ANTLR的新手,现在已经看到了v3的一些模糊性问题,您一定会欣赏v4的强大功能。以下是 a 解决方案(我是v4的新手),将计算保持在表达式级别,但显示使用ANTLR4描述表达式是多么简单。
grammar Calculator;
progr : eval+ EOF ;
eval
: mult ';' {System.out.println($eval.text + " -> " + $mult.value);}
;
mult returns [double value]
: e1 = expr {$value = $e1.value;}
| e1 = expr e2 = mult {$value = $e1.value * $e2.value;}
;
expr returns [double value]
: e1 = expr '^'<assoc=right> e2 = expr {$value = Math.pow($e1.value, $e2.value);}
| '-' e1 = expr {$value = -1 * $e1.value;}
| e1 = expr '*' e2 = expr {$value = $e1.value * $e2.value;}
| e1 = expr '/' e2 = expr {$value = $e1.value / $e2.value;}
| e1 = expr '+' e2 = expr {$value = $e1.value + $e2.value;}
| e1 = expr '-' e2 = expr {$value = $e1.value - $e2.value;}
| 'cos' s = expr {$value = 2; System.out.println("cos" + $s.text + "=2");} // {$value = Math.cos($s.value);}
| 'sin' s = expr {$value = 5; System.out.println("sin" + $s.text + "=5");} // {$value = Math.sin($s.value);}
| FLOAT {$value = Double.parseDouble($FLOAT.text); System.out.println("FLOAT=" + $FLOAT.text);}
| '(' FLOAT ')' {$value = $expr.value;} // just for demo, to avoid printing FLOAT parameters
| '(' e1 = expr ')' {$value = $e1.value; System.out.println("expr(" + $e1.text + ")=" + $e1.value);}
;
FLOAT
: DIGIT+ ( '.' DIGIT* )? EXPONENT?
| '.' DIGIT+ EXPONENT?
;
WS : [ \t\r\n] -> channel(HIDDEN)
;
fragment DIGIT : [0-9] ;
fragment EXPONENT : [Ee] ( '+' | '-' )? DIGIT+ ;
文件input.txt:
3cos(2)sin(6);
4cos(2)cos(8)sin(6);
sin(1)sin(6);
sin(1)sin(6)cos(8)10sin(8);
sin(1)5cos(6)3;
2^8;
3*-12;
55/sin(0);
55/(2 + 3);
执行:
$ echo $CLASSPATH
.:/usr/local/lib/antlr-4.0b3-complete.jar
$ alias
alias antlr4='java -jar /usr/local/lib/antlr-4.0b3-complete.jar'
alias grun='java org.antlr.v4.runtime.misc.TestRig'
$ antlr4 Calculator.g4
$ javac Calculator*.java
$ grun Calculator progr input.txt
FLOAT=3
cos(2)=2
sin(6)=5
3cos(2)sin(6); -> 30.0
FLOAT=4
cos(2)=2
cos(8)=2
sin(6)=5
4cos(2)cos(8)sin(6); -> 80.0
sin(1)=5
sin(6)=5
sin(1)sin(6); -> 25.0
sin(1)=5
sin(6)=5
cos(8)=2
FLOAT=10
sin(8)=5
sin(1)sin(6)cos(8)10sin(8); -> 2500.0
sin(1)=5
FLOAT=5
cos(6)=2
FLOAT=3
sin(1)5cos(6)3; -> 150.0
FLOAT=2
FLOAT=8
2^8; -> 256.0
FLOAT=3
FLOAT=12
3*-12; -> -36.0
FLOAT=55
sin(0)=5
55/sin(0); -> 11.0
FLOAT=55
FLOAT=2
FLOAT=3
expr(2 + 3)=5.0
55/(2 + 3); -> 11.0
注意:
1)我给cos和sin赋予了固定值,以便能够轻松验证计算
2)使用-diagnostics运行,你会看到歧义消息:
$ grun Calculator progr -diagnostics input.txt
FLOAT=3
line 1:6 reportAttemptingFullContext d=2, input='(2)'
line 1:6 reportAmbiguity d=2: ambigAlts={5, 6}, input='(2)'
这是因为我添加了一行:
| '(' FLOAT ')' {$value = $expr.value;} // just for demo
打印得很好(避免将参数打印到cos / sin)。这与'(' e1 = expr ')'
不明确。删除它,消息消失
3)了解ANTLR4如何标记(-tokens)和解析(-trace):
grun Calculator progr -tokens -diagnostics -trace input.txt
4)网站:http://antlr4.org
5)书:http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference
6)在OS X上安装4.0b3:http://forums.pragprog.com/forums/206/topics/11231
7)SO过滤器:https://stackoverflow.com/questions/tagged/antlr4
8)组:https://groups.google.com/forum/#!forum/antlr-discussion
答案 1 :(得分:1)
随着BernardK的解决方案按照我之前的语法进行按摩,这里有一个新的multiplicationExpr
可以让一切对我有用:
multiplicationExpr returns [double value]
:
p1 = signExpr {$value = $p1.value;}
( (signExpr funcExpr*) => p2 = funcExpr {$value *= $p2.value;}
| '*' p2 = signExpr {$value *= $p2.value;}
| '/' p2 = signExpr {$value /= $p2.value;}
)*
;
经过一段时间的游戏,接近我原来的东西也是如此:
multiplicationExpr returns [double value]
: p1 = signExpr {$value = $p1.value;}
( p2 = funcExpr {$value *= $p2.value;}
| '*' p2 = signExpr {$value *= $p2.value;}
| '/' p2 = signExpr {$value /= $p2.value;}
)*
;
再次感谢Bernard。