问题
I'm trying to setup a simple calculator with Antlr 4.
Grammar:
grammar calcGrammar;
input : expression EOF;
expression :
MINUS expression #unaryMinusExpr
| expression op=(MULTIPLY | DIVIDE) expression #multiplicationExpr
| expression op=(MINUS | ADD) expression #additiveExpr
| NUMBER #num
;
NUMBER : [0-9]+ ;
DOUBLE : NUMBER '.' NUMBER;
LPAR : '(';
RPAR : ')';
ADD : ('+');
MINUS : ('-');
DIVIDE : ('/');
MULTIPLY : ('*');
Java Code:
public class Listener extends ListenerBaseVisitor {
@Override
public Object visitUnaryMinusExpr(ArithmeticGrammarParser.UnaryMinusExprContext ctx) {
System.out.println(ctx.children.get(0).getText());
}
@Override
public Object visitAdditiveExpr(ArithmeticGrammarParser.AdditiveExprContext ctx) {
System.out.println(ctx.children.get(0).getText());
System.out.println(ctx.children.get(1).getText());
}
If my input is 2 - -2. The code will never go into this method with this input. Notice that the input has a negative unary operator, this should be perceived as '-2'. When i put a debug point in the method is never enters.
With my grammar setup should it not be the case that it should always take into consideration the unary minus first?
EDIT: It seems that this error arises when both unary and addictive expressions are implemented. The program will not go into the unary method in this case with input '2 - -2'
回答1:
With my grammar setup should it not be the case that it should always take into consideration the unary minus first?
Yes, it should (and it does).
I'm guessing you didn't regenerate your parser and lexer classes recently, because given your grammar (I only added SPACE : [ \t\r\n] -> skip;), when I run this class:
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
public class Main {
static class Listener extends calcGrammarBaseListener {
@Override
public void enterUnaryMinusExpr(calcGrammarParser.UnaryMinusExprContext ctx) {
System.out.println("enterUnaryMinusExpr: " + ctx.getText());
}
@Override
public void enterAdditiveExpr(calcGrammarParser.AdditiveExprContext ctx) {
System.out.println("enterAdditiveExpr: " + ctx.getText());
}
}
public static void main(String[] args) {
String source = "2 - -2";
calcGrammarLexer lexer = new calcGrammarLexer(CharStreams.fromString(source));
calcGrammarParser parser = new calcGrammarParser(new CommonTokenStream(lexer));
ParseTreeWalker.DEFAULT.walk(new Listener(), parser.input());
}
}
the following is printed:
enterAdditiveExpr: 2--2
enterUnaryMinusExpr: -2
For a visitor, you'll need to implement all your visit-methods like this:
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
public class Main {
static class Visitor extends calcGrammarBaseVisitor<Integer> {
@Override
public Integer visitInput(calcGrammarParser.InputContext ctx) {
System.out.println("visitInput: " + ctx.getText());
return visit(ctx.expression());
}
@Override
public Integer visitUnaryMinusExpr(calcGrammarParser.UnaryMinusExprContext ctx) {
System.out.println("visitUnaryMinusExpr: " + ctx.getText());
return -1 * visit(ctx.expression());
}
@Override
public Integer visitNum(calcGrammarParser.NumContext ctx) {
System.out.println("visitNum: " + ctx.getText());
return Integer.parseInt(ctx.getText());
}
@Override
public Integer visitMultiplicationExpr(calcGrammarParser.MultiplicationExprContext ctx) {
System.out.println("visitMultiplicationExpr: " + ctx.getText());
if (ctx.op.getType() == calcGrammarLexer.MULTIPLY) {
return visit(ctx.expression(0)) * visit(ctx.expression(1));
}
return visit(ctx.expression(0)) / visit(ctx.expression(1));
}
@Override
public Integer visitAdditiveExpr(calcGrammarParser.AdditiveExprContext ctx) {
System.out.println("visitAdditiveExpr: " + ctx.getText());
if (ctx.op.getType() == calcGrammarLexer.ADD) {
return visit(ctx.expression(0)) + visit(ctx.expression(1));
}
return visit(ctx.expression(0)) - visit(ctx.expression(1));
}
}
public static void main(String[] args) {
String expression = "2 - -2";
calcGrammarLexer lexer = new calcGrammarLexer(CharStreams.fromString(expression));
calcGrammarParser parser = new calcGrammarParser(new CommonTokenStream(lexer));
Integer answer = new Visitor().visit(parser.input());
System.out.printf("%s = %s\n", expression, answer);
}
}
which will print:
visitInput: 2--2<EOF>
visitAdditiveExpr: 2--2
visitNum: 2
visitUnaryMinusExpr: -2
visitNum: 2
2 - -2 = 4
Here's a small demo of an expression parser/evaluator including basic loops and if statements using ANTLR4 + a Visitor: https://github.com/bkiers/Mu
来源:https://stackoverflow.com/questions/48694645/antlr-4-how-to-handle-unary-negative-numbers