Geff
Geff

Reputation: 49

ANTLR's parser enters into "wrong" rule

I'm trying to create an interpreter for a simple programming language using ANTLR. So far it consists of print and numeric expressions. I created a 'simpleExpr' parser rule to handle negative numbers. I tried other ways, too, but that's the only one which seems to work right for me. However, for some reason my visitor enters to this rule, even if I would expect it to visit my 'number' rule. I really think, that it's not the visitor's fault, because even the tree drawn by ANTLR shows this behavior. It's weird, but it would be OK, but my problem is, that when I try to print out the result of a simple addition, e.g. print(1+2); then it doesn't do that, but enters into 'number' rule instead of 'Plus' rule.

My grammar:

grammar BatshG;
/*
 * Parser Rules
 */
compileUnit: (expression | ( println ';') | ( print ';' ))+;
expression:     
                left=expression '/' right=simpleExpr #Divi
            |   left=expression '*' right=simpleExpr #Mult
            |   left=expression '-' right=simpleExpr #Minus
            |   left=expression '+' right=simpleExpr #Plus
            |   number=simpleExpr                    #Number
;
println: 'println' '(' argument=expression ')'; 
print: 'print' '(' argument=expression ')';
simpleExpr
      :   (MINUS)?
          (FLOAT | INTEGER)
      ;
MINUS: '-';
INTEGER: [0-9] [0-9]*;
DIGIT : [0-9]                       ;
FRAC : '.' DIGIT+                   ;
EXP : [eE] [-+]? DIGIT+  ;
FLOAT : DIGIT* FRAC EXP?             ;
WS: [ \n\t\r]+ -> channel(HIDDEN);

If it helps, here is my visualized tree generated by ANTLR for

print(1+2);

enter image description here

Update: The visitor class, if it counts:

public class BatshGVisitor : BatshGBaseVisitor<ResultValue>
{
    public ResultValue Result { get; set; }
    public StringBuilder OutputForPrint { get; set; }

    public override ResultValue VisitCompileUnit([NotNull] BatshGParser.CompileUnitContext context)
    {
        OutputForPrint = new StringBuilder("");
        var resultvalue = VisitChildren(context);
        Result = new ResultValue() { ExpType = "string", ExpValue = resultvalue.ExpValue };
        return Result;
    }

    public override ResultValue VisitPlus([NotNull] BatshGParser.PlusContext context)
    {
        var leftExp = VisitChildren(context.left);
        var rigthExp = VisitChildren(context.right);

        return new ResultValue()
        {
            ExpType = "number",
            ExpValue = (double)leftExp.ExpValue + (double)rigthExp.ExpValue
        };
    }

    //public override ResultValue VisitNumber([NotNull] BatshGParser.NumberContext context)
    //{
    //    return new ResultValue()
    //    {
    //        ExpType = "number",
    //        ExpValue = Double.Parse(context.GetChild(0).GetText() 
    //        + context.GetChild(1).GetText()
    //        + context.GetChild(2).GetText()
    //        , CultureInfo.InvariantCulture)
    //    };
    //}

    public override ResultValue VisitPrint([NotNull] BatshGParser.PrintContext context)
    {
        var viCh = VisitChildren(context.argument);
        var viChVa = viCh.ExpValue;
        string printInner = viChVa.ToString();
        var toPrint = new ResultValue()
        {
            ExpType = viCh.ExpType,
            ExpValue = printInner
        };
        OutputForPrint.Append(toPrint.ExpValue);
        return toPrint;
    }

    public override ResultValue VisitSimpleExpr([NotNull] BatshGParser.SimpleExprContext context)
    {
        string numberToConvert = "";
        if (context.ChildCount == 1)
        {
            numberToConvert = context.GetChild(0).GetText();
        }
        else if (context.GetChild(0).ToString() == "-")
        {
            if (context.ChildCount == 2)
            {
                numberToConvert = "-" + context.GetChild(1);
            }
            if (context.ChildCount == 4)
            {
                numberToConvert = context.GetChild(0).ToString() + context.GetChild(1).ToString() +
                    context.GetChild(2).ToString() + context.GetChild(3).ToString();
            }
        }

        return new ResultValue()
        {
            ExpType = "number",
            ExpValue = Double.Parse(numberToConvert, CultureInfo.InvariantCulture)
        };
    }

    protected override ResultValue AggregateResult(ResultValue aggregate, ResultValue nextResult)
    {
        if (aggregate == null)
            return new ResultValue()
            {
                ExpType = nextResult.ExpType,
                ExpValue = nextResult.ExpValue
            };
        if (nextResult == null)
        {
            return aggregate;
        }
        return null;
    }
}

What's the problem with my grammar? Thank you!

Upvotes: 0

Views: 196

Answers (1)

sepp2k
sepp2k

Reputation: 370092

Inside the visit method for print statements, you have this:

var viCh = VisitChildren(context.argument);

So let's say your input was print(1+2);. Then context.argument would be the PlusContext for 1+2 and the children of context.argument would be a NumberContext for 1, a Token object for + and a SimpleExpression object for 2. So by calling VisitChildren, you're going to visit those children, which is why it never runs VisitPlus and goes directly to the numbers.

Generally, you rarely want to visit the children of some other node. You usually want to visit your own children, not skip the children and directly visit the grand children. So what you should do instead is to call Visit(context.argument);.

Upvotes: 1

Related Questions