编写编译器:如何让简单模板工作

Writing a compiler: how to get simple templates to work?

本文关键字:简单 工作 编译器      更新时间:2023-10-16

我有一个语法非常像c++的语言。词法分析器和解析器已经就位并生成正确的AST。对于大部分后端也已经完成。

编译器用来创建类型的基本系统非常简单:所有类型都被认为是内置的,所有实例都是全局的。这里有一个简单的映射,它将一个类型名称匹配到一个创建变量的方法,它基本上是一个泛型类型,比如boost::any。另一个以变量名作为键,变量作为值的映射作为全局作用域:

std::map< std::string, std::function< Variable() > typeList;
  //register some types
typeList[ "X" ] = Variable::Create<X>;
typeList[ "Y" ] = CreateInstanceOfY;
....

当编译器获得像X myVar;这样的初始化的AST节点时,它基本上会

std::map< std::string, Variable > globalScope;
globalScope[ "myVar" ] = typeList[ "X" ]();

当稍后使用myVar时,它可以通过简单的类型调度来访问,如

X& x = myVar.GetReference<X>();

现在我想扩展一下,使用简单的模板。假设有一个类型"array",它是用vector实现的。我可以注册像

这样的东西
typeList[ "array<X>" ] = Variable::Create< std::vector< X > >;

,但这不是很好管理,因为它必须在所有组合中重复。理想情况下,我需要允许这样写的功能:

typeList.CreateTemplateVariable( "array", "X" )

将创建一个变量实例,该实例内部保存一个std::vector。我努力了,但不知道该怎么做。也许我只是用错误的方式开始了简单的类型映射,这就是我无法理解它的原因。

所以问题很简单:有可能这样做吗?以及如何?

我不确定我得到你的问题是正确的,但如果你有M参数类型(vector<>, list<>,…)和N个简单类型(int, double,…),你需要M*N实际实现,如果你想支持所有的组合。所有这些实现都必须在编译时知道(或者原则上可以动态调用c++编译器)。你真的想要这个吗?

一个解决方法是使用非类型容器。例如,vector<Object*>存储指针,可以随后转换为所需的类型,例如使用dynamic_cast。这样,您只需要M个参数类型的实现,并且可以独立地将"array"解析为vector,将"X"解析为X

一般来说,您创建类模板类型的方法是按照您的描述,但不是提前创建所有可能的组合,而是根据需要创建它们。因此,您可能有一个getType例程,如:

std::function< Variable() > getType(std::string name) {
    auto rv = typeList[name];
    if (rv) return rv;
    auto template_start = name.find('<');
    if (template_start != string::npos) {
        auto template_end = name.rfind('>');
        std::string arg = name.substr(template_start+1, template_end);
        std::string base = name.substr(0, template_start);
        typeList[name] = rv = InstantiateTemplate(base, arg);
        return rv; }
    throw UnknownTypeError(name);
}

对于程序中引用的任何类型都会调用该函数,根据需要创建所需的模板实例化。