我正在创建一个编译器(用于Cool语言)作为个人项目,并且我在设计符号表时遇到问题。对于上下文,我使用类的层次结构作为我的AST。这是AST的一小部分:
class NodeAST {
public:
virtual void accept(Visitor&) = 0;
};
class ProgramAST : public NodeAST {
private:
const std::vector<ClassPtr> vClasses;
public:
ProgramAST(std::vector<ClassPtr> vClasses);
auto class_cbegin() const {
return std::cbegin(vClasses);
}
auto class_cend() const {
return std::cend(vClasses);
}
virtual void accept(Visitor& v) override;
};
class ClassAST : public NodeAST {
private:
const std::string name;
const std::vector<FeaturePtr> vFeatures;
public:
ClassAST(std::string name, std::vector<FeaturePtr> vFeatures);
auto getName() const {
return name;
}
auto feature_cbegin() const {
return std::cbegin(vFeatures);
}
auto feature_cend() const {
return std::cend(vFeatures);
}
virtual void accept(Visitor& v) override;
};
目前,我的符号表的核心是一个定义如下的地图:
std::unordered_map<std::string, NodeAST*> table
它将声明的名称映射到AST中的相应节点。这样,例如,我可以使用我在AST节点中设置的类型填充松散标识符的类型。
但问题是,当我查询符号表中的节点时,我会返回NodeAST*
。因此,我必须将其向下转换为ClassAST*
,MethodAST*
或VarDecAST*
等以实际使用它。
如何以避免向下转换的方式设计符号表?
答案 0 :(得分:1)
我不知道您正在实施的编程语言,但我认为您很可能完全避免动态转换。例如,在f(1)
中,f
可以是函数,也可以是变量,如果您的语言有lambda。你需要通过在符号表中查找来找到它。
如果你可以绝对排除这种可能性,理论上你可以为每种类型创建单独的符号表。但请记住,如果您需要检测名称冲突,显然会更难找到名称冲突,这可能意味着您的编程语言以后难以扩展。我不推荐这个解决方案。
就个人而言,我只会向您的to_class()
类添加to_var()
,is_class()
,is_var()
,NodeAST
等方法,以便动态广告封装而不是遍布整个代码库。您还可以为符号表创建一个类,以便可以使用get_class()
,get_var()
等访问元素。
如果您担心C ++中RTTI的运行时成本,您可以查看其他编译器正在使用的解决方案。对于LLVM,这里描述:http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html
答案 1 :(得分:1)
我过去非常成功地使用了访问者模式来访问具有公共基础的指针容器。在一个实现中,我在dynamic_cast&lt;&gt;上经历了~40%的加速。实现,这在我写的数据库抽象层中很重要。
这里的答案中有一点解释...... Right design pattern to deal with polymorphic collections of objects
Visitor Pattern上的维基百科页面也提供了一个很好的简短C ++示例,其中包含Dispatcher类访问的基本指针集合。
您已经显示了&#34;可访问的&#34;的源代码。你问题中的课程。我们需要看到您的&#34;访客&#34;的实施情况。看看为什么你需要一个dynamic_cast&lt;&gt ;.它不应该是必要的。应该通过函数参数重载来选择正确的visit()函数。
干杯