章节目录

路线图

本节阅读量:

这套课程会用 C++ 实现一门很小的 Lisp 风格语言。我们不会先造出一个庞大的框架,再逐个解释里面的模块;而是从一个只能计算整数加法的版本开始,每一章增加一种能力,同时保留一份可以构建和运行的完整代码。

从一个最小程序出发

第一章从这样的程序开始:

1
(+ 40 2)

它会沿着两条路径得到同一个结果:

1
2
3
源代码 -> token -> 语法树 AST
                    |-> 解释器 -> 42
                    |-> 编译器 -> IR -> x86-64 汇编

解释器读取 AST,直接算出 42。编译器读取同一棵 AST,先把计算过程拆成 IR,再生成汇编。这样,每加入一种语言特性,我们都可以从两个方向检查它:

1
2
它应该是什么意思?        由解释器回答
它怎样变成机器执行步骤?  由编译器回答

后面会加入什么

后面的章节不会推翻这条主线,只会沿着它逐步加入新的问题:

章节 语言或实现能力 要回答的核心问题
1 整数与加法 文本怎样变成结构,结构怎样被求值和编译?
2 变量与 let 变量的值在哪里,重名变量怎样区分?
3 if 与 eq? 只执行一个分支,到了汇编里怎样变成跳转?
4 lambda 与函数调用 函数怎样成为值,又怎样遵守调用约定?
5 letrec 与递归 函数怎样在自己的函数体里找到自己?
6 record 与堆对象 高级语言的值怎样变成内存中的整数、指针和对象?
7 闭包 函数怎样记住定义位置周围的变量?
8 set! 与可变状态 名字、存储位置和值为什么是三件事?
9 垃圾回收 哪些堆对象仍然有用,移动以后怎样更新引用?
10 寄存器分配 无限的临时变量怎样落到有限的寄存器和栈槽?
11(选修) 无栈协程 暂停怎样变成一条由闭包保存、以后可以继续的计算?

前十章是完整主线,第十一章是综合选修篇。每章只集中处理一个核心难点,但解释器和编译器会一起前进。前面已经支持的整数、负整数和加法,也会在后续代码快照里继续可用。

为什么先有第 0 章

正式实现语言之前,第 0 章先把后面反复出现的工具讲清楚:

1
2
3
01_cmd       怎样构建、运行、生成并链接汇编
02_cli       命令行参数怎样进入 mini 并选择处理路径
03_makefile  make 怎样根据目标和依赖决定是否重新构建

这里不展开复杂的构建系统,也不提前讲 parser、IR 或汇编生成的内部实现。目标只是让你看到后面的命令时,知道自己正在运行哪个程序、输入和输出分别是什么。

课程边界

  • 课程代码以 C++17 为基线,不依赖第三方库。
  • 编译器生成 x86-64 System V ABI 的 AT&T 汇编。
  • mini compile 只生成 .s 文件,不调用系统工具链,也不负责链接可执行程序;第九章起,读者还会显式把 .s 与 make 生成的 gc_runtime.o 链接起来。
  • 默认运行环境是 Linux、macOS 或 WSL,命令示例使用 Unix 风格 shell。
  • 第一版聚焦解释器语义、代码生成、运行时内存与寄存器分配;选修篇只实现受限的 generator 风格无栈协程,不扩展到类型系统、宏、模块、对象系统、并发、通用协程或高级优化。

下一节先不写语言实现。我们从最实际的一步开始:进入代码目录,把第一章的 mini 运行起来。


专栏首页

上一节

0.1 运行代码

下一节