



remove_complex_operands编译遍将 L Var 语言程序编译成一种受限制的形式,其中操作的参数是原子表达式。换句话说,此编译遍删除了复杂的操作数,例如下面程序中的表达式-10。这是通过引入一个新的临时变量,并将复杂操作数赋值给新变量,然后使用新变量代替原来的复杂操作数来实现的,如右侧remove_complex_operands的输出所示:
图2.11显示了此编译遍的输出语法,即
语言。它与
L
Var
语言唯一的区别是操作符参数被限制为由
atm
非终结符定义的原子表达式。特别是,整型常量和变量都是原子的。
图2.11
是
L
Var
语言,但其操作数仅限于原子表达式
原子表达式是纯粹的(它们不引起或依赖于副作用),而复杂表达式可能有副作用,例如Call(Name('input_int'),[])。在纯表达式和带副作用的表达式之间有这种分离的语言被称为
一元范式
(Moggi 1991;Danvy 2003),这解释了名称
中的
mon
的由来。remove_complex_operands编译遍的一个重要不变量是,复杂表达式之间的相对顺序不会改变,但原子表达式和复杂表达式之间的相对顺序是可以改变的,而且是经常改变的。这些更改是行为保持的,因为原子表达式是纯粹的。
我们建议这一编译遍使用一个名为rco_exp的辅助方法来实现,该方法带有两个参数:一个 L Var 语言的表达式和一个指定表达式是否需要变成原子量的布尔值。rco_exp方法应该返回一个由新表达式和元组对的列表组成的对,元组对将新的临时变量与其初始化表达式关联起来。
返回到带有表达式42+-10的示例程序,子表达式-10应该使用rco_exp函数处理,第二个参数为True,因为-10是+操作符的一个参数,因此需要变成原子量。rco_exp应用于-10时的输出如下所示:
要特别注意将原子表达式赋值给变量的程序,例如下面的程序。处理时应该保持这些赋值不变,如下面右侧的程序所示:
粗心的实现可能会产生以下带有不必要临时变量的输出。
习题2.1 在compiler.py中实现remove_complex_operands编译遍,为语法中的每个非终结符创建辅助函数,即rco_exp和rco_stmt。建议使用utils.generate_name()函数从存根stub字符串生成新的名称。
习题2.2 创建5个 L Var 语言程序来执行remove_complex_operands编译遍中最有趣的部分。这五个程序应该放在子目录tests/var中,文件名应该以文件扩展名.py结尾。运行支持代码中的run-tests.py脚本,检查输出程序是否产生与输入程序相同的结果。