AST的访问者模式
我试图使用访问者模式来执行我的编译器的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>
问题:
方法节点包含一个类型为表达式的主体成员 - 这是一个基类。 我将如何访问它?
我想也许我可以简单地为每个AST节点编写一个接受方法,然后进行遍历。 (即不是在访问者中调用visit(),而是在可见的然后调用visit(* this)中调用accept(),这样调用将是多态的,访问者的正确visit()方法被调用。
但是,如果我这样做,我将无法选择自上而下(操作然后递归)或自下而上(递归操作),因为我只能选择一个。 通过这个,我的意思是一个PrintVisitor例如需要AST的自顶向下遍历,但TypeCheck需要自下而上的方法。
有没有解决的办法? 还是我过度设计的东西? 现在我认为最快的方法就是在节点本身实现这些方法。
我们首先对游客的手艺进行小幅改动:
void visit(const Program& node) {
for (auto cs : node.classes)
visit(*cs);
}
在一般情况下,呼叫visit(*cs)
应该是cs->accept(*this)
以允许虚拟分派。
现在主要问题是:遍历顺序的控制。
访问者只能真正以深入的方式真正访问树,首先可以实现广度,但在单一visit
方法中很古怪(您基本上需要将访问与孩子的迭代分开)。
另一方面,即使是在深度优先遍历中,您可以选择是否在拜访孩子之前或之后对父母进行操作。
这样做的典型方法是在纯基本类和真正的actor之间提供一个中间层,例如:
class RecursiveAstNodeVisitor: public AstNodeVisitor
{
public:
// returns whether or not to stop recursion
virtual bool actBefore(Program const&) { return false; }
virtual void actAfter(Program const&) {}
virtual bool actBefore(Class const&) { return false; }
virtual void actAfter(Class const&) {}
// ... You get the idea
virtual void visit(Program const& p) {
if (actBefore(p)) { return; }
for (auto c: p.classes) {
c->accept(*this);
}
actAfter(p);
}
// ... You get the idea
};
覆盖者可以在递归发生之前或之后自由行动......并且当然可以对两者都起作用!
class PrintAstNodeVisitor: public RecursiveAstNodeVisitor {
public:
PrintAstNodeVisitor(std::ostream& out): _out(out), _prefix() {}
virtual bool actBefore(Program const& p) {
_out << "{n";
_out << " "type": "Program",n";
_out << " "name": " << p.name << "",n";
_out << " "classes": [n";
_prefix = " ";
return false;
}
virtual void actAfter(Program const& p) {
_out << " ]n";
_out << "}n";
}
virtual bool actBefore(Class const& c) {
_out << _prefix << "{n";
_out << _prefix << " "type": "Class",n";
// ...
}
private:
std::ostream& _out;
std::string _prefix;
};
链接地址: http://www.djcxy.com/p/61553.html