动手实现编译器(九)——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循环很容易添加。在下一节中,我们将会实现函数的部分功能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值