Support multi-letter local variables
这个commit是上一个《Support single-letter local variables》的升级完善。上一个commit基本已经搭好了大体的框架,此次修改也不是很大。
知识点
支持多字母变量面临主要的问题还是:如何分配变量地址、如何寻址。还好基本他在 parse
解决了。
一.tokenize修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Token *tokenize(void) {
//...
// Identifier
if (is_alpha(*p)) {
char *q = p++;
while (is_alnum(*p))
p++;
cur = new_token(TK_IDENT, cur, q, p - q);
continue;
}
// Multi-letter punctuators
//...
}
Identifier
典型的字母开头后续允许数字、字母、下划线的
二.parse、main修改
允许多字母变量那么就不能再用固定分配26个变量的方式,需要构建一张变量表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//parse.c
Var *locals;//变量链表
static Var *find_var(Token *tok) {
for (Var *var = locals; var; var = var->next)
if (strlen(var->name) == tok->len && !strncmp(tok->str, var->name, tok->len))
return var;
return NULL;
}
static Node *new_var_node(Var *var) {
Node *node = new_node(ND_VAR);
node->var = var; //Node节点持有Var信息(上次commit中Node节点的name替换成var)
return node;
}
static Var *new_lvar(char *name) { //构建变量链表
Var *var = calloc(1, sizeof(Var));
var->next = locals;
var->name = name;
locals = var;
return var;
}
static Node *primary(void) {
//...
Token *tok = consume_ident();
if (tok) {
//查找变量是否在变量表里,不在则新建并添加到链表里
Var *var = find_var(tok);
if (!var)
var = new_lvar(strndup(tok->str, tok->len));
return new_var_node(var);
}
return new_num(expect_number());
}
那么又是怎么计算出变量在栈上的偏移和需要分配多少栈空间呢? 第一个疑问的解决方案是在Var
持有offset
通过其在链表的位置赋值一个n*8
的值;分配的栈空间大小那么就是链表总变量数*8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//chibi.h
struct Var {
Var *next;
char *name; // Variable name
int offset; // Offset from RBP
};
//main.c
int main(int argc, char **argv) {
//...
Function *prog = program();
int offset = 0;
for (Var *var = prog->locals; var; var = var->next) {
offset += 8;
var->offset = offset;
}
prog->stack_size = offset;
// Traverse the AST to emit assembly.
codegen(prog);
return 0;
}
上面的函数program
改成返回的是Function
struct
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//chibi.h
struct Function {
Node *node;
Var *locals;
int stack_size;
};
//parse.c
Function *program(void) {
locals = NULL;
//...
Function *prog = calloc(1, sizeof(Function));
prog->node = head.next;
prog->locals = locals;
return prog;
}
Function
除了持有parse出来的node
链表、还有变量表和计算出来的要分配的栈空间大小。
三.codegen修改
由于 node节点持有变量分配在栈上的offset
、且要分配的栈大小在 main
中都提前计算好了,这次 codegen 修改实际非常小。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void gen_addr(Node *node) {
//...
printf(" lea rax, [rbp-%d]\n", node->var->offset);
//...
}
void codegen(Function *prog){
//...
printf(" sub rsp, %d\n", prog->stack_size);
// Emit code
for (Node *node = prog->node; node; node = node->next)
gen(node);
//..
}
This post is licensed under CC BY 4.0 by the author.