Я нахожусь в процессе создания своего собственного языка программирования, который я делаю для целей обучения. Я уже написал лексер и парсер рекурсивного спуска для подмножества моего языка (в настоящее время я поддерживаю математические выражения, такие как + - * /
и круглые скобки). Парсер возвращает мне абстрактное синтаксическое дерево, в котором я вызываю Evaluate
метод для получения результата выражения. Все отлично работает Вот примерно моя текущая ситуация (примеры кода на C #, хотя это в значительной степени не зависит от языка):
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;
}
}
Однако я хотел бы отделить алгоритм от узлов дерева, потому что я хочу применить принцип Open / Closed, чтобы мне не пришлось заново открывать каждый класс узлов, когда я хочу реализовать генерацию кода, например. Я читал, что Шаблон посетителя хорош для этого. Я хорошо понимаю, как работает шаблон, и что использование двойной диспетчеризации - это путь. Но из-за рекурсивной природы дерева я не уверен, как мне к нему подойти. Вот как будет выглядеть мой посетитель:
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
}
Так что это моя проблема. Я хочу немедленно заняться этим, пока мой язык не поддерживает большую функциональность, чтобы избежать более серьезной проблемы в дальнейшем.
Я не опубликовал это в StackOverflow, потому что я не хочу, чтобы вы предоставили реализацию. Я только хочу, чтобы вы поделились идеями и концепциями, которые я, возможно, пропустил, и как я должен подходить к этому.