step3 实验指导
本实验指导使用的例子为:
1+3
词法语法分析
在 step3 中,我们引入了算术运算,因此需要引入新的抽象语法树节点:
节点 | 成员 | 含义 |
---|---|---|
Binary |
左操作数 lhs ,右操作数 rhs ,运算类型 op |
二元运算 |
对有兴趣的同学:虽然
-2
和2-3
里面的-
意义不同,但 lexer 不知道这点(parser 才知道),所以它们都会用同样的 token kind-
表示。 但有时,可能需要后续阶段告诉 lexer(或 parser)一些信息,最经典的例子是 “typedef-name identifier problem”。
语义分析
同 Step2。
中间代码生成
与一元操作类似,针对加法,我们需要设计一条中间代码指令来表示它,给出的参考定义如下:
请注意,TAC 指令的名称只要在你的实现中是一致的即可,并不一定要和文档一致。
指令 | 参数 | 作用 |
---|---|---|
ADD |
T0,T1 |
将两个参数相加 |
因此,测例可以翻译成如下的中间代码:
_T0 = 1
_T1 = 3
_T2 = ADD _T0, _T1
目标代码生成
step3 目标代码生成步骤的关键点与 step2 相同,针对中间代码指令,选择合适的 RISC-V 指令来完成翻译工作。
li t0, 1
li t1, 3
add t2, t0, t1
思考题
- 我们知道“除数为零的除法是未定义行为”,但是即使除法的右操作数不是 0,仍然可能存在未定义行为。请问这时除法的左操作数和右操作数分别是什么?请将这时除法的左操作数和右操作数填入下面的代码中,分别在你的电脑(请标明你的电脑的架构,比如 x86-64 或 ARM)中和 RISCV-32 的 qemu 模拟器中编译运行下面的代码,并给出运行结果。(编译时请不要开启任何编译优化)
#include <stdio.h>
int main() {
int a = 左操作数;
int b = 右操作数;
printf("%d\n", a / b);
return 0;
}
总结
本步骤中其他运算符的实现逻辑和方法与加法类似,可以参考二元加法的实现方法设计实现其他二元运算符。