From 8a8a35b9241dcbdc39926e5e64f6f22e8174418f Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 4 Aug 2019 15:21:44 +0900 Subject: [PATCH] Support multi-letter local variables --- chibicc.h | 25 +++++++++++++++++++++---- codegen.c | 16 ++++++++-------- main.c | 13 +++++++++++-- parse.c | 42 +++++++++++++++++++++++++++++++++++------- test.sh | 7 +++++-- tokenize.c | 14 ++++++++++++-- 6 files changed, 92 insertions(+), 25 deletions(-) diff --git a/chibicc.h b/chibicc.h index 223295b..052a643 100644 --- a/chibicc.h +++ b/chibicc.h @@ -9,6 +9,7 @@ // tokenize.c // +// Token typedef enum { TK_RESERVED, // Keywords or punctuators TK_IDENT, // Identifiers @@ -29,6 +30,7 @@ struct Token { void error(char *fmt, ...); void error_at(char *loc, char *fmt, ...); bool consume(char *op); +char *strndup(char *p, int len); Token *consume_ident(); void expect(char *op); int expect_number(); @@ -43,6 +45,15 @@ extern Token *token; // parse.c // +// Local variable +typedef struct Var Var; +struct Var { + Var *next; + char *name; // Variable name + int offset; // Offset from RBP +}; + +// AST node typedef enum { ND_ADD, // + ND_SUB, // - @@ -55,7 +66,7 @@ typedef enum { ND_ASSIGN, // = ND_RETURN, // "return" ND_EXPR_STMT, // Expression statement - ND_LVAR, // Local variable + ND_VAR, // Variable ND_NUM, // Integer } NodeKind; @@ -66,14 +77,20 @@ struct Node { Node *next; // Next node Node *lhs; // Left-hand side Node *rhs; // Right-hand side - char name; // Used if kind == ND_LVAR + Var *var; // Used if kind == ND_VAR int val; // Used if kind == ND_NUM }; -Node *program(); +typedef struct { + Node *node; + Var *locals; + int stack_size; +} Program; + +Program *program(); // // codegen.c // -void codegen(Node *node); +void codegen(Program *prog); diff --git a/codegen.c b/codegen.c index 7208776..a37b009 100644 --- a/codegen.c +++ b/codegen.c @@ -2,9 +2,8 @@ // Pushes the given node's address to the stack. void gen_addr(Node *node) { - if (node->kind == ND_LVAR) { - int offset = (node->name - 'a' + 1) * 8; - printf(" lea rax, [rbp-%d]\n", offset); + if (node->kind == ND_VAR) { + printf(" lea rax, [rbp-%d]\n", node->var->offset); printf(" push rax\n"); return; } @@ -35,7 +34,7 @@ void gen(Node *node) { gen(node->lhs); printf(" add rsp, 8\n"); return; - case ND_LVAR: + case ND_VAR: gen_addr(node); load(); return; @@ -96,7 +95,7 @@ void gen(Node *node) { printf(" push rax\n"); } -void codegen(Node *node) { +void codegen(Program *prog) { printf(".intel_syntax noprefix\n"); printf(".global main\n"); printf("main:\n"); @@ -104,10 +103,11 @@ void codegen(Node *node) { // Prologue printf(" push rbp\n"); printf(" mov rbp, rsp\n"); - printf(" sub rsp, 208\n"); + printf(" sub rsp, %d\n", prog->stack_size); - for (Node *n = node; n; n = n->next) - gen(n); + // Emit code + for (Node *node = prog->node; node; node = node->next) + gen(node); // Epilogue printf(".Lreturn:\n"); diff --git a/main.c b/main.c index 45d8feb..3ac6661 100644 --- a/main.c +++ b/main.c @@ -7,9 +7,18 @@ int main(int argc, char **argv) { // Tokenize and parse. user_input = argv[1]; token = tokenize(); - Node *node = program(); + Program *prog = program(); + + // Assign offsets to local variables. + 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(node); + codegen(prog); + return 0; } diff --git a/parse.c b/parse.c index 634f95d..2cee0fa 100644 --- a/parse.c +++ b/parse.c @@ -1,5 +1,15 @@ #include "chibicc.h" +Var *locals; + +// Find a local variable by name. +Var *find_var(Token *tok) { + for (Var *var = locals; var; var = var->next) + if (strlen(var->name) == tok->len && !memcmp(tok->str, var->name, tok->len)) + return var; + return NULL; +} + Node *new_node(NodeKind kind) { Node *node = calloc(1, sizeof(Node)); node->kind = kind; @@ -25,12 +35,20 @@ Node *new_num(int val) { return node; } -Node *new_lvar(char name) { - Node *node = new_node(ND_LVAR); - node->name = name; +Node *new_var(Var *var) { + Node *node = new_node(ND_VAR); + node->var = var; return node; } +Var *push_var(char *name) { + Var *var = calloc(1, sizeof(Var)); + var->next = locals; + var->name = name; + locals = var; + return var; +} + Node *stmt(); Node *expr(); Node *assign(); @@ -42,7 +60,9 @@ Node *unary(); Node *primary(); // program = stmt* -Node *program() { +Program *program() { + locals = NULL; + Node head; head.next = NULL; Node *cur = &head; @@ -51,7 +71,11 @@ Node *program() { cur->next = stmt(); cur = cur->next; } - return head.next; + + Program *prog = calloc(1, sizeof(Program)); + prog->node = head.next; + prog->locals = locals; + return prog; } // stmt = "return" expr ";" @@ -160,8 +184,12 @@ Node *primary() { } Token *tok = consume_ident(); - if (tok) - return new_lvar(*tok->str); + if (tok) { + Var *var = find_var(tok); + if (!var) + var = push_var(strndup(tok->str, tok->len)); + return new_var(var); + } return new_num(expect_number()); } diff --git a/test.sh b/test.sh index 18654a1..53b72f9 100755 --- a/test.sh +++ b/test.sh @@ -46,11 +46,14 @@ assert 1 'return 1>=0;' assert 1 'return 1>=1;' assert 0 'return 1>=2;' +assert 3 'a=3; return a;' +assert 8 'a=3; z=5; return a+z;' + assert 1 'return 1; 2; 3;' assert 2 '1; return 2; 3;' assert 3 '1; 2; return 3;' -assert 3 'a=3; return a;' -assert 8 'a=3; z=5; return a+z;' +assert 3 'foo=3; return foo;' +assert 8 'foo123=3; bar=5; return foo123+bar;' echo OK diff --git a/tokenize.c b/tokenize.c index b752c5a..1f6d7e2 100644 --- a/tokenize.c +++ b/tokenize.c @@ -26,6 +26,13 @@ void error_at(char *loc, char *fmt, ...) { exit(1); } +char *strndup(char *p, int len) { + char *buf = malloc(len + 1); + strncpy(buf, p, len); + buf[len] = '\0'; + return buf; +} + // Consumes the current token if it matches `op`. bool consume(char *op) { if (token->kind != TK_RESERVED || strlen(op) != token->len || @@ -123,8 +130,11 @@ Token *tokenize() { } // Identifier - if ('a' <= *p && *p <= 'z') { - cur = new_token(TK_IDENT, cur, p++, 1); + if (is_alpha(*p)) { + char *q = p++; + while (is_alnum(*p)) + p++; + cur = new_token(TK_IDENT, cur, q, p - q); continue; } -- GitLab