1

I am using the antlr4 plugin for IntelliJ to create a grammar for a project I'm working on. In the process of learning how to use antlr, I took a look at Github user shmatov's simple antlr4 calculator shown below.

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
    ;

Interestingly, when a single number is entered, such as the integer '5', the resulting parse tree shows that the # Plus and # Multiplication labels are run. This doesn't make a lot of sense to me. There are no + or * operators in the input. I've uploaded an image of the parse tree below to give a better idea of what I'm talking about.

Parse tree

It seems that antlr just defaults to whatever the first alternative in the rule is, but wouldn't it make more sense for the calculator to take the straight path through labels # Calculate -> # ToMultOrDiv -> # ToPow -> # ToAtom -> # Int? How does it even go through # Plus and # Multiplication without the right operators?

Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
Bankst
  • 97
  • 1
  • 1
  • 4

1 Answers1

0

Good observation! I'm guessing the ANTLR plugin doesn't display the labels correctly. You could open an issue here: https://github.com/antlr/intellij-plugin-v4/issues (EDIT, opened an issue myself: https://github.com/antlr/antlr4/issues/2222)

When you let the input "5" go through a listener, you will see the correct path is displayed:

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

which will print:

Calculate
ToMultOrDiv
ToPow
Power
ToAtom
Int
Bart Kiers
  • 166,582
  • 36
  • 299
  • 288