I'm in the process of creating my own programming language, which I do for learning purposes. I already wrote the lexer and a recursive descent parser for a subset of my language (I currently support mathematical expressions, such as + - * /
and parenthesis). The parser hands me back an Abstract Syntax Tree, on which I call the Evaluate
method to get the result of the expression. Everything works fine. Here is approximately my current situation (Code examples in C#, although this is pretty much language-agnostic):
public abstract class Node
{
public abstract Double Evaluate();
}
public class OperationNode : Node
{
public Node Left { get; set; }
private String Operator { get; set; }
private Node Right { get; set; }
public Double Evaluate()
{
if (Operator == "+")
return Left.Evaluate() + Right.Evaluate();
//Same logic for the other operators
}
}
public class NumberNode : Node
{
public Double Value { get; set; }
public Double Evaluate()
{
return Value;
}
}
However, I would like to decouple the algorithm from the tree nodes because I want to apply Open/Closed Principle so I don't have to reopen every node class when I want to implement code generation for example. I read that the Visitor Pattern is good for that. I have a good understanding of how the pattern works and that using double dispatch is the way to go. But due to the recursive nature of the tree, I'm not sure how I should approach it. Here is what my Visitor would look like:
public class AstEvaluationVisitor
{
public void VisitOperation(OperationNode node)
{
// Here is where I operate on the operation node.
// How do I implement this method?
// OperationNode has two child nodes, which may have other children
// How do I work the Visitor Pattern around a recursive structure?
// Should I access children nodes here and call their Accept method so they get visited?
// Or should their Accept method be called from their parent's Accept?
}
// Other Visit implementation by Node type
}
So this is my problem. I want to tackle it immediately while my language does not support a lot of functionnality to avoid having a bigger problem later.
I did not post this to StackOverflow because I don't want you to provide an implementation. I only want you to share ideas and concepts that I might have missed, and how I should approach this.