Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
public class ParseTreeVisitor {
    public void visit(ParseTreeListener listener, ParseTree t) {
        if ( t instanceof ParseTree.TokenNode) {
            visitToken(listener, (ParseTree.TokenNode) t);
            return;
        }
        ParseTree.RuleNode r = (ParseTree.RuleNode)t;
        enterRule(listener, r);
        int n = r.getChildCount();
        for (int i = 0; i<n; i++) {
            visit(listener, r.getChild(i));
        }
        exitRule(listener, r);
    }

    protected void visitToken(ParseTreeListener listener, ParseTree.TokenNode t) {
        listener.visitToken(t.getToken());
    }

    /** The discovery of a rule node, involves sending two events:
     *  the generic enterRule and a RuleContext-specific event.
     *  First we trigger the generic and then the rule specific.
     *  We to them in reverse order upon finishing the node.
     */
    protected void enterRule(ParseTreeListener listener, ParseTree.RuleNode r) {
        ParserRuleContext ctx = (ParserRuleContext)r.getRuleContext();
        listener.enterRule((ParserRuleContext)r.getRuleContext());
        ctx.enterenterRule(listener);
    }

    protected void exitRule(ParseTreeListener listener, ParseTree.RuleNode r) {
        ParserRuleContext ctx = (ParserRuleContext)r.getRuleContext();
        ctx.exitexitRule(listener);
        listener.exitRule(ctx);
    }
}

...

Rules with multiple alternatives should have a different event for each alternative, but using automatically generated names like alt1(), alt2(), etc. is a bad idea because a change in the grammar will silently break the listener. That means we need to do something like I was talking about previously and specify the name of the event in the listener. Something like this (NEW SYNTAX):

Code Block
atom : INT                         -># anInt
     | ID                          -># anID
     | array=ID '[' index=expr ']' -># anArrayIndex
     ;

ANTLR can collect all of these names and trigger discover and finish methods for them. I don't think it's as useful to limit ourselves to just a single event that occurs after we've processed a rule's nodes. For example, what if you want to do something before the visitor reaches the index of the array reference? We need a listener method for the start and stop of the alternative. Notice that I changed the name from the previous incarnation from visitInt to anInt. This way, ANTLR can generate methods like enter_anInt(), etc. We say what the parser has matched, rather than the specific name of a listener or visitor. Having a discover and finish for alternatives with a single token reference doesn't make much sense but we could ignore one or the other method.

These names can also be used on single alternative rules as well to isolate name changes in the grammar from causing compilation errors in the listener. It would prevent runtime errors for ANTLR targets based upon dynamically typed languages.

This '#' allows AST rewrites with -> and visitors for parse trees.