step5 实验指导
词法语法分析
如果你使用工具完成词法语法分析,修改你的语法规范以满足要求,自行修改词法规范,剩下的交给工具即可。
语义检查无需修改。
如果你是手写分析,TODO
注意 step6 引入 block_item
后,declaration
不再是语句,所以 if (a) int b;
也不是合法代码了。
这是 C 标准中一个设定。
悬吊 else 问题
这一节引入的 if 语句既可以带 else 子句也可以不带,但这会导致语法二义性:else
到底和哪一个 if
结合?
例如 if(a) if(b) c=0; else d=0;
,到底是 if(a) {if(b) c=0; else d=0;}
还是 if(a) {if(b) c=0;} else d=0;
(其中有大括号,现在还不支持,因为它是 step7 内容)?
这个问题被称为 悬吊 else(dangling else) 问题。
如果程序员没有加大括号,那么我们需要通过一个规定来解决歧义。
我们人为规定:else
和最近的 if
结合,也就是说上面两种理解中只有前者合法。
为了让 parser 能遵守这个规定,一种方法是设置产生式的优先级,优先选择没有 else 的 if。
按照这个规定,parser 看到 if(a) if(b) c=0; else d=0;
中第一个 if 时,选择没有 else 的 if;
而看到第二个时只能选择有 else 的 if 1,也就使得 else d=0;
被绑定到 if(b)
而不是 if(a)
了。
IR 生成
显然,我们需要跳转指令以实现 if,同时还需要作为跳转目的地的标号(label)。 我们的跳转指令和汇编中的类似,不限制跳转方向,往前往后都允许。
指令 | 参数 | 含义 | IR 栈大小变化 |
---|---|---|---|
label |
一个字符串 | 什么也不做,仅标记一个跳转目的地,用参数字符串标识 | 不变 |
beqz |
标号字符串 | 弹出栈顶元素,如果它等于零,那么跳转到参数标识的 label 开始执行 |
减少 1 |
bnez |
标号字符串 | 弹出栈顶元素,如果它不等于零,那么跳转到参数标识的 label 开始执行 |
减少 1 |
br |
标号字符串 | 无条件跳转到参数标识的 label 开始执行 |
不变 |
注意一个程序中的标号,也就是 label
的参数,必须唯一,否则跳转目的地就不唯一了。
简单地后缀一个计数器即可,例如 label l1
, label l2
, label l3
...
Visitor 遍历 AST 遇到一个有 else 的 if 语句,为了生成其 IR,要生成的是
- 首先是
条件表达式的 IR
:计算条件表达式。 beqz ELSE_LABEL
:判断条件,若条件不成立则执行 else 子句- 跳转没有执行,说明条件成立,所以之后是
then 子句的 IR
br END_LABEL
:条件成立,执行完 then 以后就结束了label ELSE_LABEL
,然后是else 子句的 IR
label END_LABEL
:if 语句结束。
例子:
if (a) return 2; else a=2+3;
的 IR 是
frameaddr k ; load
,其中k
是a
的 frameaddrbeqz else_label1
,数字后缀是避免标号重复的push 2 ; ret
br end_label1
label else_label1
,然后是push 2 ; push 3 ; add ; frameaddr k ; store ; pop
label end_label1
仿照上面,容易写出条件表达式的 IR 应该如何生成,并且同时也能保证满足语义规范
6.4. 和 2.3 不同,条件表达式规定了子表达式的求值顺序。 首先对条件求值。如果条件值为真,然后仅对
?
和:
之间的子表达式求值,作为条件表达式的值, 不得对:
之后的子表达式求值。 如果条件为假,类似仅对:
之后的子表达式求值。
类似,无 else 的 if 语句的 IR 包含
条件表达式的 IR
beqz END_LABEL
then 子句的 IR
label END_LABEL
汇编生成
如下表:
IR | 汇编 |
---|---|
label LABEL_STR |
LABEL_STR: |
br LABEL_STR |
j LABEL_STR 2 |
beqz LABEL_STR |
lw t1, 0(sp) ; addi sp, sp, 4 ; beqz t1, LABEL_STR |
bnez LABEL_STR |
lw t1, 0(sp) ; addi sp, sp, 4 ; bnez t1, LABEL_STR |
任务
- 改进你的编译器,支持本节引入的新特性,通过相关测试。
- 实验报告中回答思考题。
总结
本节主要就是引入了跳转,后面 step8 循环语句还会使用。
备注
1. 见思考题 ↩
2. 如果LABEL_STR
在当前函数内,j LABEL_STR
就等于beqz x0, LABEL_STR
↩