合抱之木,生于毫末

本节阅读量:

这一章只做一件事:把下面这段小语言程序从一段文本,变成两个结果。

1
(+ 40 2)

解释器会直接打印:

1
42

编译器会生成一段能返回 42 的 x86-64 汇编。

这门小语言使用 Lisp 风格语法,(+ 40 2) 表示 40 + 2。第一章还允许单个整数,例如 42-7;完整规则下一节会展开。

通过这个加法例子,我们会走过一门语言实现里最小、最完整的两条路:

1
2
源码 -> 语法树 -> 直接算结果
源码 -> 语法树 -> 生成汇编

第一章的目标不是“记住一堆术语”,而是先建立一个感觉:

1
2
程序不是一上来就能执行。
我们要先把文本读懂,再决定怎么执行它。

你需要先知道什么

你只需要会一点 C++ 基础:

  • 能看懂 struct
  • 能看懂函数调用。
  • 大概知道指针或对象可以互相引用。
  • 知道递归函数会调用自己。

其余所有的内容,会在用到时进行讲解。本章代码量不大,核心代码仍然保持在几百行规模。

代码架构

第一章代码很小,但已经按一门语言实现的基本结构分好了层。先看目录,不要一上来就钻进某个函数里:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
code/01_numbers/src/
  cli/main.cpp              命令行入口,负责读文件、选择 run/ast/ir/compile

  front/ast.h               AST 节点定义:IntExpr、AddExpr
  front/ast.cpp             AST 节点的构造和 dump
  front/lexer.h/.cpp        source -> tokens
  front/parser.h/.cpp       tokens -> AST

  interp/interpreter.h/.cpp AST -> integer

  compile/ir.h/.cpp         AST -> IR
  compile/assembly.h/.cpp   IR -> x86-64 assembly

这些英文名可以先这样读:

1
2
3
4
front     把源码读成 AST
interp    沿着 AST 直接算结果
compile   把 AST 变成 IR,再变成汇编
cli       把命令行和这些模块串起来

其中三个名字最重要:

1
2
3
token   源码小块,例如 "("、"+"、"40"
AST     abstract syntax tree,语法树,程序结构
IR      intermediate representation,编译器内部的草稿步骤

这一章最关键的分界线是 AST:

1
2
3
source text -> tokens -> AST --interp--> integer
                         |
                         +--compile--> IR --> assembly

也就是说,lexer 先把文本切成 token,parser 再把 token 组装成 AST。解释器和编译器处理的是“结构问题”:一旦 parser 返回了 AST,后面的代码就不需要再关心源码里有几个空格、括号写在哪里、数字有几位。

本章会用四个命令观察同一个程序的不同阶段:

1
2
3
4
run      看解释器结果
ast      看语法树
ir       看编译器草稿步骤
compile  生成汇编

第一次读代码,可以按这个顺序:

1
2
3
4
5
6
7
1. front/ast.h              先看程序结构长什么样
2. front/lexer.cpp          再看源码怎么切成 token
3. front/parser.cpp         再看 token 怎么变成 AST
4. interp/interpreter.cpp   看 AST 怎么直接算出结果
5. compile/ir.h/.cpp        看 AST 怎么拆成 IR
6. compile/assembly.cpp     看 IR 怎么变成汇编
7. cli/main.cpp             最后看命令行怎么把这些步骤串起来

这样读会比直接从 main 一路跳函数更稳:先理解每一层负责什么,再看它们怎么连起来。

读完这一章,你不需要懂完整编译原理。你只需要看清楚:文本如何变成 AST,AST 如何被解释器求值,AST 又如何一路变成汇编。


0.3 make 简介

上一节

1.1 从两个表达式开始:整数和加法

下一节