It depends on what you want to do over that tree.
I usually implement it by creating a specific node for every kind of operation I would need, for example
ASTBinaryOperation implements ASTNode
{
ASTNode left, right;
Operator op;
Result visit()
{
Result lr = left.visit();
Result rr = right.visit();
return op.apply(lr, rr);
}
}
for a classical binary operator node, while I would use an ArrayList
for example for declarations:
ASTDecl implements ASTNode
{
String name;
Type type;
Value value;
}
ASTDecls implements ASTNode
{
ArrayList<ASTDecl> declarations;
}
that is built up by the parser. So a root node would be something like:
ASTRoot {
ASTDecls declarations;
ASTFunctions functions;
}
ASTFunctions {
ASTDecls args;
ASTBody body;
..
}
ASTBody {
ArrayList<ASTStatement> statements;
...
}
and so on.
Of course it depends what you will want to do, I used this approach to visit the AST to generate an intermediate code by recursively visiting the tree.. but storing whatever in an ArrayList
will make you lose the specific behaviour and meaning of a single ASTNode
.