Search code examples
javaantlrantlr4

Implementing translator using ANTLR by embedding semantic actions as Java code in the g4 grammar file


Trying to edit the below grammar and define the semantic actions required to translate arithmetic expressions written in the language defined below into postfix notation.

grammar Expr;
 
expr: expr ('*'|'/') term
    | expr ('+'|'-') term
    | term
    ;
term: '('expr')'
    | ID
    | NUM
    ;

ID: [a-z]+;
NUM: [0-9]+;

WS:  [\t\r\n]+->skip;

To implement the translator I'm embedding semantic actions as Java code in the g4 grammar file by using the push and pop operations of a stack. But when compiling I'm hit with a bunch of errors.

Here is what I have so far:

grammar Expr;
 
expr: expr (op=('*'|'/') term
  { Stack<String> s = new Stack<String>();
             s.push($op.text);
             s.push($term.text);
    String result = s.pop() + s.pop() + s.pop();
             $expr.text = result;
          }
   ) 
    | expr (op=('+'|'-') term
  { Stack<String> s = new Stack<String>();
             s.push($op.text);
             s.push($term.text);
    String result = s.pop() + s.pop() + s.pop();
             $expr.text = result;
          }
   )
    | term
    ;
term: '('expr')' { Stack<String> s = new Stack<String>();
     s.push($expr.text);
  String result = s.pop();
     $term.text = result;
   }
    | ID {Stack<String> s = new Stack<String>();
    s.push($ID.text);
 String result = s.pop();
    $term.text = result;
   } 

    | NUM {Stack<String> s = new Stack<String>();
    s.push($NUM.text);
 String result = s.pop();
    $term.text = result;
   } 
    ;

ID: [a-z]+;
NUM: [0-9]+;

WS:  [\t\r\n]+->skip;

When compiling I'm hit with an error block

enter image description here

I'm not sure how to fix this or if there's a better way to implement.


Solution

  • There are a number of issues here:

    0 - Bart already mentioned how to import Stack, and that you can't assign to $term.text.

    1 - If you want ANTLR to parse with the proper order of evaluation, you'll need expr on both sides of your operator alternatives.

    2 - in the rule expr (op=('*'|'/') term) (omitting the action code), the ( and ) have no impact.

    3 - your WS rule didn't include the space character.

    4 - always initiate your parsing with a rule the ends with one EOF (this forces the parser to attempt to consume all of the input. Without it, ANTLR may "stop short" if that's all it can recognize.

    5 - You don't really need to create a new Stack instance in every action (the way you're pushing and popping in your actions basically just uses that stack as a place to define temp values, and doing no real processing between pushing and popping.). A global stack, however, can be used to good effect.

    With all of those things in mind...

    grammar Expr
        ;
    
    @header {
    import java.util.Stack;
    
    }
    
    @members {
    Stack<String> stack = new Stack<String>();
    }
    
    start: expr EOF;
    
    expr
        : expr op = ('*' | '/') expr {
            var e2 = stack.pop();  // second expr is on top of stack
            stack.push( stack.pop() + " " + e2 + " " + $op.text);
          }
        | expr op = ('+' | '-') expr {
            var e2 = stack.pop(); // second expr is on top of stack
            stack.push( stack.pop() + " " + e2 + " " + $op.text);
          }
        | term // already on stack
        ;
    term
        : '(' expr ')' // already on stack
        | ID { stack.push($ID.text); }
        | NUM { stack.push($NUM.text); }
        ;
    
    ID:  [a-z]+;
    NUM: [0-9]+;
    
    WS: [ \t\r\n]+ -> skip;