I need some advice on my AST design. I'm using a pretty typical AST node system for a simple interpreter.
At this point I'm trying to implement simple constant propagation. This involves transformations of the node tree which has led to some confusion about the best design for my AST, in particular how to access class members from upcasted Node leafs.
class Node {
public:
Node* parentNode;
std::vector<Node*> childNodes;
Node() {}
};
class Var : public Node {
public:
std::string identfier;
Var();
Var(std::string cidentfier) {
identfier = cidentfier;
}
};
class Assign : public Node {
public:
Node* right;
Var* left;
Assign();
Assign(Var* cleft, Node* cright) {
cleft->parentNode = this;
cright->parentNode = this;
left = cleft;
right = cright;
childNodes.push_back(cleft);
childNodes.push_back(cright);
}
};
Suppose I have a assign Node which looks roughly like x = y
Var* leftNode = new Var("x");
Var* rightNode = new Var("y");
Node* assignNode = new AssignNode(leftNode, rightNode);
Now I want to replace the right side of the assign node (y) with a new Var Node (z), so that my new expression looks like this x = z
. However I only have a reference to the (y) Var Node via the made up getNodeToBeReplaced()
function.
Node* &rightNode = getNodeToBeReplaced(); //returns y
Node* assignNode = rightNode->parentNode; //returns assign node with x = y
Var* replacementNode = new Var("z");
I need to change the assignNode
's right
member to replacementNode
in order to modify the tree (also change replacementNode
's parent to assignNode
but that's fairly trivial).
The issue is, what is the best way to access assignNode
's right
pointer when the assignNode is upcasted so it is a Node
object. I do not want to do any kind of expensive downcasting back to Assign*
.
*assignNode->right = *replacementNode; // can't be done as assignNode is upcasted to Node*
The only bit of information I can modify is the pointers to the class members stored within the childNodes
vector that each Node has.
Therefore this code works fine for this purpose.
Node* &rightNode = getNodeToBeReplaced(); //returns y
Node* assignNode = rightNode->parentNode; //returns assign node with x = y
auto &parentChild = std::find(assignNode->childNodes.begin(), assignNode->childNodes.end(), rightNode);
Var* replacementNode = new Var("z");
*replacementNode->parentNode = *assignNode;
**toBeReplacedParentChild = *replacementNode;
However I'm unsure as to if this is good AST design. If not all the class members are pushed to the childNodes
vector then the replacement would not work.
Can anyone propose a better solution to modifying and keeping track of child nodes within a AST?