Reputation: 460
I need to create a simple compiler for a calculator and I'm using antlr4 and the Visitor-Pattern. this is my grammar file Simple_Calculator.g4 :
grammar Simple_Calculator;
program : statements ;
statements : statement | statements statement ;
statement : identifier '=' expr ';' #assign
| 'begin' statements 'end' #brac
| 'if' expr 'then' statement #if
| 'if' expr 'then' statement 'else' statement #if_elset
| 'while' expr 'do' statement #while
| 'for' identifier '=' number ':' number 'do' statement #for
| 'print' identifier ';' #print
;
expr : expr binop expr #ope
| '!' expr #invert
| '(' expr ')' #parenthesis
| identifier #identify
| number #num
;
binop : '+'
| '-'
| '*'
| '/'
| '<'
| '>'
| '<='
| '>='
| '=='
| '!='
| '^'
;
identifier : STRING+('-'|STRING|INT)* ;
number : INT+('.'INT+)? ;
STRING: [a-zA-Z]+ ;
INT : [0-9]+ ;
WS : [ \t\r\n] -> skip ;
and the follow is visitOpe
and visitAssign
methods in MainVisitor extends Simple_CalculatorBaseVisitor
class :
public class MainVisitor extends Simple_CalculatorBaseVisitor<Object> {
@Override
public Object visitAssign(Simple_CalculatorParser.AssignContext ctx) {
String id = (String) (visit(ctx.identifier()));
String value = (String)(visit(ctx.expr()));
if (storage.containsKey(id)) {
storage.replace(id, value);
} else {
storage.put(id, value);
}
return storage; //To change body of generated methods, choose Tools | Templates.
} // end of visitAssign
@Override
public Object visitOpe(Simple_CalculatorParser.OpeContext ctx) {
String leftOperand = (String) visit(ctx.expr(0));
String rightOperand = (String) visit(ctx.expr(1));
/*if (rightOperand.matches("^\\d+$")) {
return rightOperand;
}*/
String Operation = ctx.binop().getText();
switch (Operation) {
case "+": {
leftOperand = setOperands(leftOperand);
rightOperand = setOperands(rightOperand);
return String.valueOf(Integer.parseInt(leftOperand) + Integer.parseInt(rightOperand));
}
case "-":
leftOperand = setOperands(leftOperand);
rightOperand = setOperands(rightOperand);
return String.valueOf(Integer.parseInt(leftOperand) - Integer.parseInt(rightOperand));
case "*":
leftOperand = setOperands(leftOperand);
rightOperand = setOperands(rightOperand);
return String.valueOf(Integer.parseInt(leftOperand) * Integer.parseInt(rightOperand));
case "/":
leftOperand = setOperands(leftOperand);
rightOperand = setOperands(rightOperand);
return String.valueOf(Integer.parseInt(leftOperand) / Integer.parseInt(rightOperand));
// the rest possible conditions
}
//other methods
}// end of visitOpe
the problem is, when i wanna use '-' operator in an assign expression, when the program read the line String value = (String)(visit(ctx.expr()));
in visitAssign
, it wont visit visitOpe
after that, instead it returns the whole expression.
for example, when I give the program :
i=5;
i=i+2;
print i;
as input, it works fine. it stores i
as an identifier in a HashMap
, and in second line, it will add 2 units to it and print its value finally.
but if i change i=i+2;
to i=i-2
, it stores i
with initial value of 5, but in second line, it just doesn't go through visitOpe
method, instead it returns "i-2" and stores this as value of i
and finally print it.
'*' and '/' operators work fine as addition. It is only about '-' and i don't know what is the problem.
So, How to fix this:
Regards
Upvotes: 1
Views: 133
Reputation: 7409
Different observation, but if you're making a calculator or anything that evaluates math expressions, you might also rethink your binop
precedence. Within a single group they are evaluated top to bottom. It's not like old BNF grammars where people did tricks and the higher precedence operators fell to the bottom. So in your case, an expression like 3+2*5 would yield 25 instead of 13 because addition is of higher precedence than multiplication. Notice how this excellent grammar (Mu) from Bart Kiers precedence is laid out:
expr
: expr POW<assoc=right> expr #powExpr
| MINUS expr #unaryMinusExpr
| NOT expr #notExpr
| expr op=(MULT | DIV | MOD) expr #multiplicationExpr
| expr op=(PLUS | MINUS) expr #additiveExpr
| expr op=(LTEQ | GTEQ | LT | GT) expr #relationalExpr
| expr op=(EQ | NEQ) expr #equalityExpr
| expr AND expr #andExpr
| expr OR expr #orExpr
| atom #atomExpr
;
Highest precedence at top.
Upvotes: 2
Reputation: 5568
With that grammar fragment
identifier : STRING+('-'|STRING|INT)* ;
i-2
is a valid identifier. One option would be to remove the '-'
from identifier.
So:
identifier : STRING+(STRING|INT)* ;
Upvotes: 1