I'm writing a compiler for a C-like language, and I'm looking for an elegant way to traverse my abstract syntax tree. I'm trying to implement the Visitor pattern, although I'm not convinced that I'm doing it correctly.
struct Visitor {
// Expressions
virtual void visit(AsgnExpression&);
virtual void visit(ConstantExpression&);
...
virtual void visit(Statement&);
...
virtual void finished(ASTNode&);
protected:
virtual void visit(ASTNode&) = 0;
};
visit
is overloaded for each type, and by default each overload will call visit(ASTNode&)
which subclasses are forced to implement. This makes it easier to do quick and dirty things, although defining a visit
for each type is tedious. Each subclass of ASTNode
must implement an accept
method which is used to traverse the tree structure.
class ASTNode {
public:
virtual ~ASTNode();
virtual void accept(Visitor& visitor) = 0;
};
However, this design is quickly becoming tedious because the accept
methods are often very similar.
Who should be responsible for traversing the structure, the nodes or the visitor? I'm leaning towards having ASTNode
provide an iterator for accessing its children, and then having the visitor traverse the structure. If you have any experience designing Abstract Syntax Trees, please share your wisdom with me!