为了学习编译器的工作原理,我最近在编写一个 C 编译器。
C 编译器的作用是把源代码(字符串)翻译成汇编语言。
这个编译器首先把字符串拆分成 token,然后借助 token 把字符串解析成节点,最后生成汇编代码。
- 词法分析器(Tokenizer)
- 语法分析器(Parser)
- 代码生成器(Code Generator)
词法分析器
词法分析器遍历字符串,生成一个由 token 组成的链表。
语法分析器
语法分析器把字符串解析成节点。
要实现语法分析器,我们需要先设计编译器。通常我们使用 BNF(巴科斯-瑙尔范式)来进行编译器设计。
BNF 记法长这样:
expr = num ("+" num | "-" num)*
这是一个描述加法和减法的简单例子。
这个表达式先接收一个数字,如果后面还有数字,就对其做加法或减法(* 表示重复 0 次或多次)。
代码生成器
代码生成器负责生成汇编代码。
前面那个 BNF 例子会被翻译成如下的汇编:
.intel_syntax noprefix
.globl main
main:
push 2
push 3
pop rdi
pop rax
add rax, rdi
push rax
pop rax
ret
它表示 2 + 3,以栈式虚拟机的方式实现。
它先把 2 和 3 这两个数字压入栈中,然后把 3 赋给 rdi(一个汇编寄存器),把 2 赋给 rax(一个汇编寄存器)。
最后计算 2 + 3,并把结果赋给 rax。
在这个例子里,接下来的两条指令可以忽略,因为它们只是执行了 rax = rax。
ret 返回结果,也就是 5。
进展
目前我已经实现了数学运算部分。
源代码已上传到 github。