路线图
本节阅读量:这套课程会用 C++ 实现一门很小的 Lisp 风格语言。我们不会先造出一个庞大的框架,再逐个解释里面的模块;而是从一个只能计算整数加法的版本开始,每一章增加一种能力,同时保留一份可以构建和运行的完整代码。
从一个最小程序出发
第一章从这样的程序开始:
|
|
它会沿着两条路径得到同一个结果:
|
|
解释器读取 AST,直接算出 42。编译器读取同一棵 AST,先把计算过程拆成 IR,再生成汇编。这样,每加入一种语言特性,我们都可以从两个方向检查它:
|
|
后面会加入什么
后面的章节不会推翻这条主线,只会沿着它逐步加入新的问题:
| 章节 | 语言或实现能力 | 要回答的核心问题 |
|---|---|---|
| 1 | 整数与加法 | 文本怎样变成结构,结构怎样被求值和编译? |
| 2 | 变量与 let |
变量的值在哪里,重名变量怎样区分? |
| 3 | if 与 eq? |
只执行一个分支,到了汇编里怎样变成跳转? |
| 4 | lambda 与函数调用 |
函数怎样成为值,又怎样遵守调用约定? |
| 5 | letrec 与递归 |
函数怎样在自己的函数体里找到自己? |
| 6 | record 与堆对象 |
高级语言的值怎样变成内存中的整数、指针和对象? |
| 7 | 闭包 | 函数怎样记住定义位置周围的变量? |
| 8 | set! 与可变状态 |
名字、存储位置和值为什么是三件事? |
| 9 | 垃圾回收 | 哪些堆对象仍然有用,移动以后怎样更新引用? |
| 10 | 寄存器分配 | 无限的临时变量怎样落到有限的寄存器和栈槽? |
| 11(选修) | 无栈协程 | 暂停怎样变成一条由闭包保存、以后可以继续的计算? |
前十章是完整主线,第十一章是综合选修篇。每章只集中处理一个核心难点,但解释器和编译器会一起前进。前面已经支持的整数、负整数和加法,也会在后续代码快照里继续可用。
为什么先有第 0 章
正式实现语言之前,第 0 章先把后面反复出现的工具讲清楚:
|
|
这里不展开复杂的构建系统,也不提前讲 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 运行起来。
本节目录