1.1错误检查和恢复策略详述
1.1.1 恢复策略
1.1.1.1 形参重定义
形参发生重定义错误时,不能只是报错,因为后续还会可能会调用子程序,在调用时,需要进行形参和实参的一致性检查,如果在函数定义时,没有成功将形参加入符号表,会对后续的调用检查产生影响。
所以这里需要进行适当的恢复,恢复的策略就是仍将形参加入符号表,但需要进行一些改动。考虑到PASCAL-S的标识符不包含下划线,所以我们可以在形参和别的符号发生重名时,在其前面加入下划线。而且形参之间可能还会再次重名,所以需要统计重名的次数,然后根据重名的次数来决定在标识符前面加多少个下划线,最后再加入符号表即可。
1.1.1.2 主程序名重定义
当主程序名和库程序同名时,采用和形参重定义相同的加下划线的策略。
1.1.1.3 其它标识符的重定义
其它标识符在检测到重定义时,无需进行恢复,因为我们无从得知程序员接下来对该标识符的使用是按照哪种定义的。
1.1.1.4 类型检查
- 数组引用时,即使其下标表达式类型检查出错,或者下标表达式取值越界,其检查类型的返回值仍为其定义时的数组元素类型
函数调用时,即使其调用的语义分析检测出错误,其检查类型的返回值仍为其定义时的返回值类型
1.1.1.5 数组定义时下界大于上界
1.1.2 各抽象语法树的语义分析策略详述
1.1.2.1 _VariantReference
注意,检查成功的情况,需要标记_VariantReference的具体种类,即是”array”、”var”、“constant”、”function call”还是”function return reference”
检测是否未定义,若未定义,则放弃当前节点的后续检查
- 若已定义,且是非数组引用
- 如果是当前所在函数名
- 检查是否是函数返回值赋值语句的左值
- 否则就是函数的无参递归调用,需检查函数定义时形参个数是否为0
- 如果是函数名
- 检查是否是赋值语句的左值,是就报错
- 否则就是函数的无参调用,需检查函数定义时形参个数是否为0
- 如果是函数名、传值参数、引用参数、普通变量、常量以外的情况,报错
- 如果是当前所在函数名
若已定义,且是数组引用
如果是复合语句块
- 对语句列表中的每一个语句进行语义分析
- 如果是repeat语句
- 检查条件表达式的类型
- 对循环语句体进行语义分析
- 如果是while语句
- 检查条件表达式的类型
- 对循环语句体进行语义分析
- 如果是for语句
- 检查循环变量
- 如果未定义,放弃for语句的后续语义分析
- 如果种类、类型错误,放弃for语句的后续语义分析
- 检查初值表达式和终值表达式的类型
- 对循环语句体进行语义分析
- 检查循环变量
- 如果是if语句
- 检查条件表达式的类型
- 对then语句体进行语义分析
- 如果有else部分,对else语句体进行语义分析
- 如果是赋值语句
- 对左值_VariantReference和右值_Expression进行语义分析
- 如果左值是常量,则放弃赋值语句的后续语义分析
- 如果是左值函数名,且作为当前函数的返回值语句,则检查右值表达式的类型是否和当前函数返回值类型一致
- 否则检查左值和右值的类型是否一致
- 对左值_VariantReference和右值_Expression进行语义分析
如果是过程调用语句
- 如果过程名未定义,则放弃当前过程调用语句的后续语义分析
- 如果定义的种类不是过程,则放弃当前过程调用语句的后续语义分析
- 如果是exit过程调用
- 如果当前所在程序是过程,则检查exit的实参是否为0
- 如果当前所在程序是函数,则检查exit的实参是否为1,类型是否和函数返回值一致,对实参表达式进行语义分析
- 如果是read过程调用,需检查实参个数是否至少为1,检查实参的种类是否正确,且类型不能为boolean;对实参表达式进行语义分析
- 如果是write过程调用,需检查实参个数是否至少为1;对实参表达式进行语义分析
- 如果是用户自定义过程调用,需检查实参和形参的一致性,如果形参是引用参数,实参就必须是普通变量、传值参数、引用参数、数组元素,而不能是别的情况;对实参表达式进行语义分析
1.1.2.3 _FormalParameter
如果当前参数名和库程序名、主程序名或主程序参数名同名,报错并恢复
- 如果当前参数名和当前符号表中已定义的符号同名,报错并恢复
-
1.1.2.4 _FunctionDefinition
如果重定义,则放弃当前函数/过程定义的后续语义分析
- 将函数/过程定义加入到主符号表中,创建并定位到子符号表
- 对形式参数列表进行语义分析
- 对常量定义列表进行语义分析
- 对变量定义列表进行语义分析
- 对语句列表进行语义分析
-
1.1.2.5 _Variant
如果重定义,则放弃当前变量定义的后续语义分析
- 如果是普通变量,则加入符号表
如果是数组,则检查是否存在下界大于上界的情况,如果存在,进行报错和恢复,最后加入符号表
1.1.2.6 _Constant
如果重定义,则放弃当前常量定义的后续语义分析
- 如果直接由常量提供取值,则加入符号表
如果由别的标识符提供取值,则检查右值标识符是否未定义,如果已定义,检查是否是常量,如果都没有问题,就加入符号表
1.1.2.7 _SubProgram
对全局常量定义列表进行语义分析
- 对全局变量定义列表进行语义分析
- 对子程序定义列表进行语义分析,每个子程序分析完后,都要重定位到主符号表
对主程序语句列表进行语义分析,由于主程序可以看做是一个过程,所以无需进行函数返回值语句存在性检测
1.1.2.8 _FunctionCall
如果函数名未定义,则报错,放弃当前函数调用的后续语义分析
- 如果定义的不是函数名,则报错,放弃当前函数调用的后续语义分析
检查实参与形参的一致性,对实参表达式进行语义分析,如果形参是引用参数,还需检查实参是否是普通变量、传值参数、引用参数或数组元素。
1.1.2.9 _Expression
如果是”var”
- 对_VariantReference进行语义分析
- 检查是否是整型常量,如果是,计算取值,并标记为整型常量表达式
- 返回类型_VariantReference的类型作为当前表达式的类型
- 如果是”integer”
- 标记为整型常量表达式
- 返回”integer”类型
- 如果是”real”
- 返回“real”类型
- 如果是“char”
- 返回”char”类型
- 如果是“function”
- 对_FunctionCall进行语义分析
- 返回_FunctionCall的类型作为当前表达式的类型
- 如果是“compound”::“relop”
- 对两个操作数表达式进行语义分析
- 对两个操作数表达式的类型进行检查
- 返回”boolean”或“error”类型
- 如果是“compound”::“not”
- 对操作数表达式进行语义分析
- 对操作数表达式的类型进行检查
- 返回“boolean”或“error”类型
- 如果是“compound”::“minus”
- 对操作数表达式进行语义分析
- 检查、计算、标记整型常量表达式
- 对操作数表达式的类型进行检查
- 返回”integer”或“real”或“error”类型
- 如果是“compound”::“bracket”
- 对操作数表达式进行语义分析
- 返回操作数表达式的类型作为当前表达式的类型
- 如果是“compound”::“+、-、*、/”
- 对两个操作数表达式进行语义分析
- 检查、计算、标记整型常量表达式
- 对两个操作数表达式的类型进行检查
- 返回“integer”或“real”或“error”类型
- 如果是“compound”::“div、mod”
- 对两个操作数表达式进行语义分析
- 检查、计算、标记整型常量表达式
- 对两个操作数表达式的类型进行检查
- 返回”integer”或“error”类型
如果是“compound”::“and”、or”
检查主程序名是否与库程序名同名,如果同名,报错并进行恢复,并将信息加入主符号表
- 检查主程序参数名是否与库程序名、主程序名同名,如果同名,报错并进行恢复,并将信息加入主符号表
- 将库程序加入主符号表
- 对_SubPorgam进行语义分析