我正在创建一个针对个人语言的编译器(针对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

09-05 05:56