chibiccを読む~Cコンパイラコードリーディング~ ステップ27
トップページ
jupiteroak.hatenablog.com
「低レイヤを知りたい人のためのCコンパイラ作成入門」のCコンパイラを読んでいきます。
www.sigbus.info
ステップ27に該当
github.com
該当ステップなし
github.com
- 今回作成するコンパイラ
- 追加・修正されたコンパイラのソースコード(ステップ27、行コメントとブロックコメント)
- 追加・修正されたコンパイラのソースコード(該当ステップなし、ブロックのスコープに対応したコンパイラを作成する)
- テストコード
- Makefile
今回作成するコンパイラ
ステップ27 行コメントとブロックコメント(行コメントとブロックコメントを扱えるコンパイラを作成する)
↓
該当ステップなし(ブロックのスコープに対応したコンパイラを作成する)
追加・修正されたコンパイラのソースコード(ステップ27、行コメントとブロックコメント)
tokenize関数
Token *tokenize() { char *p = user_input; Token head; head.next = NULL; Token *cur = &head; while (*p) { // Skip whitespace characters. if (isspace(*p)) { p++; continue; } // Skip line comments. if (startswith(p, "//")) { p += 2; while (*p != '\n') p++; continue; } // Skip block comments. if (startswith(p, "/*")) { char *q = strstr(p + 2, "*/"); if (!q) error_at(p, "unclosed block comment"); p = q + 2; continue; } // Keyword or multi-letter punctuator char *kw = starts_with_reserved(p); if (kw) { int len = strlen(kw); cur = new_token(TK_RESERVED, cur, p, len); p += len; continue; } // Single-letter punctuator if (strchr("+-*/()<>;={},&[]", *p)) { cur = new_token(TK_RESERVED, cur, p++, 1); continue; } // Identifier if (is_alpha(*p)) { char *q = p++; while (is_alnum(*p)) p++; cur = new_token(TK_IDENT, cur, q, p - q); continue; } // String literal if (*p == '"') { cur = read_string_literal(cur, p); p += cur->len; continue; } // Integer literal if (isdigit(*p)) { cur = new_token(TK_NUM, cur, p, 0); char *q = p; cur->val = strtol(p, &p, 10); cur->len = p - q; continue; } error_at(p, "invalid token"); } new_token(TK_EOF, cur, p, 0); return head.next; }
文字列の先頭アドレスを取得する(変更なし)
char *p = user_input;
トークンからなる連結リストのヘッダーを作成する(変更なし)
Token head; head.next = NULL; Token *cur = &head;
空白文字の場合(変更なし)
while (*p) { // Skip whitespace characters. if (isspace(*p)) { p++; continue; }
コメント行の場合
https://github.com/rui314/chibicc/commit/81403b2d9969dafaaed05b8a4a85ee92bb80c89d#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R231
https://github.com/rui314/chibicc/blob/81403b2d9969dafaaed05b8a4a85ee92bb80c89d/tokenize.c#L231
// Skip line comments. if (startswith(p, "//")) { p += 2; while (*p != '\n') p++; continue; }
startswith(p, "//") が真となる場合 → 文字m042;pが"//"の一部だった場合は、コメント行とみなします。
"//"の2文字分だけpをインクリメントし、さらに、文字m042;pが改行文字'\n'になるまでインクリメントします。
コメントアウトの場合
https://github.com/rui314/chibicc/commit/81403b2d9969dafaaed05b8a4a85ee92bb80c89d#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R239
https://github.com/rui314/chibicc/blob/81403b2d9969dafaaed05b8a4a85ee92bb80c89d/tokenize.c#L239
// Skip block comments. if (startswith(p, "/*")) { char *q = strstr(p + 2, "*/"); if (!q) error_at(p, "unclosed block comment"); p = q + 2; continue; }
startswith(p, "/*") が真となる場合 → 文字m042;pが"/*"の一部だった場合は、コメントアウトとみなします。
strstr関数を呼び出して、コメントアウトの開始記号"/*"以降にある文字列(第一引数)から、コメントアウトの終了記号の文字列"*/"(第二引数)を探索し、
"*/"のアドレスを戻り値として取得します。pはコメントアウトの開始記号"/*"のうち"/"を指定するアドレスなので、p+2はコメントアウトの開始記号"/*"以降にある文字列の先頭アドレスとなります。
!qが偽となる場合 → "*/"のアドレスを戻り値として取得できなかった場合は、error_at関数を呼び出してエラーメッセージを出力します。
最後に、pをq + 2で更新して、コメントアウトの終了記号の文字列"*/"の次の文字をpが指定するようにします。
キーワードの場合(変更なし)
// Keyword or multi-letter punctuator char *kw = starts_with_reserved(p); if (kw) { int len = strlen(kw); cur = new_token(TK_RESERVED, cur, p, len); p += len; continue; }
1文字の記号の場合(変更なし)
// Single-letter punctuator if (strchr("+-*/()<>;={},&[]", *p)) { cur = new_token(TK_RESERVED, cur, p++, 1); continue; }
識別子の場合(変更なし)
// Identifier if (is_alpha(*p)) { char *q = p++; while (is_alnum(*p)) p++; cur = new_token(TK_IDENT, cur, q, p - q); continue; }
文字列リテラルの場合
// String literal if (*p == '"') { char *q = p++; while (*p && *p != '"') p++; if (!*p) error_at(q, "unclosed string literal"); p++; cur = new_token(TK_STR, cur, q, p - q); cur->contents = strndup(q + 1, p - q - 2); cur->cont_len = p - q - 1; continue; }
数値リテラルの場合(変更なし)
// Integer literal if (isdigit(*p)) { cur = new_token(TK_NUM, cur, p, 0); char *q = p; cur->val = strtol(p, &p, 10); cur->len = p - q; continue; }
その他の場合(変更なし)
error_at(p, "invalid token"); }
連結リストの先頭トークンを戻り値としてリターンする(変更なし)
return head.next;
追加・修正されたコンパイラのソースコード(該当ステップなし、ブロックのスコープに対応したコンパイラを作成する)
push_var関数
https://github.com/rui314/chibicc/commit/5346339ae97195bed027f44b55434be803f718fa#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR65
https://github.com/rui314/chibicc/blob/5346339ae97195bed027f44b55434be803f718fa/parse.c#L65
Var *push_var(char *name, Type *ty, bool is_local) { Var *var = calloc(1, sizeof(Var)); var->name = name; var->ty = ty; var->is_local = is_local; VarList *vl = calloc(1, sizeof(VarList)); vl->var = var; if (is_local) { vl->next = locals; locals = vl; } else { vl->next = globals; globals = vl; } VarList *sc = calloc(1, sizeof(VarList)); sc->var = var; sc->next = scope; scope = sc; return var; }
Var構造体を生成する
Var *var = calloc(1, sizeof(Var)); var->name = name; var->ty = ty; var->is_local = is_local;
calloc関数を呼び出して、変数を表現したVar構造体を生成します。
変数名(name)をnameメンバに、変数の型(を表現したType構造体変数ty)をtyメンバに、ローカル変数かグローバル変数かを示すis_localをis_localメンバに、設定します。
変数を管理しているValist構造体を生成する
VarList *vl = calloc(1, sizeof(VarList)); vl->var = var; if (is_local) { vl->next = locals; locals = vl; } else { vl->next = globals; globals = vl; }
calloc関数を呼び出して、VarList構造体を生成します。このVarList構造体は変数を表現したVar構造体を管理するために用いられます。
is_localがtrueの場合 → Var構造体がローカル変数を表現している場合は、localsが指定する連結リストにVarList構造体を追加します。
is_localがfalseの場合 → Var構造体がグローバル変数を表現している場合は、globalsが指定する連結リストにVarList構造体を追加します。
スコープを管理しているVarList構造体を生成する
VarList *sc = calloc(1, sizeof(VarList)); sc->var = var; sc->next = scope; scope = sc;
calloc関数を呼び出して、VarList構造体を生成します。このVarList構造体は変数のスコープを管理するために用いられます。
find_var関数
https://github.com/rui314/chibicc/commit/5346339ae97195bed027f44b55434be803f718fa#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR9
https://github.com/rui314/chibicc/blob/5346339ae97195bed027f44b55434be803f718fa/parse.c#L9
// Find a variable by name. Var *find_var(Token *tok) { for (VarList *vl = scope; vl; vl = vl->next) { Var *var = vl->var; if (strlen(var->name) == tok->len && !memcmp(tok->str, var->name, tok->len)) return var; } return NULL; }
scopeが指定する連結リストから、トークンが表現する変数名を探すようにします。
stmt_expr関数!
https://github.com/rui314/chibicc/commit/5346339ae97195bed027f44b55434be803f718fa#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR428
https://github.com/rui314/chibicc/blob/5346339ae97195bed027f44b55434be803f718fa/parse.c#L428
// stmt-expr = "(" "{" stmt stmt* "}" ")" // // Statement expression is a GNU C extension. Node *stmt_expr(Token *tok) { VarList *sc = scope; Node *node = new_node(ND_STMT_EXPR, tok); node->body = stmt(); Node *cur = node->body; while (!consume("}")) { cur->next = stmt(); cur = cur->next; } expect(")"); scope = sc; if (cur->kind != ND_EXPR_STMT) error_tok(cur->tok, "stmt expr returning void is not supported"); *cur = *cur->lhs; return node; }
stmt関数!
https://github.com/rui314/chibicc/commit/5346339ae97195bed027f44b55434be803f718fa#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR297
https://github.com/rui314/chibicc/blob/5346339ae97195bed027f44b55434be803f718fa/parse.c#L297
if (tok = consume("{")) { Node head; head.next = NULL; Node *cur = &head; VarList *sc = scope; while (!consume("}")) { cur->next = stmt(); cur = cur->next; } scope = sc; Node *node = new_node(ND_BLOCK, tok); node->body = head.next; return node; }
テストコード
https://github.com/rui314/chibicc/commit/5346339ae97195bed027f44b55434be803f718fa#diff-3722d9ba8feb2d3feac8ce71a209a638d4b404e1c53f937188761181594023e2R194
https://github.com/rui314/chibicc/blob/5346339ae97195bed027f44b55434be803f718fa/test.sh#L194
#!/bin/bash cat <<EOF | gcc -xc -c -o tmp2.o - int ret3() { return 3; } int ret5() { return 5; } int add(int x, int y) { return x+y; } int sub(int x, int y) { return x-y; } int add6(int a, int b, int c, int d, int e, int f) { return a+b+c+d+e+f; } EOF assert() { expected="$1" input="$2" ./chibicc <(echo "$input") > tmp.s gcc -static -o tmp tmp.s tmp2.o ./tmp actual="$?" if [ "$actual" = "$expected" ]; then echo "$input => $actual" else echo "$input => $expected expected, but got $actual" exit 1 fi } assert 0 'int main() { return 0; }' assert 42 'int main() { return 42; }' assert 21 'int main() { return 5+20-4; }' assert 41 'int main() { return 12 + 34 - 5 ; }' assert 47 'int main() { return 5+6*7; }' assert 15 'int main() { return 5*(9-6); }' assert 4 'int main() { return (3+5)/2; }' assert 10 'int main() { return -10+20; }' assert 10 'int main() { return - -10; }' assert 10 'int main() { return - - +10; }' assert 0 'int main() { return 0==1; }' assert 1 'int main() { return 42==42; }' assert 1 'int main() { return 0!=1; }' assert 0 'int main() { return 42!=42; }' assert 1 'int main() { return 0<1; }' assert 0 'int main() { return 1<1; }' assert 0 'int main() { return 2<1; }' assert 1 'int main() { return 0<=1; }' assert 1 'int main() { return 1<=1; }' assert 0 'int main() { return 2<=1; }' assert 1 'int main() { return 1>0; }' assert 0 'int main() { return 1>1; }' assert 0 'int main() { return 1>2; }' assert 1 'int main() { return 1>=0; }' assert 1 'int main() { return 1>=1; }' assert 0 'int main() { return 1>=2; }' assert 3 'int main() { int a; a=3; return a; }' assert 8 'int main() { int a; int z; a=3; z=5; return a+z; }' assert 3 'int main() { int a=3; return a; }' assert 8 'int main() { int a=3; int z=5; return a+z; }' assert 1 'int main() { return 1; 2; 3; }' assert 2 'int main() { 1; return 2; 3; }' assert 3 'int main() { 1; 2; return 3; }' assert 3 'int main() { int foo=3; return foo; }' assert 8 'int main() { int foo123=3; int bar=5; return foo123+bar; }' assert 3 'int main() { if (0) return 2; return 3; }' assert 3 'int main() { if (1-1) return 2; return 3; }' assert 2 'int main() { if (1) return 2; return 3; }' assert 2 'int main() { if (2-1) return 2; return 3; }' assert 3 'int main() { {1; {2;} return 3;} }' assert 10 'int main() { int i=0; i=0; while(i<10) i=i+1; return i; }' assert 55 'int main() { int i=0; int j=0; while(i<=10) {j=i+j; i=i+1;} return j; }' assert 55 'int main() { int i=0; int j=0; for (i=0; i<=10; i=i+1) j=i+j; return j; }' assert 3 'int main() { for (;;) return 3; return 5; }' assert 3 'int main() { return ret3(); }' assert 5 'int main() { return ret5(); }' assert 8 'int main() { return add(3, 5); }' assert 2 'int main() { return sub(5, 3); }' assert 21 'int main() { return add6(1,2,3,4,5,6); }' assert 32 'int main() { return ret32(); } int ret32() { return 32; }' assert 7 'int main() { return add2(3,4); } int add2(int x, int y) { return x+y; }' assert 1 'int main() { return sub2(4,3); } int sub2(int x, int y) { return x-y; }' assert 55 'int main() { return fib(9); } int fib(int x) { if (x<=1) return 1; return fib(x-1) + fib(x-2); }' assert 3 'int main() { int x=3; return *&x; }' assert 3 'int main() { int x=3; int *y=&x; int **z=&y; return **z; }' assert 5 'int main() { int x=3; int y=5; return *(&x+1); }' assert 5 'int main() { int x=3; int y=5; return *(1+&x); }' assert 3 'int main() { int x=3; int y=5; return *(&y-1); }' assert 5 'int main() { int x=3; int y=5; int *z=&x; return *(z+1); }' assert 3 'int main() { int x=3; int y=5; int *z=&y; return *(z-1); }' assert 5 'int main() { int x=3; int *y=&x; *y=5; return x; }' assert 7 'int main() { int x=3; int y=5; *(&x+1)=7; return y; }' assert 7 'int main() { int x=3; int y=5; *(&y-1)=7; return x; }' assert 8 'int main() { int x=3; int y=5; return foo(&x, y); } int foo(int *x, int y) { return *x + y; }' assert 3 'int main() { int x[2]; int *y=&x; *y=3; return *x; }' assert 3 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *x; }' assert 4 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *(x+1); }' assert 5 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *(x+2); }' assert 0 'int main() { int x[2][3]; int *y=x; *y=0; return **x; }' assert 1 'int main() { int x[2][3]; int *y=x; *(y+1)=1; return *(*x+1); }' assert 2 'int main() { int x[2][3]; int *y=x; *(y+2)=2; return *(*x+2); }' assert 3 'int main() { int x[2][3]; int *y=x; *(y+3)=3; return **(x+1); }' assert 4 'int main() { int x[2][3]; int *y=x; *(y+4)=4; return *(*(x+1)+1); }' assert 5 'int main() { int x[2][3]; int *y=x; *(y+5)=5; return *(*(x+1)+2); }' assert 6 'int main() { int x[2][3]; int *y=x; *(y+6)=6; return **(x+2); }' assert 3 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *x; }' assert 4 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+1); }' assert 5 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+2); }' assert 5 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+2); }' assert 5 'int main() { int x[3]; *x=3; x[1]=4; 2[x]=5; return *(x+2); }' assert 0 'int main() { int x[2][3]; int *y=x; y[0]=0; return x[0][0]; }' assert 1 'int main() { int x[2][3]; int *y=x; y[1]=1; return x[0][1]; }' assert 2 'int main() { int x[2][3]; int *y=x; y[2]=2; return x[0][2]; }' assert 3 'int main() { int x[2][3]; int *y=x; y[3]=3; return x[1][0]; }' assert 4 'int main() { int x[2][3]; int *y=x; y[4]=4; return x[1][1]; }' assert 5 'int main() { int x[2][3]; int *y=x; y[5]=5; return x[1][2]; }' assert 6 'int main() { int x[2][3]; int *y=x; y[6]=6; return x[2][0]; }' assert 8 'int main() { int x; return sizeof(x); }' assert 8 'int main() { int x; return sizeof x; }' assert 8 'int main() { int *x; return sizeof(x); }' assert 32 'int main() { int x[4]; return sizeof(x); }' assert 96 'int main() { int x[3][4]; return sizeof(x); }' assert 32 'int main() { int x[3][4]; return sizeof(*x); }' assert 8 'int main() { int x[3][4]; return sizeof(**x); }' assert 9 'int main() { int x[3][4]; return sizeof(**x) + 1; }' assert 9 'int main() { int x[3][4]; return sizeof **x + 1; }' assert 8 'int main() { int x[3][4]; return sizeof(**x + 1); }' assert 0 'int x; int main() { return x; }' assert 3 'int x; int main() { x=3; return x; }' assert 0 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[0]; }' assert 1 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[1]; }' assert 2 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[2]; }' assert 3 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[3]; }' assert 8 'int x; int main() { return sizeof(x); }' assert 32 'int x[4]; int main() { return sizeof(x); }' assert 1 'int main() { char x=1; return x; }' assert 1 'int main() { char x=1; char y=2; return x; }' assert 2 'int main() { char x=1; char y=2; return y; }' assert 1 'int main() { char x; return sizeof(x); }' assert 10 'int main() { char x[10]; return sizeof(x); }' assert 1 'int main() { return sub_char(7, 3, 3); } int sub_char(char a, char b, char c) { return a-b-c; }' assert 97 'int main() { return "abc"[0]; }' assert 98 'int main() { return "abc"[1]; }' assert 99 'int main() { return "abc"[2]; }' assert 0 'int main() { return "abc"[3]; }' assert 4 'int main() { return sizeof("abc"); }' assert 7 'int main() { return "\a"[0]; }' assert 8 'int main() { return "\b"[0]; }' assert 9 'int main() { return "\t"[0]; }' assert 10 'int main() { return "\n"[0]; }' assert 11 'int main() { return "\v"[0]; }' assert 12 'int main() { return "\f"[0]; }' assert 13 'int main() { return "\r"[0]; }' assert 27 'int main() { return "\e"[0]; }' assert 0 'int main() { return "\0"[0]; }' assert 106 'int main() { return "\j"[0]; }' assert 107 'int main() { return "\k"[0]; }' assert 108 'int main() { return "\l"[0]; }' assert 0 'int main() { return ({ 0; }); }' assert 2 'int main() { return ({ 0; 1; 2; }); }' assert 1 'int main() { ({ 0; return 1; 2; }); return 3; }' assert 3 'int main() { return ({ int x=3; x; }); }' assert 2 'int main() { /* return 1; */ return 2; }' assert 2 'int main() { // return 1; return 2; }' assert 2 'int main() { int x=2; { int x=3; } return x; }' assert 2 'int main() { int x=2; { int x=3; } { int y=4; return x; }}' assert 3 'int main() { int x=2; { x=3; } return x; }' echo OK
Makefile
https://github.com/rui314/chibicc/blob/5346339ae97195bed027f44b55434be803f718fa/Makefile
CFLAGS=-std=c11 -g -static SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o) chibicc: $(OBJS) $(CC) -o $@ $(OBJS) $(LDFLAGS) $(OBJS): chibicc.h test: chibicc ./test.sh clean: rm -f chibicc *.o *~ tmp* .PHONY: test clean