从零写一门小语言


如果你刚学完 C++ 基础,已经知道怎样写函数、定义类和使用指针,也许仍然有一个疑问:写下的代码究竟是怎样运行起来的?这套课程就是为这个问题准备的。

我们不依赖第三方库,用 C++ 实现一门很小的 Lisp 风格语言。它既有解释器,也有编译器:解释器直接计算程序的结果,编译器则把同一个程序一步一步转换成 x86-64 汇编。

第一章的语言只支持整数和加法。面对下面这个程序:

1
(+ 40 2)

我们会完整走通两条路径:

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

你以后也许并不会亲手开发编译器,这完全没有关系。编译器是这套课程的训练载体,而不是学习的终点。它的输入清楚、结果可见,中间每一步都可以观察,恰好能把许多重要的编程能力集中在一个完整项目里。

真正值得带走的,是下面这些可以迁移到其他项目中的能力:

  • 把问题建模成数据。 源代码要变成 token 和 AST,配置文件、网络协议、查询语句和业务规则同样要先变成程序能够处理的结构。你会练习怎样选择数据结构,并把规则和约束明确地写进程序。
  • 把复杂过程拆成阶段。 词法分析、语法解析、解释器、IR 和汇编生成各自只解决一部分问题。这种分层方法同样适用于数据处理流水线、后端业务流程和开发工具:每一层都有清楚的输入、输出与责任。
  • 理解状态、身份和所有权。 变量绑定、闭包捕获、堆对象和可变状态会让我们分清名字、位置、值与生命周期。这些问题也存在于回调、缓存、界面状态和游戏对象中,是写好 C++ 绕不开的基本功。
  • 建立逐层验证的调试习惯。 当结果不对时,不再只盯着最终输出猜原因,而是依次检查 token、AST、IR 和汇编,找到最早出现偏差的地方。这种利用中间结果缩小范围的方法,也适用于其他复杂程序。
  • 看懂抽象下面发生了什么。 理解栈、寄存器、调用约定、堆和垃圾回收之后,链接错误、生命周期问题、运行时错误和性能问题就不再完全是黑盒。即使日常使用高级框架,你也更容易判断问题发生在哪一层。

课程采用增量式的组织方式。每一章集中解决一个核心难点,同时保留一份可以构建、运行和修改的完整代码。你会亲手实现词法分析、语法解析、AST、解释器、IR 和汇编生成,再随着语言加入变量、条件、函数、递归、记录、闭包和可变状态,继续进入堆对象、垃圾回收与寄存器分配。完成十章主线以后,还可以通过第十一章的无栈协程选修篇,把闭包、状态、控制流和 GC 串成一个 generator。你不必等到课程结束才看到所有模块拼起来;从第一章开始,每一步都可以亲手验证。

课程结束时,你得到的不只是一些零散示例,而是一门由自己逐步实现的语言,以及一套可以带到其他项目中的建模、拆解和调试方法。

你不需要学过编译原理,也不需要提前掌握汇编。学过 C++ 的基础语法,就可以开始。课程代码以 C++17 为基线,默认运行环境为 Linux、macOS 或 WSL;具体工具和运行方式会在第 0 章说明。

代码仓库:build-mini-lang

现在,让我们从一个最小的程序开始,看看它究竟是怎样运行起来的。

章节目录