Versions Compared

Key

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

...

Code Block
interface ParseTreeListener {
    void visitToken(Token token);
    void discoverRuleenterRule(ParserRuleContext ctx);
    void finishRuleexitRule(ParserRuleContext ctx);
}

With the discover and finish enter/exit methods, programmers can specify what to do on the way down and on the way up the tree. By using a listener mechanism, I can hide all of the details of a visitor within the ANTLR runtime library. It's too easy to forget that visitor methods must check their children. In this way, listeners just manage the event, not the walking of the tree.

...

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;
        discoverRuleenterRule(listener, r);
        int n = r.getChildCount();
        for (int i = 0; i<n; i++) {
            visit(listener, r.getChild(i));
        }
        finishRuleexitRule(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 discoverRuleenterRule 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 discoverRuleenterRule(ParseTreeListener listener, ParseTree.RuleNode r) {
        ParserRuleContext ctx = (ParserRuleContext)r.getRuleContext();
        listener.discoverRuleenterRule((ParserRuleContext)r.getRuleContext());
        ctx.discoverenter(listener);
    }

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

We want ANTLR to generate an interface like the following that has the rule specific events:

Code Block
interface TListener extends ParseTreeListener {
	void discover_senterRule(s_ctx ctx);
	void finish_sexitRule(s_ctx ctx);
	void discover_ifstatenterRule(ifstat_ctx ctx);
	void finish_ifstatexitRule(ifstat_ctx ctx);
}

Should ANTLR always generate this interface in case it's needed?

So, to specify what to do upon discovering any rule, programmers would override the visitor's discoverRuleenterRule() method. To respond only to the discovery of a specific rule, they would implement a method from the grammar specific interface. For example, imagine the following simple parser grammar:

...

Code Block
public static class s_ctx extends ParserRuleContext {
    public ifstat_ctx i;
    public s_ctx(RuleContext parent, int state) {
        super(parent, state);
    }   
    public void discoverenterRule(TListener listener) { listener.discover_s_ctxenterRule(this); }
    public void finishexitRule(TListener listener) { listener.finish_s_ctxexitRule(this); }
}   

Naturally, we can easily generate a blank listener implementation of TListener so programmers only have to implement the events they care about:

Code Block
TListener listener = new BlankTListener() {
    public void discover_senterRule(s_ctx ctx) { ... }
    ...
};

...

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 discoverenter_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.

...