1.1 实验中遇到的问题和解决方案

1.1.1 重命名恢复策略

一般情况下,出现重命名时,可以直接采用忽略的方法,但是存在一些特殊情况。例如程序的参数定义发生了重命名,此时若不将其加入符号表,则后续如果出现了该程序的调用,则无法检查形参和实参的一致性。
一种可行的方案是进行两遍语义分析,第一遍做定义域检查,第二遍做类型检查,只要第一遍中发现错误,就不再进行第二遍。
还有一种方案就是进行恢复,即对参数进行适当的修改再加入符号表中,考虑到PASCAL-S的标识符中不包含下划线,所以我们可以在参数发生重命名时,在其前面加上一个或若干个(如果重命名多次)下划线,再加入符号表,这样后续调用的时候就可以正常比较形参和实参的一致性了。
另外,主程序名、主程序参数名在发生重命名时,也可以采用这样的策略。

1.1.2 抽象语法树

普通语法树要设计翻译方案十分繁杂,所以我们语法分析需输出抽象语法树,删去各种冗余信息,并表示成一棵便于语义分析和代码生成的树形结构。为了减少YACC代码编写的复杂度,我们将AST的生成单独抽离出来,即YACC只生成普通语法分析树(没什么工作量),再编写程序将普通语法分析树转化为抽象语法树。

1.1.3 词法分析缓存下一行

词法分析在报错时,应能输出错误所在行,并用^在该行下部指出错误所在位置。这样就需要提前缓存下一行,所以引入了\n.*的词法规则匹配下一行,并利用yyless(1)退回下一行的所有内容,以便后续再进行具体的词法分析,同时需要在整个PASCAL-S源程序的最前面加上一个换行符,同时也要注意这样预处理后对于行号的影响,即当前行号比初始的实际行号大1。

1.1.4 位置信息

如果要想在语法分析中提供更加详细的位置信息,就需要当前语法成分在某一行的具体位置(开始位置和结束位置)。一开始我们自己设置了一个变量tokenPos,自己在词法分析中控制其清零和增长,但是精度不高,且不支持更高层的语法成分的位置信息,例如idlist的开始位置和结束位置。
此时我们查阅相关文档,发现了YACC提供了YYLTYPE结构,支持开始行号、开始列号、结束行号、结束列号四个值,并且能够自动计算各高层语法成分的这些取值,极大的方便了报错提供位置信息,只需要我们在LEX中添加终结符号位置信息的初始化代码即可。

1.2 存在的问题/待改进的地方

  • 可以添加更多的警告信息,以帮助PASCAL-S程序员更好的检查出程序中比较隐蔽的问题,例如当for语句的初值表达式和终值表达式均为整型常量表达式时,可以比较两者的大小,若后者小于前者,则给出警告信息,而非报错信息。
  • 应将true和false设置为布尔常量关键字,使得能够定义布尔型常量,且能够对布尔型变量直接赋值为true或者false
  • 没有专门处理浮点数字不完整的错误,例如3.和.3的错误,在我们编译器的词法分析中,将返回给语法整数3和符号点,语法中将能检测出错误,但是并不会专门提示浮点数字不完整的错误。
  • 语法错误恢复与处理,由于YACC的天生缺陷,很容易出现error burst的雪崩情况。