为了学习编译器的工作原理,我最近在编写一个 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,以栈式虚拟机的方式实现。

它先把 23 这两个数字压入栈中,然后把 3 赋给 rdi(一个汇编寄存器),把 2 赋给 rax(一个汇编寄存器)。 最后计算 2 + 3,并把结果赋给 rax。 在这个例子里,接下来的两条指令可以忽略,因为它们只是执行了 rax = raxret 返回结果,也就是 5

进展

目前我已经实现了数学运算部分。

源代码已上传到 github