Skip to content

Organizing Visitors

Luis Diogo Couto edited this page Nov 21, 2014 · 1 revision

At some point, everyone has to implement a visitor over the AST. This page helps you organize one.

The details of implementing a visitor and its cases depend heavily on what functionality you want so we will not discuss it here. However, organizing the cases and distributing the visitor across classes should always be done in the same way. That makes it easier for everyone to navigate the code.

As you know, we have a very big AST. That means a lot of visitor cases (around 280 max). Therefore, you should organize your visitor in the following manner:

  • Single class visitor - this is only recommended for smaller visitors. What's a smaller visitor? That's an open question but try to keep classes under 2000 lines of code. If your visitor exceeds this, it's not small.

  • Root and sub- visitors - this is recommended for visitors that are too big for one class. You have a root visitor that acts as the main entry point into the functionality and that is applied from the outside. This visitor has subvisitor instance variables, each of which is responsible for a subset of the AST nodes. We recommend that you divide these subvisitors according to the P families of the AST (PExp, PDefinition, etc.) as they have corresponding default cases and provide a clean organization of all nodes.

Here is a quick example of a root visitor dispatching to 3 subvisitors:

public class RootVisitor extends AnalysisAdaptor {

	AnalysisAdaptor expVisitor = new ExpVisitor(this);
	AnalysisAdaptor defVisitor = new DefVisitor(this);
	AnalysisAdaptor stmVisitor = new StmVisitor(this);

	@Override
	public void defaultPExp(PExp node) throws AnalysisException {
		node.apply(expVisitor);
	}

	@Override
	public void defaultPDefinition(PDefinition node) throws AnalysisException {
		node.apply(defVisitor);
	}

	@Override
	public void defaultPStm(PStm node) throws AnalysisException {
		node.apply(stmVisitor);
	}

}

And a subvisitor with a reference back to the root and some re-dispatch.

public class DefVisitor extends AnalysisAdaptor {

	RootVisitor rootVisitor ;
	
	public DefVisitor(Root root) {
		this.rootVisitor=root;
	}

	@Override
	public void caseAExplicitFunctionDefinition(AExplicitFunctionDefinition node)
			throws AnalysisException {
		node.getPredef().apply(this);
		node.getBody().apply(rootVisitor);
		//...
	}

	//further definition cases...
	
	@Override
	public void defaultINode(INode node) throws AnalysisException {
		node.apply(rootVisitor);
	}

One final note: you may run into a few visitors with cases distributed over an inheritance chain. Do not follow this. That code is out of date and should be refactored.