【问题标题】:ANTLR 4 How to handle unary/negative numbersANTLR 4 如何处理一元/负数
【发布时间】:2018-02-08 20:55:37
【问题描述】:

我正在尝试使用 Antlr 4 设置一个简单的计算器。

语法:

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 代码:

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

如果我的输入是 2 - -2。使用此输入,代码将永远不会进入此方法。请注意,输入有一个负一元运算符,这应该被视为“-2”。当我在方法中放置一个调试点时,永远不会进入。

我的语法设置不应该总是首先考虑一元减号吗?

编辑:似乎在实现一元表达式和上瘾表达式时会出现此错误。在这种情况下,程序将不会使用输入 '2 - -2' 进入一元方法

【问题讨论】:

    标签: java antlr4


    【解决方案1】:

    我的语法设置不应该总是首先考虑一元减号吗?

    是的,应该(而且确实如此)。

    我猜你最近没有重新生成你的 parser 和 lexer 类,因为在我运行这个类时考虑到你的语法(我只添加了 SPACE : [ \t\r\n] -> skip;):

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

    打印以下内容:

    enterAdditiveExpr: 2--2
    enterUnaryMinusExpr: -2
    

    对于访问者,您需要像这样实现所有访问方法:

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

    将打印:

    visitInput: 2--2<EOF>
    visitAdditiveExpr: 2--2
    visitNum: 2
    visitUnaryMinusExpr: -2
    visitNum: 2
    2 - -2 = 4
    

    这是一个表达式解析器/求值器的小演示,包括基本循环和使用 ANTLR4 + 访问者的 if 语句:https://github.com/bkiers/Mu

    【讨论】:

    • 感谢您的回复。我想如果我有两种方法可以同时处理一元运算符和减法运算,就会出现问题。发生这种情况时,代码将永远不会进入一元方法。我已经为此更新了我的帖子。
    • 没问题。但是enterAdditiveExpr是否也被添加了也没关系。请参阅我的编辑。但是现在我看到您使用的不是听众,而是访问者。这会有所不同。我也会发布一个访客演示。
    • 所以根据我的研究,我发现对于更复杂的操作(例如循环和 if 语句)需要使用访问者。这是真的吗?
    • 是的,那么访客更合适。
    • 谢谢。我将实施,看看我是否遇到任何问题。快速提问:这如何处理优先级?它会在加法之前成功执行乘法吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-17
    • 1970-01-01
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    相关资源
    最近更新 更多