我正在创建一个针对个人语言的编译器(针对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*
等,才能真正使用它。如何设计符号表,从而避免向下转换?
最佳答案
我不知道您要实现的编程语言,但我认为您真的有可能完全避免动态转换。例如,在f(1)
中,如果您的语言具有lambda,则f
可以是函数或变量。您需要通过在符号表中查找来找出答案。
如果绝对可以排除这种可能性,则理论上可以为每种类型创建单独的符号表。但是请记住,如果您需要检测名称冲突,显然会更难发现名称冲突,这可能意味着您的编程语言以后很难扩展。我不推荐这种解决方案。
我个人只是将to_class()
,to_var()
,is_class()
,is_var()
等方法添加到NodeAST
类中,以便动态转换被封装并且不会在整个代码库中散布。您还可以为符号表创建一个类,以便可以使用get_class()
,get_var()
等访问元素。
如果您担心C++中RTTI的运行时成本,可以查看其他编译器正在使用的解决方案。对于LLVM,在此处进行描述:http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html