Trouble with ANTLR Visitor Pattern - Not Entering visitExpressFrag Method

55 Views Asked by At

I am working on a lexical and syntax analyser that I amimplementing semantic analysis and intermediate representation generation using ANTLR, and I've encountered a problem with my visitor implementation. Specifically, I'm unable to successfully enter the visitExpressFrag method in my EvalVisitor class. This method is crucial for handling expressions in my language.

Here's a simplified version of my problem:

In my EvalVisitor.java, I have the following methods:

@Override
public Integer visitAddMinusOp(CCALParser.AddMinusOpContext ctx) {
    Integer left = visit(ctx.expression_fragment(0));
    Integer right = visit(ctx.expression_fragment(1));
    if (left == null || right == null) {
        throw new RuntimeException("Left or right operand is null in AddMinusOp");
    }
    System.out.println("visitAddMinusOp - Left: " + left + ", Right: " + right);
    // ...
}

@Override
public Integer visitExpressFrag(CCALParser.ExpressFragContext ctx) {
    if (ctx.expression_fragment() == null) {
        System.out.println("visitExpressFrag - Expression fragment context is null.");
    } else {
        System.out.println("visitExpressFrag - Expression Fragment Text: " + ctx.expression_fragment().getText());
    }
    // ...
}

However, when running my code, the visitExpressFrag method doesn't seem to be invoked as expected. I added print statements for debugging, but they don't get executed, indicating that the method isn't being entered.

I tried directly calling visitExpressFrag like so:

@Override
public Integer visitAddMinusOp(CCALParser.AddMinusOpContext ctx) {
    Integer left = visitExpressFrag(0);
    // ...
}

But this results in a compilation error:

EvalVisitor.java:297: error: incompatible types: int cannot be converted to ExpressFragContext
    Integer left = visitExpressFrag(0);
                                ^

This is the relevant part of my CCALParser.java (i am not fully sure if this is relevant):

// ExpressFragContext definition
public static class ExpressFragContext extends ExpressionContext {
    public Expression_fragmentContext expression_fragment() {
        return getRuleContext(Expression_fragmentContext.class,0);
    }
    // ...
}

And my CCAL.g4 looks like this:

expression : expression_fragment binary_arith_op expression_fragment #AddMinusOp
           | LPAREN expression RPAREN                       #Parens
           | ID LPAREN arg_list RPAREN                      #FunctionCallOp
           | expression_fragment                            #ExpressFrag
           ;

binary_arith_op : PLUS | MINUS;

expression_fragment 
    : ID                                          #ID
    | MINUS ID                                    #NegatedVariable
    | NUMBER                                      #NumberLiteral
    | TRUE                                        #TrueLiteral
    | FALSE                                       #FalseLiteral
    | LPAREN expression RPAREN                    #NestedExpression
    ;

I'm not sure how to properly invoke visitExpressFrag or why it's not being called as expected. Any insights or suggestions on how to resolve this issue would be greatly appreciated.

1

There are 1 best solutions below

0
Bart Kiers On

visitExpressFrag(0) is incorrect, because it expects a ParserRuleContext as a parameter, yet you provide an int as a parameter.

However, when running my code, the visitExpressFrag method doesn't seem to be invoked

It does with me. Using the visitor like this:

class EvalVisitor extends CCALBaseVisitor<Integer> {

    @Override
    public Integer visitParse(CCALParser.ParseContext ctx) {
        return visit(ctx.expression());
    }

    @Override
    public Integer visitAddMinusOp(CCALParser.AddMinusOpContext ctx) {
        Integer left = visit(ctx.expression_fragment(0));
        Integer right = visit(ctx.expression_fragment(1));
        return ctx.binary_arith_op().MINUS() == null ? left + right : left - right;
    }

    @Override
    public Integer visitParens(CCALParser.ParensContext ctx) {
        return visit(ctx.expression());
    }

    @Override
    public Integer visitExpressFrag(CCALParser.ExpressFragContext ctx) {
        return visit(ctx.expression_fragment());
    }

    @Override
    public Integer visitNumberLiteral(CCALParser.NumberLiteralContext ctx) {
        return Integer.valueOf(ctx.NUMBER().getText());
    }

    @Override
    public Integer visitTrueLiteral(CCALParser.TrueLiteralContext ctx) {
        return 1;
    }

    @Override
    public Integer visitFalseLiteral(CCALParser.FalseLiteralContext ctx) {
        return 0;
    }

    @Override
    public Integer visitNestedExpression(CCALParser.NestedExpressionContext ctx) {
        return visit(ctx.expression());
    }
}

and small driver class:

public class CCALDemo {
    public static void main(String[] args) {
        CCALLexer lexer = new CCALLexer(CharStreams.fromString("8 - 3"));
        CCALParser parser = new CCALParser(new CommonTokenStream(lexer));
        Integer answer = new EvalVisitor().visit(parser.parse());
        System.out.println(answer);
    }
}

will print 5 as expected.

You really don't need a expression_fragment rule (which contains a duplicate nested expression) and I suspect you also want to account for -(1+2), not just -ID. An expression rule like this would be better:

expression
 : MINUS expression                      #Negated
 | expression binary_arith_op expression #AddMinusOp
 | LPAREN expression RPAREN              #Parens
 | ID LPAREN arg_list RPAREN             #FunctionCallOp
 | ID                                    #ID
 | NUMBER                                #NumberLiteral
 | TRUE                                  #TrueLiteral
 | FALSE                                 #FalseLiteral
 ;