首页 > 解决方案 > 如何设计符号表来支持函数重载?

问题描述

我正在创建一个编译器,并且真的在语义分析阶段苦苦挣扎。我不确定如何处理符号表中的函数重载。我似乎找不到任何描述这个特定问题的资源。我认为必须在某处使用名称修饰,并且我很确定应该将 AST 中的类型转换为字符串。

只要每个声明具有不同的参数集,就允许在同一范围内声明多个具有相同名称的函数。以下片段是我的语言中的一个示例(它与 Swift 非常相似)。

func add(a: Int, b: Int) {
  return a + b;
}
func add(a: Float, b: Float) {
  return a + b;
}

我不知道如何在符号表中存储函数。这是我的符号表数据结构的一部分。

struct Symbol {};

struct Var final : Symbol {
  std::string type;
};

using FuncParams = std::vector<std::string>;
struct Func final : Symbol {
  std::string ret;
  FuncParams params;
};

using Table = std::unordered_map<std::string, std::unique_ptr<Symbol>>;
struct Scope {
  Table table;
  Scope *parent = nullptr;
};
using Scopes = std::vector<std::unique_ptr<Scope>>;

我可以使用 astd::unordered_multimap并将函数名称存储add为键,并将参数名称存储在符号对象中。我可以使用 astd::unordered_map并将用参数修饰的函数名存储add_Int_Int为键,并将参数的名称也存储在符号对象中。

另外,应该Symbol是基类还是应该将各种符号放在一个Symbol对象中?我见过许多使用 anenum来区分函数、变量和类型声明的示例,但函数存储返回类型和参数类型。我应该使用标记工会吗?

我觉得有一种聪明而简单的方法可以解决这个问题,但我就是找不到。

更新:

我接受了@NeilButterworth 的建议,并使用未损坏的函数名称作为符号键,这似乎是要走的路(但我知道什么!)。对我的其他问题的回答或对此主题的一些建议将不胜感激。

标签: c++data-structurescompiler-construction

解决方案


解决方案是按照@NeilButtworth 在评论中所说的去做。但是您将希望围绕创建“兼容”类型树构建一些逻辑。因为您希望函数具有兼容的参数。我会推荐一些函数,如果一个类型是另一个类型的子类型,比如isTypeCompatible(actualType,formalType). 然后从那里您可以尝试找到具有与调用匹配的最具体签名的函数。所以f(5)应该调用f(int),因为 5 是一个显式的 int 但只是一个隐式的浮点数。这样做的一种方法是查看哪个签名最小化了类型树与给定类型和正式类型的距离。

编辑:通过“兼容”类型树,它与继承基本相同。就像我们有 g(float) 一样,g(5)因为 int 文字可以转换为 float 文字。使用自定义用户类构建类型树要容易得多,因为您的 AST 应该已经在解析和类型检查时构建。


推荐阅读