Reputation: 5249
I have a mini AST structure in which each node may have a left and a right child, for example:
class AstNode;
typedef std::shared_ptr<AstNode> AstNodePtr;
class AstNode
{
public:
AstNode()
: m_children(2)
{
}
virtual ~AstNode()
{
}
virtual void accept(AstNodeVisitor& visitor) = 0;
void addLeft(const AstNodePtr& child);
void addRight(const AstNodePtr& child);
const AstNodePtr left() const;
const AstNodePtr right() const;
private:
std::vector<AstNodePtr> m_children;
};
It works great for the operations I need so far, but when it comes to a branch statement, I don't know how to implement it with this binary tree structure. According to wiki, a branch statement will have 3 leaves:
I can get away with it for now because most of my if statement has no else, so the condition will be the left child, and if-body will be the right child. But it's not going to work with a else-body. I can potentially embed condition in the branch node itself, which means do a pre-order traversal on branch node, but it feels uncomfortable because no other type of nodes involve potential subtree traversal when evaluating itself.
Maybe the AST should not be a binary tree, rather each node can have any number of children, but that will(I think) make the implementation a bit awkward. Any suggestions please?
Upvotes: 6
Views: 13502
Reputation: 45675
You could define an abstract AST node which doesn't hold any children. Then for each number of child nodes ("arity"), define a different subclass:
return
or for unary operators such as negationif-then-else
constructs as well as for the ternary operator ?!
switch-case
construct, if you want to support them. Your statement-sequence
perfectly fits into this node type, too. If you don't implement this node type, you could put statement sequences in a binary tree structure, but that sounds like a dirty hack.for
loops. They have an initial, a conditional and an incremental statement plus a body.Note that implementing everything with dynamically sized children lists is a bad idea in my opinion, since it doesn't make sense to have a node of type operator =
with only one child, as an example.
Then, inherit the concrete node types from the node class corresponding to the arity.
class ASTNode {
public:
virtual ASTNode() {}
virtual void accept(AstNodeVisitor& visitor) = 0;
};
// ----
class ASTNodeUnary : public ASTNode {
protected:
AstNodePtr c1;
};
class ASTNodeBinary : public ASTNode {
protected:
AstNodePtr c1, c2;
};
class ASTNodeTernary : public ASTNode {
protected:
AstNodePtr c1, c2, c3;
};
class ASTNodeDynamic : public ASTNode {
protected:
std::vector<AstNodePtr> children;
};
// ----
class ASTNodeBranch : public ASTNodeTernary {
...
};
and so on
Upvotes: 6
Reputation: 1686
By the nature, ASTs should be implemented in multi-child trees to support if-condition-then expressions. But a workaround could be having 2 types for IF;
left child of the if-body is used if condition of the parent is true, right child is used otherwise.
Upvotes: 7