Skip to content

compiling x-lisp

· xieyuheng

编译 lambda ​

编译 lambda 的方式和处理 currying 的方式是类似的, 都可以使用 curried-value。

currying 是 apply 的时候参数不够就返回 curried-value。

lambda 是把所有的函数内的 lambda 都提到函数外, 成为全局函数,这个过程是递归的。 然后把原来 lambda 的位置翻译成一个 curried-value。

runtime 中有用 c 实现的 overloaded apply 函数, 可以处理 curried-value 和 function。

在 basic-lisp 生成代码时, 如果发现 apply 的 target 是 constant, 可以根据具体的 constant 类型以及 arity, 来生成更具体的代码,避免运行时调用一般的 apply。

编译 primitive ​

比如,如果发现 target 是 primitive, 就可以生成对 runtime primitive function 的调用, 如果发现是对应于 instruction 的 primitive, 就可以直接生成 instruction。

也就是说,所编译的就是 lambda calculus, 通过 apply 不同的 target 来扩展 lambda calculus。

保持简单 ​

也就是说,这里的原则是: 任何 codegen 上不确定的东西, 都可以用 runtime 中的 c 函数来实现。

甚至 pattern match 也可以。 当然这是效率非常低的。

其实作为第一个编译器, 我需要的只是编译到 C 的 calling convention。 只要让语言可以用 C 扩展就可以。

行动 ​

先设计核心的 passes, 把 lambda calculus 编译到 basic-lisp, 外加 define 和 conditional 功能。

把整个编译流程跑起来。

然后再将所编译的对象扩展到整个 x-lisp。

A flower in nature is not built step by step, it grows from a center and unfolds itself as a whole to it's full form.

lambda calculus is the center. lambda calculus is the core.

反思 EOC 课程设计 ​

我觉得这也是为什么 EOC 的课程感觉不太对。 EOC 虽然是渐进地从一个具有完整流程的编译器开始的, 但是所编译的对象语言不是从 core 展开的。

到课程的中后期,才讲到 lambda conversion, 在讲解的时候,老师也说,这是这个课程的核心。

但是很遗憾,课程没有从核心开始。

开始的时候,没有 GC,所以按照 EOC 的设计, 很难从 lambda calculus 开始编译。 但是如果区分 frontend 和 backend, 就可以在 frontend 从 lambda calculus 开始,中间语言可以先解释运行。 而到 backend 的时候,核心问题就是 GC 了。