繁体   English   中英

纯函数编译器如何使用类型信息注释AST?

[英]How do purely functional compilers annotate the AST with type info?

在语法分析阶段,命令式编译器可以从已经包含在构造期间设置为nulltype字段的节点构建AST,然后在语义分析阶段,通过分配声明/推断来填充类型类型到type字段中。

纯粹的函数式语言如何处理这个问题,在那里你没有分配的奢侈品? 无类型AST是否映射到另一种类型丰富的AST? 这是否意味着我需要为每个AST节点定义两种类型,一种用于语法阶段,一种用于语义阶段?

是否有纯函数式编程技巧可以帮助编译器编写这个问题?

我通常将一个源(或已经降低了几步)AST重写为一个新形式,用一对(tag, expression)替换每个expression节点。

标签是唯一的数字或符号,然后由下一个通道使用,它从AST中导出类型方程。 例如, a + b将产生类似{ numeric(Tag_a).东西numeric(Tag_a). numeric(Tag_b). equals(Tag_a, Tag_b). equals(Tag_e, Tag_a). }。

然后解决类型方程(例如,通过简单地将它们作为Prolog程序运行),并且,如果成功,所有标签(这个程序中的变量)现在绑定到具体类型,如果不成功,它们将被保留为类型参数。

在下一步中,我们之前的AST再次被重写,这次用所有推断的类型信息替换标签。

整个过程是一系列纯重写,无需破坏性地替换AST中的任何内容。 典型的编译管道可能需要几十次重写,其中一些会改变AST数据类型。

有几种方法可以对此进行建模。 您可以使用与命令式案例中相同类型的可空数据字段:

data Exp = Var Name (Maybe Type) | ...
parse :: String -> Maybe Exp     -- types are Nothings here
typeCheck :: Exp -> Maybe Exp    -- turns Nothings into Justs

甚至,使用更精确的类型

data Exp ty = Var Name ty | ...
parse :: String -> Maybe (Exp ())
typeCheck :: Exp () -> Maybe (Exp Type)

我不能说为它是如何应该做的,但我没有做到这一点在F#中的C#编译器在这里

方法基本上是 - 从源代码构建一个AST,留下类型信息不受约束的东西 - 所以AST.fs基本上是AST,它为类型名称,函数名称等字符串。

当AST开始被编译为(在这种情况下).NET IL时,我们最终会得到更多的类型信息(我们在源代码中创建类型 - 让我们调用这些类型的存根)。 然后,这为我们提供了创建方法存根所需的信息(代码可能包含包含类型存根和内置类型的签名)。 从这里开始,我们现在有足够的类型信息来解析代码中的任何类型名称或方法签名。

我将其存储在TypedAST.fs文件中。 我一次性完成这项工作,但这种做法可能很天真。

现在我们有了一个完全类型化的AST,你可以做一些事情,比如编译它,完全分析它,或者你喜欢用它做什么。

因此,在回答问题时“这是否意味着我需要为每个AST节点定义两种类型,一种用于语法阶段,一种用于语义阶段? ”,我不能肯定地说这是事实,但它肯定是什么我做了,它似乎是MS对Roslyn所做的事情(虽然它们基本上用类型信息IIRC装饰了原始树)

是否有纯函数式编程技巧可以帮助编译器编写这个问题? ”鉴于AST在我的情况下基本上是镜像的,它可以使它成为泛型并转换树,但代码可能最终(更)可怕。

type 'type AST;
| MethodInvoke of 'type * Name * 'type list
| ....

与处理关系数据库时的情况一样,在函数式编程中,通常不将所有内容都放在单个数据结构中是个好主意。

特别是,可能没有“AST”的数据结构。

最有可能的是,将有表示已解析表达式的数据结构。 处理类型信息的一种可能方式是在解析期间已经为树的每个节点分配唯一标识符(如整数),并且具有将这些节点ID与类型相关联的一些合适的数据结构(如哈希映射)。 那么,类型推理传递的工作就是创建这个地图。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM