动手实现编译器(九)——WHILE语句

在上一节中,我们实现了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循环很容易添加。在下一节中,我们将会实现函数的部分功能。


版权声明:本文为qq_34851605原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。