在上一节中,我们实现了IF语句,在这一节中,我们将进一步实现WHILE循环。从某种意义上说,WHILE循环非常类似于IF语句没有“else”子句,除了总是跳回顶部的循环。
WHILE语句的SysY语法如下
Stmt → ‘while’ ‘(’ Cond ‘)’ Stmt
处理流程:
Lstart: 条件
如果条件为假跳到Lend
执行语句
跳转到Lstart
Lend:
这意味着我们可以借用扫描、解析和代码生成我们与 IF 语句一起使用的结构并进行了一些小的更改
还要处理 WHILE 语句。
修改词法分析
对于新的“while”关键字,我们需要一个新的单词T_WHILE
。
在match_keyword()
中加入对“while”的匹配。
case 'w': if(!strcmp(s, "while")) return (T_WHILE);
break;
修改语法分析
对于新的“while”操作,我们需要一个新的AST操作类型A_WHILE
。这个节点有一个左子树来计算条件,还有一个语句块的右子树,它自身是WHILE循环。
同时添加对WHILE语句的分析函数。
// 分析WHILE语句,并返回AST
struct ASTnode *while_statement()
{
struct ASTnode *condAST, *bodyAST;
// 匹配"while"和"("
match(T_WHILE, "while");
lparen();
// 分析以下表达式和后面的')',
// 确保树的操作是一个比较。
condAST = binexpr(0);
if (condAST->op < A_EQ || condAST->op > A_GE)
{
fprintf(stderr, "Bad comparison operator on line %d\n", Line);
exit(1);
}
rparen();
// 获得语句块的AST
bodyAST = Block_statement();
// 生成并返回AST
return mkastnode(A_WHILE, condAST, NULL, bodyAST, 0);
}
同时在Block_statement()
函数中添加对T_WHILE的处理:
case T_WHILE: tree = while_statement(); break;
修改代码生成器
增加为WHILE语句生成代码的函数:
// 为WHILE语句和可选的ELSE子句生成代码
int code_WHILE_generator(struct ASTnode *n)
{
int Lstart, Lend;
// 生成Lstart、Lend标签,
// 输出Lstart标签
Lstart = label();
Lend = label();
arm_label(Lstart);
// 生成条件代码,然后跳转到结束标签,
// 传送Lend标签作为寄存器
code_generator(n->left, Lend, n->op);
arm_freeall_registers();
// 生成语句块
code_generator(n->right, NOREG, n->op);
arm_freeall_registers();
// 输出跳转到Lstart标签,
// 输出Lend标签
arm_jump(Lstart);
arm_label(Lend);
return NOREG;
}
此外还要在code_generator()
函数中加入A_WHILE操作的处理:
case A_WHILE: return code_WHILE_generator(n);
比较运算符的一部分父AST节点可以是A_WHILE,所以改运算符跳转语句为
if(parentASTop == A_IF || parentASTop == A_WHILE)
return arm_compare_and_jump(n->op, leftreg, rightreg, reg);
修改汇编代码
因为WHILE语句共用IF语句的操作,所以无需生成另外的汇编代码。
测试结果
输入:
{
int i;
i=1;
while (i <= 20)
{
print i;
i= i + 1;
}
}
输出(out.s):
.text
.global __aeabi_idiv
.section .rodata
.align 2
.LC0:
.ascii "%d\012\000"
.text
.align 2
.global main
.type main, %function
main:
push {fp, lr}
add fp, sp, #4
.text
.comm i,4,4
mov r4, #1
ldr r3, .L2+0
str r4, [r3]
L1:
ldr r3, .L2+0
ldr r4, [r3]
mov r5, #20
cmp r4, r5
bgt L2
ldr r3, .L2+0
ldr r4, [r3]
mov r1, r4
ldr r0, .L3
bl printf
ldr r3, .L2+0
ldr r4, [r3]
mov r5, #1
add r4, r4, r5
ldr r3, .L2+0
str r4, [r3]
b L1
L2:
mov r3, #0
mov r0, r3
pop {fp, pc}
.L3:
.word .LC0
.size main, .-main
.L2:
.word i
输出(out):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
总结
因为WHILE循环和IF语句有很多相似之处,所以当我们完成IF语句后WHILE循环很容易添加。在下一节中,我们将会实现函数的部分功能。