一个符号 table 怎么设计才能支持函数重载呢?
How can a symbol table be designed to support function overloading?
我正在创建一个编译器,但在语义分析阶段确实很吃力。我不确定如何处理符号 table 中的函数重载。我似乎找不到任何描述此特定问题的资源。我认为必须在某处使用名称修改,我很确定 AST 中的类型应该转换为字符串。
允许在同一范围内声明多个同名函数,只要每个声明具有不同的参数集即可。以下片段是我的语言示例(它与 Swift 非常相似)。
func add(a: Int, b: Int) {
return a + b;
}
func add(a: Float, b: Float) {
return a + b;
}
我不知道如何在符号中存储函数table。这是我的符号 table 数据结构的一部分。
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>>;
我可以使用 std::unordered_multimap
并将函数名称 add
存储为键,并将参数名称存储在符号对象中。我可以使用 std::unordered_map
并将用参数 add_Int_Int
修饰的函数名称存储为键,并将参数的名称也存储在符号对象中。
此外,Symbol
应该是一个基础 class 还是我应该将各种符号放在一个 Symbol
对象中?我见过许多使用 enum
来区分函数、变量和类型声明的示例,但函数存储 return 类型和参数类型。我应该使用标记联合吗?
我觉得有一种聪明而简单的方法可以解决这个问题,但我就是找不到。
更新:
我采纳了@NeilButterworth 的建议并使用未损坏的函数名称作为符号键,这似乎是可行的方法(但我知道什么!)。对我的其他问题的回答或有关此主题的一些建议将不胜感激。
解决办法就是按照@NeilButtworth 在评论中所说的去做。但是您将希望围绕创建 "compatible" 类型的树构建一些逻辑。因为您希望函数具有兼容的参数。如果一个类型是另一个类型的子类型,比如 isTypeCompatible(actualType,formalType)
,我会推荐一些 returns 的函数。然后从那里您可以尝试找到具有最 specific 与调用匹配的签名的函数。所以 f(5)
应该调用 f(int)
因为 5 是一个显式 int 但只是一个隐式 float。这样做的一种方法是查看哪个签名最小化类型树与给定类型和形式类型之间的距离。
编辑:通过"compatible"类型树,它与继承本质上相同。就像如果我们有 g(float) g(5)
是如何工作的,因为 int 文字可以转换为 float 文字。使用自定义用户 类 构建类型树要容易得多,因为你的 AST 应该已经在解析和类型检查时构建了。
我正在创建一个编译器,但在语义分析阶段确实很吃力。我不确定如何处理符号 table 中的函数重载。我似乎找不到任何描述此特定问题的资源。我认为必须在某处使用名称修改,我很确定 AST 中的类型应该转换为字符串。
允许在同一范围内声明多个同名函数,只要每个声明具有不同的参数集即可。以下片段是我的语言示例(它与 Swift 非常相似)。
func add(a: Int, b: Int) {
return a + b;
}
func add(a: Float, b: Float) {
return a + b;
}
我不知道如何在符号中存储函数table。这是我的符号 table 数据结构的一部分。
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>>;
我可以使用 std::unordered_multimap
并将函数名称 add
存储为键,并将参数名称存储在符号对象中。我可以使用 std::unordered_map
并将用参数 add_Int_Int
修饰的函数名称存储为键,并将参数的名称也存储在符号对象中。
此外,Symbol
应该是一个基础 class 还是我应该将各种符号放在一个 Symbol
对象中?我见过许多使用 enum
来区分函数、变量和类型声明的示例,但函数存储 return 类型和参数类型。我应该使用标记联合吗?
我觉得有一种聪明而简单的方法可以解决这个问题,但我就是找不到。
更新:
我采纳了@NeilButterworth 的建议并使用未损坏的函数名称作为符号键,这似乎是可行的方法(但我知道什么!)。对我的其他问题的回答或有关此主题的一些建议将不胜感激。
解决办法就是按照@NeilButtworth 在评论中所说的去做。但是您将希望围绕创建 "compatible" 类型的树构建一些逻辑。因为您希望函数具有兼容的参数。如果一个类型是另一个类型的子类型,比如 isTypeCompatible(actualType,formalType)
,我会推荐一些 returns 的函数。然后从那里您可以尝试找到具有最 specific 与调用匹配的签名的函数。所以 f(5)
应该调用 f(int)
因为 5 是一个显式 int 但只是一个隐式 float。这样做的一种方法是查看哪个签名最小化类型树与给定类型和形式类型之间的距离。
编辑:通过"compatible"类型树,它与继承本质上相同。就像如果我们有 g(float) g(5)
是如何工作的,因为 int 文字可以转换为 float 文字。使用自定义用户 类 构建类型树要容易得多,因为你的 AST 应该已经在解析和类型检查时构建了。