0
votes

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!

1
All of the alternatives in expression contain a simpleExpr - also the only way to reach FLOAT or INTEGER is through simpleExpr. So it's not surprising that the parse tree always contains simpleExprs. If you don't want that, you should just make MINUS? (FLOAT | INTEGER) one of your alternatives for expression.sepp2k
That said, there's no actual problem with your grammar (except that / and * as well as - and + usually have the same precedence, but don't in your grammar, but that's not what your question is about). You should show us your visitor that doesn't work and explain in more detail how it doesn't work (expected behaviour, actual behaviour).sepp2k
@sepp2k it's not surprising. As I mentioned other approaches didn't work for me. For example if I used directly the MINUS? (FLOAT | INTEGER) then when I tried to parse e.g. print(1-2); then it didn't recognise '-' as substraction operator but as part of -2, so there was no operator between two numbers so it run into error. But maybe you're right and I shouldn't expect to enter the 'number' rule if there is 'simpleExpr'.Geff
There is no Number rule - there's a Number alternative in the expression rule. And you do enter that. The generated parse tree node will have type NumberContext (which extends ExpressionContext), it's just not displayed as such in the tree in the GUI because it displays the names of the rules, not the names of the alternatives.sepp2k
"if I used directly the MINUS? (FLOAT | INTEGER) then when I tried to parse e.g. print(1-2); then it didn't recognise '-' as substraction operator but as part of -2" I don't see why that would happen, so I think you actually did something else (like allowing INTEGER and FLOAT to start with a - or creating a NUMBER token that can start with a -), mixed up the result of trying different things or misinterpreted what was happening.sepp2k

1 Answers

1
votes

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