最近看 UCC 的代码,研究下编译器的工作原理。大学时候的编译原理知识也忘得差不多了,本来当初就学的不好。现在拿一个实例来研究下吧。
从用户接口来看,一个正常的流程是这个样子的:
从一个 test.c 生成一个 test.exe.中间经历的步骤,从UCC源代码看,主要是这个流程:
1. 对 test.c 进行预处理。
这一步是 ucc 调用 vc 的cl进行的,结果生成 test.i 文件:
2. 对test.i 进行编译处理。
这一步是调用自己的编译器 ucl 进行的,结果生成 test.asm 汇编文件:
3. 对汇编文件 test.asm 进行编译
这一步是调用 vc 的汇编编译器 ml 进行的,结果生成 test.obj 文件:
4. 对 test.obj 和一些需要的lib 进行链接
这一步是调用 vc 的链接器 link 进行的,结果生成 test.exe 可执行文件:
由上面看到,仅仅对 C源程序的纯编译是调用自己的 ucl 进行,其余都是调用VC进行。接下来看看 ucl 的工作流程:
1. 读入 test.i, 生成语法树,并进行语法检查
这一步是调用函数 ParseTranslationUnit() 函数进行, 结果生成一个 AstTranslationUnit 类型的语法树( 暂且定名为 transUnit )。
2. 对 transUnit 进行语义检查
这一步是调用函数 CheckTranslationUnit() 函数进行,
3. 将transUnit 翻译成 中间代码
这一步是调用函数 Translate() 进行,
4. 通过 transUnit 生成汇编文件 test.asm
这一步是调用函数 EmitTranslationUnit() 进行,