Я пытаюсь использовать шаблон посетителя для выполнения операций для AST моего компилятора, но я не могу найти реализацию, которая будет работать правильно.
Выдержка из классов AST:
class AstNode
{
public:
AstNode() {}
};
class Program : public AstNode
{
public:
std::vector<std::shared_ptr<Class>> classes;
Program(const std::vector<std::shared_ptr<Class>>&);
void accept(AstNodeVisitor& visitor) const { visitor.visit(*this); }
};
class Expression : public AstNode
{
public:
Expression() {}
};
class Method : public Feature
{
public:
Symbol name;
Symbol return_type;
std::vector<std::shared_ptr<Formal>> params;
std::shared_ptr<Expression> body;
Method(const Symbol&, const Symbol&, const std::vector<std::shared_ptr<Formal>>&,
const std::shared_ptr<Expression>&);
feature_type get_type() const;
};
class Class : public AstNode
{
public:
Symbol name;
Symbol parent;
Symbol filename;
std::vector<std::shared_ptr<Feature>> features;
Class(const Symbol&, const Symbol&, const Symbol&,
const std::vector<std::shared_ptr<Feature>>&);
};
class Assign : public Expression
{
public:
Symbol name;
std::shared_ptr<Expression> rhs;
Assign(const Symbol&, const std::shared_ptr<Expression>&);
};
Посетитель (частичная реализация):
class AstNodeVisitor
{
public:
virtual void visit(const Program&) = 0;
virtual void visit(const Class&) = 0;
virtual void visit(const Attribute&) = 0;
virtual void visit(const Formal&) = 0;
virtual void visit(const Method&) = 0;
};
class AstNodePrintVisitor : public AstNodeVisitor
{
private:
size_t depth;
public:
void visit(const Program& node) {
for (auto cs : node.classes)
visit(*cs);
}
void visit(const Class&);
void visit(const Attribute&);
void visit(const Formal&);
void visit(const Method&);
};
Как я это использую:
AstNodePrintVisitor print;
ast_root->accept(print); // ast_root is a shared_ptr<Program>
Вопрос:
Узел метода содержит член тела типа Expression -, который является базовым классом. Как я буду его посещать?
Я подумал, может быть, я мог бы просто написать метод accept для каждого узла AST и вместо этого выполнять обход там. (т.е. вместо того, чтобы вызывать визит ()в посетителе, вызовите accept ()в посещаемом объекте, затем вызовите визит (*это ), чтобы вызовы были полиморфными и вызывался правильный метод посещения ()посетителя.
Однако, если я сделаю это, у меня не будет возможности пройти сверху -вниз (операцию, затем рекурсию )или снизу -вверх (рекурсию, затем операцию ), поскольку мне нужно выбрать только одну. Под этим я подразумеваю, что для PrintVisitor, например, потребуется обход AST сверху -вниз, а для TypeCheck потребуется подход снизу -вверх.
Есть ли способ обойти это? Или я переборщил -с инженерными вещами? Прямо сейчас я думаю, что самый быстрый способ — просто реализовать методы в самих узлах.