Accept multiple statements separated by semicolons
通过分号分割多个语句。
知识点
分号(;
)属于c函数ispunct
处理的符号,所以tokenize不需要修改。codegen有小修改,稍微大点的修改是parse.
一.parse修改
之前处理的输入都是单语句,现在可以处理多语句。来看他的语法分析部分
1
2
// program = stmt*
// stmt = expr ";"
一个语句(stmt)由表达式(expr)以分号结尾,而一个程序(program)可以有多个语句构成。
既然一个程序可以由多个语句构成,意味着需要构造多个 stmt
节点,形成一个 链表。构造的stmt
节点它有单一的子节点,不像之前的 relational、add、mul构造的节点是具备lhs、rhs子节点的。作者为了方便在原来的Node
上直接扩展加了 next
字段。来看下链表的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Node *program(void){
Node head={};
Node *cur= &head;
while(!at_eof()){
cur->next=stmt();
cur=cur->next;
}
return head.next;
}
static Node *stmt(void){
Node *node=expr();
expect(";");
return node;
}
二.codegen修改
codegen从之前的单次gen
调用,变成了遍历链表再逐条语句stmt
调用gen
。
这里需要注意的是由于在gen
函数的结尾生成的汇编代码将语句的返回值默认压到栈顶,生成的汇编代码本质上是一个函数(main函数)程序退出也就是该函数的退出。所以之前他在codegen
函数结尾生成一条pop rax
指令弹出栈顶数据将语句的返回值当作程序退出时的返回值。
1
2
3
4
5
6
7
8
9
10
static void gen(Node *node) {
//...
printf(" push rax\n");
}
void codegen(Node *node) {
//...
gen(node);
printf(" pop rax\n");
printf(" ret\n");
}
虽然这次他修改支持多语句,但是返回值只支持最后一条语句的。
1
2
3
4
5
6
7
8
void codegen(Node *node) {
//...
for(Node *n=node; n; n=n->next){
gen(n);
printf(" pop rax\n");
}
printf(" ret\n");
}
This post is licensed under CC BY 4.0 by the author.