


本文主要介绍编译器结构中的前端部分。
LLVM前端通过对源程序的预处理,构成源程序的字符流扫描与分解,将单词序列提取为各类语法短语,生成抽象语法树,最终转换为中间代码。编译器前端包含的几个过程有:预编译、词法分析、语法分析、语义分析。
预编译所完成的基本上是对程序源代码的替代与整理工作,经过此类替代整理生成一个没有宏定义、条件编译指令、特殊符号、用户注释的输出文件。
预编译主要处理:
(1)文件包含
(2)宏展开
(3)条件编译
(4)删除注释
词法分析是对构成程序的源代码从左到右将字符逐个读入编译器,并扫描和分解字符流,从而识别出一个个单词,并确定单词的类型,将识别出的单词转换为统一的语法单元形式。包括如下类型:
(1)关键字,如int、float、if、 sizeof等;
(2)标识符,用来表示各种名字,如变量、数组名、函数名等;
(3)运算符,包括算术运算符、逻辑运算符、关系运算符等;
(4)分解符,包括“,、;”等符号;
(5)常数,包括整型、浮点型、字符型等。
如图,通过一个示例,分析了程序中各部分的类型。

语法分析的任务是将词法分析生成的单词组合成语法短语,同时分析这些短语是否符合高级程序设计语言中的语法规则。
有下面的规则来定义表达式:
(1)标识符是表达式;
(2)常数是表达式;
(3)若表达式1和表达式2都是表达式,那么表达式1 + 表达式2以及表达式1 * 表达式2也都是表达式。
有下面的规则来定义赋值语句:
(1)
(2)
(3)
(4)
如图,通过语法分析,可将如下代码生成相应的语法树。

语义分析阶段的任务是审查源代码有无语义错误,源代码中有些语法成分,按照语法规则去判断是正确的,但不符合语义规则,比如使用了没有声明的变量。
语义分析主要的任务可归结为以下四类:
(1)完成静态语义审查和处理;
(2)上下文相关性审查;
(3)类型匹配审查;
(4)类型转换。
比如语句c=a+b*3中,运算符*的两个运算对象分别是b和3,如果b是实型变量,3是整型常数,语义分析阶段执行类型审查之后,会自动地将整型量转换为实型量以完成同类型的数据运算,体现在语法分析所得到的语法树上,即增加一个运算符结点(inttoreal)。

示例分析
示例是一个简单的打印程序,用来观察编译的前端阶段所进行的处理。在程序中包括了头文件、宏定义和注释,在进行预编译后会对这些语句按照预编译规则进行相应的处理;在进行词法分析后会读入源程序的字符流,将其转换为对应的单词序列;在进行语法分析后会依据语法规则把源代码的单词序列组成语法短语构成的语法树;在进行语义分析后可以检查源代码有无语义错误。
//文件名:hello.c
#include <stdio.h>#define N 1024
int main ()
{
int a,b,c,d[N];
a = 2;
b = 4;
c = a + b*3;
printf( 运行命令:clang -E hello.c -o hello.i

运行命令:clang -fmodules -fsyntax-only -Xclang -dump-tokens hello.c

运行命令:clang -fmodules -fsyntax-only -Xclang -ast-dump hello.c

编译器前端的工作主要作用于源语言,前端通过分析高级语言代码的文本,相应地进行词法分析、语法分析、语义分析到生成中间代码的各个编译阶段,同时还包括与前端每个阶段相关的出错处理与符号表管理等。
(1)https://zhuanlan.zhihu.com/p/671404183?utm_psn=1751212485884256258
(2)【编译原理】编译器前端-CSDN博客


