# 语义分析

语义分析，顾名思义是执行一系列关于程序语义的处理，并明确程序的含义，例如把引用到的标识符和具体的符号关联，分辨所使用的语言规则等等。语义分析的目标是尽可能深入地了解源程序的语义，并利用这些信息进行后面的工作(例如代码优化)。语义分析一般沿着语法树结构进行，并借助属性文法来开展，这种由语法树结构指导处理的做法称为“语法驱动”。所谓属性文法，就是给每个语法符号绑定一系列的属性后形成的扩展文法，这种文法有利于进行语法驱动的语义分析。关于属性文法的具体说明请参考本文档后面的部分。

语法驱动的语义分析过程是以遍历ast的方式进行的，有两种主要的遍历方式：

1.  语义分析和建立ast同时进行，甚至不需要建立ast。这种方式的优点是编写的总代码量比较少，分析速度快，而且语法分析和语义分析(甚至中间代码翻译)是可以同时进行的。但是它的缺点在于语义分析能够利用的信息严重受限于语法分析的顺序，通常要求所引用的所有符号都必须在前面已经声明，而且实现的时候逻辑容易混乱。

    早期的编译器，如最初的c语言编译器，受制于计算资源的匮乏(尤其是可用的内存很受限制)，甚至都不能把文件整个读取进来然后解析，更不用说构建一棵完整的ast了。许多c语言的历史糟粕，如函数必须先声明才能使用等，很大程度上是因为编译器必须在parsing的同时进行语义分析，很难获得一个全局的视野。
2. 首先建立ast，然后再通过多次的遍历ast来进行语义值传递，语义错误检测等。这种方式的优点是功能强大，能够尽最大可能提取源程序的语义信息，而且其逻辑简单。缺点在于由于需要多次遍历ast，运行时的开销可能较大，显然从现在的观点来看，绝大多数情况下这个缺点都可以完全忽略了。

在原理课中我们讲解了语法制导的语义计算，并且关于它衍生出了很多的技巧和考点，例如ll分析中消除翻译模式中左递归和lr分析中模拟继承属性，这些都属于第一种方式。不幸(?)的是这些知识点并没有在实验中体现出来：因为我们根本不需要在生成ast的同时进行语义分析，而是选择了第二种简单得多的策略，在完整生成了ast之后才开始语义分析。
