It is up to the visitor implementation to decide whether to visit child nodes and in which order. That's the whole point of the visitor pattern.
In order to adapt the visitor for more situations it is helpful (and quite common) to use generics like this (it's Java):
public interface ExpressionNodeVisitor<R, P> {
R visitNumber(NumberNode number, P p);
R visitBinary(BinaryNode expression, P p);
// ...
}
And an accept
method would look like this:
public interface ExpressionNode extends Node {
<R, P> R accept(ExpressionNodeVisitor<R, P> visitor, P p);
// ...
}
This allows to pass additional parameters to visitor and retrieve a result from it. So, the expression evaluation can be implemented like this:
public class EvaluatingVisitor
implements ExpressionNodeVisitor<Double, Void> {
public Double visitNumber(NumberNode number, Void p) {
// Parse the number and return it.
return Double.valueOf(number.getText());
}
public Double visitBinary(BinaryNode binary, Void p) {
switch (binary.getOperator()) {
case '+':
return binary.getLeftOperand().accept(this, p)
+ binary.getRightOperand().accept(this, p);
// More cases for other operators here.
}
}
}
The accept
method parameter isn't used in the above example, but just believe me: it is quite useful to have one. For example, it can be a Logger instance to report errors to.
Best Answer
A parse tree is also known as a concrete syntax tree.
Basically, the abstract tree has less information than the concrete tree. The concrete tree contains each element in the language, whereas the abstract tree has thrown away the uninteresting pieces.
For example the expression:
(2 + 5) * 8
The concrete looks like this
Whereas the abstract tree has:
In the concrete cases the parentheses and all pieces of the language have been incorporated into the tree. In the abstract case the parentheses are gone, because their information has been incorporated into the tree structure.