0

I an trying to extend the grammar given in the following link

I want to evaluate expressions like Height* @cos(x+y) here @cos is my project function. i want to add many such other system function

in my grammar added

CosExp return [double value]
: exp=additionExp {$value = Math.cos($exp.value);}
;

complete grammar (edited the working grammar) is given below:

grammar Exp;
@header {
    package antlr.output;
    import java.util.HashMap;
}
@parser::members {

  private java.util.HashMap<String, Double> memory = new java.util.HashMap<String, Double>();

  public static Double eval(String expression) throws Exception {
    return eval(expression, new java.util.HashMap<String, Double>()); 
  }

  public static Double eval(String expression, java.util.Map<String, Double> vars) throws Exception {
    ANTLRStringStream in = new ANTLRStringStream(expression);
    ExpLexer lexer = new ExpLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    ExpParser parser = new ExpParser(tokens);
    parser.memory.putAll(vars);
    return parser.parse(); 
  }
}

parse returns [double value]
    :    exp=additionExp {$value = $exp.value;}
    ;

additionExp returns [double value]
    :    m1=multiplyExp      {$value =  $m1.value;} 
        ( '+' m2=multiplyExp {$value += $m2.value;} 
        | '-' m2=multiplyExp {$value -= $m2.value;}
        )*  
    ;

multiplyExp returns [double value]
    :   a1=atomExp       {$value =  $a1.value;}
        ( '*' a2=atomExp {$value *= $a2.value;} 
        | '/' a2=atomExp {$value /= $a2.value;}
        )*  
    ;

atomExp returns [double value]
    :    n=Number                {$value = Double.parseDouble($n.text);}
    |    i=Identifier            {$value = memory.get($i.text);}
    |    '(' exp=additionExp ')' {$value = $exp.value;}
    ;

CosExp return [double value]  // I have added these lines
: exp=additionExp {$value = Math.cos($exp.value);} // I have added these lines
;
Identifier
    :    ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
    ;

Number
    :    ('0'..'9')+ ('.' ('0'..'9')+)?
    ;

WS  
    :   (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
    ;

in my Java code

Map<String, Double> vars = new HashMap<String, Double>();
vars.put("Height", 12.0);
vars.put("x", 2.0);
vars.put("y", 2.0);
System.out.println(ExpParser.eval("Height* @cos(x+y)", vars));

when i run the code i am getting java.lang.NullPointerException i know there is error in grammar but not able to figure out.

mleko
  • 11,650
  • 6
  • 50
  • 71

1 Answers1

0

Your grammar cannot parse @cos in the string "Height* @cos(x+y)". Most probably you had a token recognition error, and cos becomes an Identifier which is not in the memory Map, hence the NullPointerException in the statement memory.get.

$ java test_exp
Found ID=<Height>
Found Height=12.0
line 1:9 token recognition error at: '@'
Found ID=<cos>
Exception in thread "main" java.lang.NullPointerException
    at ExpParser.atomExp(ExpParser.java:391)
    at ExpParser.multiplyExp(ExpParser.java:312)
    at ExpParser.additionExp(ExpParser.java:216)
    at ExpParser.expression(ExpParser.java:170)
    at ExpParser.parse(ExpParser.java:130)
    at ExpParser.eval(ExpParser.java:97)
    at test_exp.main(test_exp.java:9)

The following grammar works in v4.6.

File Exp.g4 :

grammar Exp;

@header {
//    package antlr.output;
  import java.util.HashMap;
}

@parser::members {

  private HashMap<String, Double> memory = new java.util.HashMap<String, Double>();

  public static Double eval(String expression) throws Exception {
    return eval(expression, new java.util.HashMap<String, Double>()); 
  }

  public static Double eval(String expression, java.util.Map<String, Double> vars) throws Exception {
    ANTLRInputStream in = new ANTLRInputStream(expression);
    ExpLexer lexer = new ExpLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    ExpParser parser = new ExpParser(tokens);
    parser.memory.putAll(vars);
    ParseContext ctx = parser.parse();
    return ctx.expression().value;
  }
}

parse
@init {System.out.println("Exp last update 1843");}
    :   expression
        {System.out.println("Expression " + $expression.text + "=" + $expression.value);}
    ;

expression returns [double value]
    :   e1=expression            {$value =  $e1.value;}
        (   op='*' e2=expression {$value *= $e2.value;} 
          | op='/' e2=expression {$value /= $e2.value;}
        )
        {System.out.println("Mult " + $e1.text + ($op.text != null ? $op.text + $e2.text + "=" + $value : ""));}
    |   e1=expression            {$value =  $e1.value;} 
        (   op='+' e2=expression {$value += $e2.value;} 
          | op='-' e2=expression {$value -= $e2.value;}
        )
        {System.out.println("Add " + $e1.text + ($op.text != null ? $op.text + $e2.text + "=" + $value : ""));}
    |   '(' expression ')'       {$value = $expression.value;}
    |   function                 {$value = $function.value;
                                  System.out.println("Function " + $function.text + "=" + $value);
                                 }
    |   atom                     {$value = $atom.value;}
    ;

function returns [double value]
    :   '@' 'cos' expression {$value = Math.cos($expression.value);}
    |   '@' 'sin' expression {$value = Math.sin($expression.value);}
    ;

atom returns [double value]
    :   NUMBER                {$value = Double.parseDouble($NUMBER.text);}
    |   IDENTIFIER            {System.out.println("Found ID=<" + $IDENTIFIER.text + ">");
                               $value = memory.get($IDENTIFIER.text);
                               System.out.println("Found " + $IDENTIFIER.text + "=" + $value);
                              }
    ;

IDENTIFIER
    :    ( LETTER | '_' ) ( LETTER | '_' | DIGIT )*
    ;

NUMBER
    :    DIGIT+ ( '.' DIGIT+ )?
    ;


WS      : [ \t\r\n] -> channel(HIDDEN) ;

fragment LETTER : [a-zA-Z] ;
fragment DIGIT  : [0-9] ;

Note that I have introduced op= only for the trace output. Also note that the addition and multiplication cannot have an *, as in

:    m1=multiplyExp      {$value =  $m1.value;} 
    (   op='+' m2=multiplyExp {$value += $m2.value;} 
      | op='-' m2=multiplyExp {$value -= $m2.value;}
    )* 

It generates an error :

error(148): Exp.g4:32:0: left recursive rule expression contains a left recursive
alternative which can be followed by the empty string

File test_exp.java :

import java.util.*;

public class test_exp {
    public static void main(String[] args) throws Exception {
        Map<String, Double> vars = new HashMap<String, Double>();
        vars.put("Height", 12.0);
        vars.put("x", 2.0);
        vars.put("y", 2.0);
        System.out.println("Result=" + ExpParser.eval("Height * @cos(x + y)", vars));
    }
}

Execution :

$ export CLASSPATH=".:/usr/local/lib/antlr-4.6-complete.jar"
$ alias
alias a4='java -jar /usr/local/lib/antlr-4.6-complete.jar'
$ a4 Exp.g4 
$ javac E*.java
$ javac test_exp.java 
$ java test_exp
Exp last update 1843
Found ID=<Height>
Found Height=12.0
Found ID=<x>
Found x=2.0
Found ID=<y>
Found y=2.0
Add x+y=4.0
Function @cos(x + y)=-0.6536436208636119
Mult Height*@cos(x + y)=-7.843723450363344
Expression Height * @cos(x + y)=-7.843723450363344
Result=-7.843723450363344
BernardK
  • 3,674
  • 2
  • 15
  • 10
  • Thanks @BernardK :) it is working absolutely fine..though lot of "system.out.println" are irritating :)) – Pramod Azad Nov 08 '17 at 01:26
  • It's precisely with print trace, namely `Found ID=` followed by NullPointerException, that I have discovered the error. It's the best way I know to see which rules are matched. – BernardK Nov 08 '17 at 02:02