实验内容
本阶段的内容是对pa1中生成的ast进行语义分析。
语义分析的目的是理解输入程序的含义,即弄清楚输入中所出现的每个标识符所指的内容以及明确输入中所出现的各种语句的含义,从而检查那些符合语法规定的程序可能出现的语义错误,以及提供信息以辅助中间代码生成。
对于标识符所指的内容,我们往往借助符号表来进行处理:随着语义分析过程的进行,当遇到符号定义的时候,我们需要往符号表中加入适当的记录信息;当遇到对符号的引用的时候,我们在符号表中查找这个符号的名字,并确定这个引用所指的是什么符号。如果引用一个没有定义的符号的话,我们将没法知道这个引用的具体含义,后面的翻译阶段也没法正常工作,因此需要把不合法的符号引用筛选出来。同样,如果有两个同样名字的符号在同一个作用域里面被定义,那么在引用这个名字的时候我们将不知道引用的到底是哪一个符号,因此这样的符号声明冲突也需要筛选出来。
对于语句的含义,我们需要根据语言规范中的规定对每一个语句或者表达式进行检查,看其是否合法。由于不符合语言规范的语句或者表达式所导致的执行效果是没有定义的,因此如果我们不把这样的不合法语句和表达式排除掉的话,那么在后面的翻译过程中编译器将不知道应该把它们翻译成什么。一般情况下,编译器对于语句和表达式进行的最基本的语义检查为类型检查,即检查这些语句和表达式的各个参数的数据类型是否符合规定,并且推断出它们执行结果的数据类型。
在具体实现中,我们把语义分析分为ast的两趟扫描进行:第一趟扫描的时候建立符号表的信息,并且检测符号声明冲突以及跟声明有关的符号引用问题;第二趟扫描的时候检查所有的语句以及表达式的参数的数据类型。在pa2中我们将借助ast结点的附加属性信息来进行语义分析。
为了规避rust对于可变引用的许多限制,框架中ast节点的附加属性一般都用Cell
或者RefCell
包裹起来。这的确不是一种很优雅的方式,另一种可行的做法是保持ast节点的不可变性,在原始的ast节点定义中不包含这些附加属性,需要添加属性的时候就创建新ast节点。这有一点函数式编程的思想,不过在rust中这样实现会不可避免地需要更大的代码量和更大的运行时开销,为了简单起见我们还是使用前一种看起来比较丑陋,但是实现起来相对容易的方法。
实验框架中已经实现了decaf语言的基本语法的语义分析,大家的任务是实现新语法的语义分析,同时也有可能需要修改既有的实现。新语法的具体语义可以参考专门的文档。如果有什么特别需要注意的地方,之后会在这里补充。
Last updated