1
votes

I can't believe that I am the first man to ask this question! When using ANTLR4, I need a visitor to walk through the parse tree and make some modifications, so I need to access the subtrees within each tree node. Here is a snippet of my Fortran grammar:

ifStatement
    : IF_KEYWORD expression
      ( ( THEN_KEYWORD
          executableStatement*
          elseIfStatement*  // <--- problem is here
          elseStatement?
          END_KEYWORD IF_KEYWORD
        ) | executableStatement )
    ;

elseIfStatement
    : ELSE_KEYWORD IF_KEYWORD expression THEN_KEYWORD executableStatement*
    ;

As you can see, there is repeating subtree elseIfStatement in ifStatement. When I create a visitor for the parse tree, I want to access the contexts of all the elseIfStatement that are parsed:

public Void visitIfStatement(FortranParser.IfStatementContext ctx) {
    ...
    for (FortranParser.ElseIfStatementContext elsIf : ctx.elseIfStatement()) // ERROR!!!
        visitElseIfStatement(elseIf);
    ...
    return null;
}

But ctx.elseIfStatement() only returns the first occurrence of elseIfStatement as:

if (a == 1) then
    a = 2
else if (b == 1) then |
    b = 3             | -> returned by ctx.elseIfStatement()
else if (c == 1) then \
    c = 4             \ -> ignored??
else
    d = 4
end if

So how to access all the elseIfStatement subtrees? And this question applies to all the parser rule pattern with '*' as executableStatement* above.

1

1 Answers

1
votes

Could be a bug, if I remove the | executableStatement alternative, ctx.elseIfStatement() does return a List<ElseIfStatementContext>. You might want to report it: https://github.com/antlr/antlr4/issues

However, as you're doing it now, you'll be forced to do some null checks to see which of the alternatives matched. A nicer way would be to "label" your alternatives:

ifStatement
 : IF_KEYWORD expression
   THEN_KEYWORD
   executableStatement* 
   elseIfStatement*  
   elseStatement? END_KEYWORD IF_KEYWORD     #ifMultipleStatements
 | IF_KEYWORD expression executableStatement #ifSingleStatement
 ;

which will generate the following:

public static class IfMultipleStatementsContext extends IfStatementContext {
    ...
    public List<ElseIfStatementContext> elseIfStatement() {
        return getRuleContexts(ElseIfStatementContext.class);
    }
    ...
}

I.e.: it generates the proper amount of ElseIfStatementContexts.