我已经阅读了几篇论文,文章和《编译器:原理,技巧和工具(第二版)》(第2版)(又称“龙书”)第4章第4.1.4节,它们都讨论了语法编译器错误恢复的主题。但是,在尝试了几种现代编译器之后,我发现它们还可以从语义错误以及语法错误中恢复。
我非常了解编译器从句法相关的错误中恢复的算法和技术,但是我并不完全理解编译器如何从语义错误中恢复。
我目前使用的访问者模式略有变化,以从我的抽象语法树生成代码。考虑我的编译器编译以下表达式:
1 / (2 * (3 + "4"))
编译器将生成以下抽象语法树:
op(/)
|
-------
/ \
int(1) op(*)
|
-------
/ \
int(2) op(+)
|
-------
/ \
int(3) str(4)
然后,代码生成阶段将使用访问者模式来递归地遍历抽象语法树并执行类型检查。将遍历抽象语法树,直到编译器到达表达式的最内层为止。(3 + "4")
。然后,编译器会检查表达式的每一面,并发现它们在语义上不相等。编译器引发类型错误。这就是问题所在。编译器现在应该做什么?
为了使编译器能够从此错误中恢复并继续对表达式的外部部分进行类型检查,它必须从评估表达式的最内部部分到表达式的下一个内部部分返回某种类型(int
或str
)。但是它根本没有返回的类型。由于发生类型错误,因此没有推断出任何类型。
我提出的一种可能的解决方案是,如果确实发生类型错误,则应该引发错误,并且应该将一个特殊值(表示发生类型错误)返回给以前的抽象语法树遍历调用。如果先前的遍历调用遇到此值,则他们知道在抽象语法树中更深处发生类型错误,因此应避免尝试推断类型。尽管此方法确实有效,但效率似乎很低。如果表达式的最内层部分位于抽象语法树的深处,则编译器将不得不进行许多递归调用,而仅是意识到无法完成任何实际工作,而只需从每个返回即可。
我是否使用了上述方法(我对此表示怀疑)。如果是这样,效率不高吗?如果不是,那么编译器从语义错误中恢复时将使用什么方法?