Estoy tratando de usar el patrón de visitante para realizar operaciones para el AST de mi compilador pero parece que no puedo encontrar una implementación que funcione correctamente.Patrón de visitante para AST
clases AST extracto:
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>&);
};
de Visitantes (aplicación parcial):
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&);
};
¿Cómo lo estoy usando:
AstNodePrintVisitor print;
ast_root->accept(print); // ast_root is a shared_ptr<Program>
El problema:
El El nodo de método contiene un bo dy miembro de tipo Expresión - que es una clase base. ¿Cómo lo voy a visitar?
Pensé que tal vez podría simplemente escribir un método de aceptación para cada nodo AST y hacer el recorrido allí en su lugar. (es decir, en lugar de llamar a visit() en el visitante, llame a accept() en la visita visitable (* this) para que las llamadas sean polimórficas y se llame al método de visita correcto() del visitante.
Sin embargo, si hago esto, no tendré opción para cruzar de arriba hacia abajo (operación luego recurrente) o de abajo hacia arriba (recurse luego operación) ya que tengo que elegir solo uno. Con esto quiero decir que PrintVisitor por ejemplo necesitará un cruce descendente del AST, pero un TypeCheck necesitará un enfoque de abajo hacia arriba.
¿Hay alguna forma de evitar esto? ¿O estoy sobreingeniería? En este momento, creo que la manera más rápida es simplemente implementar los métodos. en los nodos.
O simplemente usa Bison. –
@ H2CO3 Sí, usé Bison para analizar y así es como se crea el AST. Actualmente estoy realizando un análisis semántico (verificación de tipo, alcance, ...) y también tendré que pensar en el código gen. –
oh OK :) Y, por cierto, ¿no puedes usar el enfoque descendente para la verificación de tipos? –