Skip to content

Commit

Permalink
ローカル変数導入
Browse files Browse the repository at this point in the history
  • Loading branch information
pocari committed Jun 27, 2020
1 parent a15be3b commit 9dbdc68
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 41 deletions.
33 changes: 29 additions & 4 deletions 9cc.c
Expand Up @@ -23,18 +23,43 @@ int main(int argc, char **argv) {

// head はfree用
Token *head = token = tokenize(user_input);
Node *node = expr();
// fprintf(stderr, "-------------------------------- tokenized\n");

// パースする(結果は グローバル変数のcode[100]に入る)
program();

// fprintf(stderr, "-------------------------------- parsed\n");
printf(".intel_syntax noprefix\n");
printf(".global main\n");
printf("main:\n");

codegen(node);
printf(" pop rax\n");

// プロローグ
// rbp初期化とローカル変数確保
// 識別子用のアルファベット小文字26(=8byte * 26 = 208byte)文字分の領域確保
printf(" push rbp\n"); // 前の関数呼び出しでのrbpをスタックに対比
printf(" mov rbp, rsp\n"); // この関数呼び出しでのベースポインタ設定
printf(" sub rsp, 208\n"); // ローカル変数確保

// 先頭の文からコード生成
for (int i = 0; code[i]; i++) {
codegen(code[i]);

// 最後に演算結果がスタックの先頭にあるので、スタックが溢れないようにそれを1文毎にraxに対比
printf(" pop rax\n");
}

// エピローグ
// rbpの復元と戻り値設定
// 最後の演算結果が、rax(forの最後でpopしてるやつ)にロードされてるのでそれをmainの戻り値として返す
printf(" mov rsp, rbp\n");
printf(" pop rbp\n");
printf(" ret\n");

free_tokens(head);
free_nodes(node);
for (int i = 0; code[i]; i++) {
free_nodes(code[i]);
}

return 0;
}
Expand Down
10 changes: 8 additions & 2 deletions 9cc.h
Expand Up @@ -11,6 +11,7 @@
// tokenize.c
typedef enum {
TK_RESERVED, // RESERVEDとなっているが今の時点では + または - の記号
TK_IDENT, // 識別子
TK_NUM, // 整数トークン
TK_EOF, // 入力終了
} TokenKind;
Expand All @@ -26,6 +27,7 @@ struct Token {

Token * tokenize(char *p);
void free_tokens(Token *cur);
void dump_token(Token *token);

// 現在着目しているトークン
extern Token *token;
Expand All @@ -44,6 +46,8 @@ typedef enum {
ND_LTE, // <=
ND_EQL, // ==
ND_NOT_EQL, // !=
ND_ASSIGN, // =
ND_LVAR, // ローカル変数
ND_NUM, // 整数
} NodeKind;

Expand All @@ -53,14 +57,16 @@ struct Node {
NodeKind kind;
Node *lhs;
Node *rhs;
int val;
int val; // kindがND_NUMの場合に使う
int offset; // kindがND_LVARの場合に使う(その変数のrbpからのオフセット)
};

void error_at(char *loc, char *fmt, ...);
void error(char *fmt, ...);
void free_nodes(Node *node);

Node *expr();
extern Node *code[100];
void program();


// codegen.c
Expand Down
45 changes: 42 additions & 3 deletions codegen.c
@@ -1,9 +1,48 @@
#include "9cc.h"

void gen_lval(Node *node) {
if (node->kind != ND_LVAR) {
error("代入の左辺値が変数ではありません");
}

// 識別子 a 〜 z まであり、それぞれrbpから8, 16, 32 ...のオフセットのところにあるので、
// その識別子に対応する場所のアドレスをスタックに積む
// スタックが伸びる方向は↓とする
// | ... |
// +-----+
// | | <- rbp (この関数のスタックのベースポインタ)
// +-----+
// | | <- 識別子 a (rbp - 8)
// +-----+
// | | <- 識別子 b (rbp - 16)
// +-----+
// | |
// +-----+
printf(" mov rax, rbp\n");
printf(" sub rax, %d\n", node->offset);
printf(" push rax\n");
}

void codegen(Node *node) {
if (node->kind == ND_NUM) {
printf(" push %d\n", node->val);
return ;
switch (node->kind) {
case ND_NUM:
printf(" push %d\n", node->val);
return ;
case ND_LVAR:
gen_lval(node);
printf(" pop rax\n"); // gen_lval(node)で積んだ変数のアドレスをraxにロード
printf(" mov rax, [rax]\n"); // 変数のアドレスにある値をraxにロード
printf(" push rax\n"); // 変数の値(rax)をスタックに積む
return;
case ND_ASSIGN:
gen_lval(node->lhs);
codegen(node->rhs);
//rhsの結果がスタックの先頭、その次に変数のアドレスが入ってるのでそれをロード
printf(" pop rdi\n"); // rhsの結果
printf(" pop rax\n"); // 左辺の変数のアドレス
printf(" mov [rax], rdi\n"); // 左辺の変数にrhsの結果を代入
printf(" push rdi\n"); // この代入結果自体もスタックに積む(右結合でどんどん左に伝搬していくときの右辺値になる)
return;
}

codegen(node->lhs);
Expand Down
64 changes: 60 additions & 4 deletions parser.c
Expand Up @@ -52,11 +52,21 @@ bool consume(char *op) {
return true;
}

Token *consume_ident() {
if (token->kind == TK_IDENT) {
Token *ret = token;
token = token->next;

return ret;
}
return NULL;
}

void expect(char *op) {
if (token->kind != TK_RESERVED ||
token->len != strlen(op) ||
memcmp(token->str, op, token->len)) {
error("'%c'ではありません", op);
error("'%c'(token->kind: %d, token->len: %d, strlen(op): %d)ではありません", op, token->kind, token->len, strlen(op));
}
token = token->next;
}
Expand All @@ -75,24 +85,56 @@ bool at_eof() {
return token->kind == TK_EOF;
}

// expr = equality
// program = stmt*
// stmt = expr ";"
// expr = assign
// assign = equality (= assign)?
// equality = relational ("==" relational | "!=" relational)*
// relational = add ("<" add | "<=" add | ">" add | ">=" add)*
// add = mul ("+" mul | "-" mul)*
// mul = unary ("*" unary | "/" unary)*
// unary = ("+" | "-")? primary
// primary = num | "(" expr ")"
// primary = num | ident | "(" expr ")"

void program();
Node *stmt();
Node *expr();
Node *assign();
Node *equality();
Node *relational();
Node *add();
Node *mul();
Node *unary();
Node *primary();

Node *code[100];

void program() {
int i = 0;
while (!at_eof()) {
code[i++] = stmt();
}
code[i] = NULL;
}

Node *stmt() {
Node *node = expr();
expect(";");
return node;
}

Node *expr() {
return equality();
return assign();
}

Node *assign() {
Node *node = equality();

if (consume("=")) {
return new_node(ND_ASSIGN, node, assign());
}

return node;
}

Node *equality() {
Expand Down Expand Up @@ -170,6 +212,20 @@ Node *primary() {
expect(")");
return node;
}

Token *t = consume_ident();
// fprintf(stderr, "try,consume_ident\n");
if (t) {
// fprintf(stderr, "true,consume_ident\n");
Node *node = calloc(1, sizeof(Node));
node->kind = ND_LVAR;
// 識別子aが rbp + 8
// 識別子bが rbp + 16 のオフセットになるように計算する
node->offset = (t->str[0] - 'a' + 1) * 8;
return node;
}

// () でも ident でもなければ 整数
return new_node_num(expect_number());
}

Expand Down
58 changes: 31 additions & 27 deletions test.sh
Expand Up @@ -16,32 +16,36 @@ assert() {
fi
}

assert 0 0
assert 42 42
assert 21 "5+20-4"
assert 41 " 12 + 34 - 5 "
assert 47 "5+6*7"
assert 15 "5 * (9- 6)"
assert 4 "( 3 + 5) / 2"
assert 60 "(1 + 2) * 3 + 4 + (5 + 6 * 7)"
assert 5 "-1 + 2 * 3"
assert 5 "-1 + +2 * 3"
assert 12 "-(-1 + -2) * 4"
assert 0 '0==1'
assert 1 '42==42'
assert 1 '0!=1'
assert 0 '42!=42'
assert 0 '0;'
assert 42 '42;'
assert 21 '5+20-4;'
assert 41 ' 12 + 34 - 5 ;'
assert 47 '5+6*7;'
assert 15 '5 * (9- 6);'
assert 4 '( 3 + 5) / 2;'
assert 60 '(1 + 2) * 3 + 4 + (5 + 6 * 7);'
assert 5 '-1 + 2 * 3;'
assert 5 '-1 + +2 * 3;'
assert 12 '-(-1 + -2) * 4;'
assert 0 '0==1;'
assert 1 '42==42;'
assert 1 '0!=1;'
assert 0 '42!=42;'

assert 1 '0<1'
assert 0 '1<1'
assert 0 '2<1'
assert 1 '0<=1'
assert 1 '1<=1'
assert 0 '2<=1'
assert 1 '0<1;'
assert 0 '1<1;'
assert 0 '2<1;'
assert 1 '0<=1;'
assert 1 '1<=1;'
assert 0 '2<=1;'

assert 1 '1>0'
assert 0 '1>1'
assert 0 '1>2'
assert 1 '1>=0'
assert 1 '1>=1'
assert 0 '1>=2'
assert 1 '1>0;'
assert 0 '1>1;'
assert 0 '1>2;'
assert 1 '1>=0;'
assert 1 '1>=1;'
assert 0 '1>=2;'

assert 10 'a=10;'
assert 11 'a=10;b=a;b+1;'
assert 20 'a=b=10;a+b;'
29 changes: 28 additions & 1 deletion tokenize.c
Expand Up @@ -9,6 +9,27 @@ Token *new_token(TokenKind kind, Token *cur, char *str, int len) {
return tok;
}

void dump_token(Token *t) {
char *kind_str;
switch (t->kind) {
case TK_RESERVED: // RESERVEDとなっているが今の時点では + または - の記号
kind_str = "TK_RESERVED";
break;
case TK_IDENT: // 識別子
kind_str = "TK_IDENT";
break;
case TK_NUM: // 整数トークン
kind_str = "TK_NUM";
break;
case TK_EOF: // 入力終了
kind_str = "TK_EOF";
break;
default:
error("不正なtokenです");
}
fprintf(stderr, "(Token kind: %s)\n", kind_str);
}

void free_tokens(Token *cur) {
while (cur->next) {
Token *tmp = cur->next;
Expand All @@ -30,6 +51,7 @@ Token * tokenize(char *p) {
Token *cur = &head;

while (*p) {
// fprintf(stderr, "*p ... %c\n", *p);
if (isspace(*p)) {
p++;
continue;
Expand All @@ -41,7 +63,7 @@ Token * tokenize(char *p) {
continue;
}

if (*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '(' || *p == ')' || *p == '>' || *p == '<') {
if (*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '(' || *p == ')' || *p == '>' || *p == '<' || *p == ';' || *p == '=') {
cur = new_token(TK_RESERVED, cur, p++, 1);
continue;
}
Expand All @@ -52,6 +74,11 @@ Token * tokenize(char *p) {
continue;
}

if ('a' <= *p && *p <= 'z') {
cur = new_token(TK_IDENT, cur, p++, 1);
continue;
}

error_at(p, "トークナイズできません");
}
new_token(TK_EOF, cur, p, 0);
Expand Down

0 comments on commit 9dbdc68

Please sign in to comment.