ANTLR Implicit Multiplication

纵饮孤独 提交于 2019-12-05 18:12:55
BernardK

New year, new version :-)

As you are new to ANTLR, and now have seen some ambiguity problems with v3, you will surely appreciate the power of v4. Following is a solution (I'm new to v4) keeping the computation at the expression level, but showing how simple it is to describe expressions with 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+ ;

File 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);

Execution :

$ 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

Notes :
1) I have given fixed values to cos and sin to be able to easily verify the computations.
2) Run with -diagnostics and you'll see ambiguity messages :

$ 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)'

This is because I have added the line :

    |   '(' FLOAT ')'              {$value = $expr.value;} // just for demo

to print nicely (avoid printing parameters to cos/sin). This is ambiguous with '(' e1 = expr ')'. Remove it and the messages disappear.
3) To see how ANTLR4 tokenizes (-tokens) and parses (-trace) :

grun Calculator progr -tokens -diagnostics -trace input.txt

4) site : http://antlr4.org
5) book : http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference
6) install 4.0b3 on OS X : http://forums.pragprog.com/forums/206/topics/11231
7) SO filter : https://stackoverflow.com/questions/tagged/antlr4
8) group : https://groups.google.com/forum/#!forum/antlr-discussion

With BernardK's solution massaged into my previous grammar, here is the new multiplicationExpr that gets everything working for me:

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;}
                )*
        ;

After a bit more playing around, something close to what I originally had works as well:

multiplicationExpr      returns [double value]
        :               p1 = signExpr   {$value = $p1.value;}
                (       p2 = funcExpr   {$value *= $p2.value;}
                | '*'   p2 = signExpr   {$value *= $p2.value;}
                | '/'   p2 = signExpr   {$value /= $p2.value;}
                )*
        ;

Thank you again, Bernard.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!