为什么Antlr默认某些替代方案而不是其他方案?

时间:2018-02-09 20:42:19

标签: java intellij-idea antlr4

我正在使用IntelliJ的antlr4插件为我正在处理的项目创建语法。在学习如何使用antlr的过程中,我看了下面显示的G​​ithub用户shmatov的简单antlr4计算器。

grammar Calculator;
INT    : [0-9]+;
DOUBLE : [0-9]+'.'[0-9]+;
PI     : 'pi';
E      : 'e';
POW    : '^';
NL     : '\n';
WS     : [ \t\r]+ -> skip;
ID     : [a-zA-Z_][a-zA-Z_0-9]*;

PLUS  : '+';
EQUAL : '=';
MINUS : '-';
MULT  : '*';
DIV   : '/';
LPAR  : '(';
RPAR  : ')';

input
    : plusOrMinus NL? EOF # Calculate
    ;

plusOrMinus 
    : plusOrMinus PLUS multOrDiv  # Plus
    | plusOrMinus MINUS multOrDiv # Minus
    | multOrDiv                   # ToMultOrDiv
    ;

multOrDiv
    : multOrDiv MULT pow # Multiplication
    | multOrDiv DIV pow  # Division
    | pow                # ToPow
    ;

pow
    : unaryMinus (POW pow)? # Power
    ;

unaryMinus
    : MINUS unaryMinus # ChangeSign
    | atom             # ToAtom
    ;

atom
    : PI                    # ConstantPI
    | E                     # ConstantE
    | DOUBLE                # Double
    | INT                   # Int
    | ID                    # Variable
    | LPAR plusOrMinus RPAR # Braces
    ;

有趣的是,当输入单个数字时,例如整数“5”,生成的解析树会显示运行#Plus和#Multiplication标签。这对我来说没有多大意义。输入中没有+*运算符。我已经上传了下面的解析树图片,以便更好地了解我在说什么。

Parse tree

似乎antlr默认为规则中的第一个替代方案,但是计算器采用标记# Calculate -> # ToMultOrDiv -> # ToPow -> # ToAtom -> # Int的直线路径会不会更有意义?如果没有合适的操作员,它甚至如何通过#Plus和#Rultiplication?

1 个答案:

答案 0 :(得分:0)

好观察!我猜测ANTLR插件没有正确显示标签。你可以在这里打开一个问题:https://github.com/antlr/intellij-plugin-v4/issues(编辑,我自己开了一个问题:https://github.com/antlr/antlr4/issues/2222

当您让输入"5"通过侦听器时,您将看到显示正确的路径:

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 CalculatorBaseListener {

        @Override
        public void enterCalculate(CalculatorParser.CalculateContext ctx) {
            System.out.println("Calculate");
        }

        @Override
        public void enterToMultOrDiv(CalculatorParser.ToMultOrDivContext ctx) {
            System.out.println("ToMultOrDiv");
        }

        @Override
        public void enterToPow(CalculatorParser.ToPowContext ctx) {
            System.out.println("ToPow");
        }

        @Override
        public void enterPower(CalculatorParser.PowerContext ctx) {
            System.out.println("Power");
        }

        @Override
        public void enterToAtom(CalculatorParser.ToAtomContext ctx) {
            System.out.println("ToAtom");
        }

        @Override
        public void enterInt(CalculatorParser.IntContext ctx) {
            System.out.println("Int");
        }
    }

    public static void main(String[] args) {
        CalculatorLexer lexer = new CalculatorLexer(CharStreams.fromString("5"));
        CalculatorParser parser = new CalculatorParser(new CommonTokenStream(lexer));
        ParseTreeWalker.DEFAULT.walk(new Listener(), parser.input());
    }
}

将打印:

Calculate
ToMultOrDiv
ToPow
Power
ToAtom
Int