I'm writing a compiler, and I'm trying to come up with a good design for an abstract syntax tree, and it's conversion into code.
The AST is built in depth first order, such that every node added will only ever refer to previously added nodes, and in most cases referenced nodes are the most recently added nodes. It is naturally a tree, so each node except the root node is referenced by one parent. For data, an AST node has a type, which can optionally be represented as an enum, a string value, and references to a usually small number of other nodes.
The above naturally leads to an implementation of a std::deque of nodes, acting like a stack. This takes advantage of the fact that elements in a deque tend to be adjacent in memory. It also allows the tree to grow without reallocation as it is built.
To convert the ast to code, I need to traverse the ast in essentially the same depth first traversal, except it may be convenient to start at the root node instead of the first node added. This would make make code generation a recursive algorithm, implementing depth first traversal. How to convert each node depends mostly on the logical type of the node. There may be a hierarchy to this conversion, that is it may make sense to say that converting a constructor is similar to converting a function. A hierarchy may organize code better.
So it strikes me as prudent to use virtual dispatch to implement the conversion. That is, each node type has an associated virtual convert/code_gen/compile function somewhere.
These two designs are at odds with each other, and I'd like your input on how to graft them together. There may also be other considerations that I've overlooked.
How to have a near continuous in memory and simply structured data, but use virtual dispatch to do operate on that data?
Thanks for your replies.