├── .clang-format ├── .devcontainer.json ├── .gitignore ├── .travis.yml ├── .vscode └── tasks.json ├── 9cc.c ├── 9cc.h ├── Dockerfile ├── Makefile ├── README.md ├── codegen.c ├── docker.mk ├── parse.c ├── test.sh ├── test ├── func_call_multi_arg.c └── func_call_test.c ├── util.c └── util_test.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: MultiLine 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakInheritanceList: BeforeColon 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializersBeforeComma: false 45 | BreakConstructorInitializers: BeforeColon 46 | BreakAfterJavaFieldAnnotations: false 47 | BreakStringLiterals: true 48 | ColumnLimit: 80 49 | CommentPragmas: '^ IWYU pragma:' 50 | CompactNamespaces: false 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 52 | ConstructorInitializerIndentWidth: 4 53 | ContinuationIndentWidth: 4 54 | Cpp11BracedListStyle: true 55 | DerivePointerAlignment: false 56 | DisableFormat: false 57 | ExperimentalAutoDetectBinPacking: false 58 | FixNamespaceComments: true 59 | ForEachMacros: 60 | - foreach 61 | - Q_FOREACH 62 | - BOOST_FOREACH 63 | IncludeBlocks: Preserve 64 | IncludeCategories: 65 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 66 | Priority: 2 67 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 68 | Priority: 3 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: false 73 | IndentPPDirectives: None 74 | IndentWidth: 2 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: true 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: None 83 | ObjCBinPackProtocolList: Auto 84 | ObjCBlockIndentWidth: 2 85 | ObjCSpaceAfterProperty: false 86 | ObjCSpaceBeforeProtocolList: true 87 | PenaltyBreakAssignment: 2 88 | PenaltyBreakBeforeFirstCallParameter: 19 89 | PenaltyBreakComment: 300 90 | PenaltyBreakFirstLessLess: 120 91 | PenaltyBreakString: 1000 92 | PenaltyBreakTemplateDeclaration: 10 93 | PenaltyExcessCharacter: 1000000 94 | PenaltyReturnTypeOnItsOwnLine: 60 95 | PointerAlignment: Right 96 | ReflowComments: true 97 | SortIncludes: true 98 | SortUsingDeclarations: true 99 | SpaceAfterCStyleCast: false 100 | SpaceAfterTemplateKeyword: true 101 | SpaceBeforeAssignmentOperators: true 102 | SpaceBeforeCpp11BracedList: false 103 | SpaceBeforeCtorInitializerColon: true 104 | SpaceBeforeInheritanceColon: true 105 | SpaceBeforeParens: ControlStatements 106 | SpaceBeforeRangeBasedForLoopColon: true 107 | SpaceInEmptyParentheses: false 108 | SpacesBeforeTrailingComments: 1 109 | SpacesInAngles: false 110 | SpacesInContainerLiterals: true 111 | SpacesInCStyleCastParentheses: false 112 | SpacesInParentheses: false 113 | SpacesInSquareBrackets: false 114 | Standard: Cpp11 115 | StatementMacros: 116 | - Q_UNUSED 117 | - QT_REQUIRE_VERSION 118 | TabWidth: 8 119 | UseTab: Never 120 | ... 121 | 122 | -------------------------------------------------------------------------------- /.devcontainer.json: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/vscode-remote/devcontainer.json for format details. 2 | { 3 | "dockerFile": "Dockerfile", 4 | "extensions": [] 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | tmp* 4 | 9cc 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: minimal 2 | before_install: 3 | - docker build -t 9cc . 4 | services: 5 | - docker 6 | script: 7 | - make test 8 | 9 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "test", 8 | "type": "shell", 9 | "command": "make test", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /9cc.c: -------------------------------------------------------------------------------- 1 | #include "9cc.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // トークナイズした結果のトークン列はこの配列に保存する 9 | Function *code[100]; 10 | char *user_input; 11 | Map *ident_map; 12 | bool debug_flag; 13 | 14 | void error_at(char *loc, char *fmt, ...) { 15 | va_list ap; 16 | va_start(ap, fmt); 17 | int pos = loc - user_input; 18 | fprintf(stderr, "%s\n", user_input); 19 | fprintf(stderr, "%*s", pos, ""); // pos個の空白を出力 20 | fprintf(stderr, "^"); 21 | vfprintf(stderr, fmt, ap); 22 | fprintf(stderr, "\n"); 23 | exit(1); 24 | } 25 | 26 | void log_at(char *loc, char *msg) { 27 | int pos = loc - user_input; 28 | fprintf(stderr, "%s\n", user_input); 29 | fprintf(stderr, "%*s", pos, ""); // pos個の空白を出力 30 | fprintf(stderr, "^ %s\n", msg); 31 | } 32 | 33 | int is_alnum(char c) { 34 | return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 35 | ('0' <= c && c <= '9') || (c == '_'); 36 | } 37 | 38 | // 新しいトークンを作成してcurに繋げる 39 | Token *new_token(int ty, Token *cur, char *input) { 40 | Token *tok = calloc(1, sizeof(Token)); 41 | tok->ty = ty; 42 | tok->input = input; 43 | cur->next = tok; 44 | return tok; 45 | } 46 | 47 | Token *tokenize(char *p) { 48 | Token head; 49 | head.next = NULL; 50 | Token *cur = &head; 51 | 52 | while (*p) { 53 | if (isspace(*p)) { 54 | p++; 55 | continue; 56 | } 57 | if (strncmp(p, "return", 6) == 0 && !is_alnum(p[6])) { 58 | cur = new_token(TK_RETURN, cur, p); 59 | p += 6; 60 | continue; 61 | } 62 | if (strncmp(p, "else", 4) == 0 && !is_alnum(p[4])) { 63 | cur = new_token(TK_ELSE, cur, p); 64 | p += 4; 65 | continue; 66 | } 67 | if (strncmp(p, "while", 5) == 0 && !is_alnum(p[5])) { 68 | cur = new_token(TK_WHILE, cur, p); 69 | p += 5; 70 | continue; 71 | } 72 | if (strncmp(p, "for", 3) == 0 && !is_alnum(p[3])) { 73 | cur = new_token(TK_FOR, cur, p); 74 | p += 3; 75 | continue; 76 | } 77 | if (strncmp(p, "if", 2) == 0 && !is_alnum(p[2])) { 78 | cur = new_token(TK_IF, cur, p); 79 | p += 2; 80 | continue; 81 | } 82 | if (('a' <= *p && *p <= 'z') || ('A' <= *p && *p <= 'Z') || *p == '_') { 83 | int j = 0; 84 | // 変数が何文字続くか判定 85 | do { 86 | j++; 87 | } while (is_alnum(p[j])); 88 | cur = new_token(TK_IDENT, cur, p); 89 | cur->name = strndup(p, j); 90 | p += j; 91 | continue; 92 | } 93 | if (strncmp(p, "==", 2) == 0) { 94 | cur = new_token(TK_EQ, cur, p); 95 | p += 2; 96 | continue; 97 | } 98 | if (strncmp(p, "!=", 2) == 0) { 99 | cur = new_token(TK_NE, cur, p); 100 | p += 2; 101 | continue; 102 | } 103 | if (strncmp(p, "<=", 2) == 0) { 104 | cur = new_token(TK_LE, cur, p); 105 | p += 2; 106 | continue; 107 | } 108 | if (strncmp(p, ">=", 2) == 0) { 109 | cur = new_token(TK_GE, cur, p); 110 | p += 2; 111 | continue; 112 | } 113 | if (*p == '{') { 114 | cur = new_token(*p, cur, p); 115 | p++; 116 | continue; 117 | } 118 | if (*p == '}') { 119 | cur = new_token(*p, cur, p); 120 | p++; 121 | continue; 122 | } 123 | if (*p == '<') { 124 | cur = new_token(TK_LT, cur, p); 125 | p++; 126 | continue; 127 | } 128 | if (*p == '>') { 129 | cur = new_token(TK_GT, cur, p); 130 | p++; 131 | continue; 132 | } 133 | if (*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '(' || 134 | *p == ')' || *p == '=' || *p == ';' || *p == ',') { 135 | cur = new_token(*p, cur, p); 136 | p++; 137 | continue; 138 | } 139 | if (isdigit(*p)) { 140 | cur = new_token(TK_NUM, cur, p); 141 | cur->val = strtol(p, &p, 10); 142 | continue; 143 | } 144 | error_at(p, "トークナイズできません"); 145 | } 146 | cur = new_token(TK_EOF, cur, p); 147 | return head.next; 148 | } 149 | // エラーを報告するための関数 150 | __attribute__((noreturn)) void error(char *fmt, ...) { 151 | va_list ap; 152 | va_start(ap, fmt); 153 | vfprintf(stderr, fmt, ap); 154 | fprintf(stderr, "\n"); 155 | exit(1); 156 | } 157 | 158 | void logging(char *fmt, ...) { 159 | va_list ap; 160 | va_start(ap, fmt); 161 | vfprintf(stderr, fmt, ap); 162 | fprintf(stderr, "\n"); 163 | } 164 | void log_debug(char *fmt, ...) { 165 | if (!debug_flag) { 166 | return; 167 | } 168 | va_list ap; 169 | va_start(ap, fmt); 170 | vfprintf(stderr, fmt, ap); 171 | fprintf(stderr, "\n"); 172 | } 173 | 174 | int main(int argc, char **argv) { 175 | // テスト実行 176 | if (strcmp(argv[1], "-test") == 0) { 177 | runtest(); 178 | return 0; 179 | } 180 | // debug output 181 | debug_flag = false; 182 | if (strcmp(argv[1], "-debug") == 0) { 183 | debug_flag = true; 184 | user_input = argv[2]; 185 | } else { 186 | user_input = argv[1]; 187 | } 188 | ident_map = new_map(); 189 | log_debug("start tokenize"); 190 | token = tokenize(user_input); 191 | 192 | log_debug("start program"); 193 | program(); 194 | log_debug("start gen"); 195 | printf(".intel_syntax noprefix\n"); 196 | printf(".global main\n"); 197 | for (int i = 0; code[i]; i++) { 198 | gen_func(code[i]); 199 | } 200 | 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /9cc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #define _XOPEN_SOURCE 700 3 | // トークンの型を表す値 4 | enum { 5 | TK_NUM = 256, // 整数 6 | TK_IDENT, // 識別子 7 | TK_RETURN, // return 8 | TK_EOF, // 入力の終わりを表すトークン 9 | TK_EQ, // equal 10 | TK_NE, // not equal 11 | TK_LE, // less than or equal 12 | TK_GE, // greater than or equal 13 | TK_LT, // less than 14 | TK_GT, // greater than 15 | TK_IF, // if 16 | TK_ELSE, // else 17 | TK_WHILE, // while 18 | TK_FOR, // for 19 | }; 20 | 21 | typedef struct Token Token; 22 | // トークンの型 23 | struct Token { 24 | int ty; // トークンの型 25 | Token *next; 26 | int val; // tyがTK_NUMの時、その数値 27 | char *name; // tyがTK_IDENTの場合、その名前 28 | char *input; // トークン文字列(エラーメッセージ用) 29 | }; 30 | 31 | // current token 32 | Token *token; 33 | 34 | enum { 35 | ND_NUM = 256, 36 | ND_IDENT, 37 | ND_EQ, // equal 38 | ND_NE, // not equal 39 | ND_LE, // less than or equal 40 | ND_LT, // less than 41 | ND_RETURN, // return 42 | ND_IF, // if 43 | ND_IF_ELSE, // if - else 44 | ND_WHILE, // while 45 | ND_FOR, // for 46 | ND_BLOCK, // compound statement(block) 47 | ND_FUNC_CALL, // function call 48 | }; 49 | 50 | typedef struct { 51 | void **data; 52 | int capacity; 53 | int len; 54 | } Vector; 55 | 56 | typedef struct { 57 | Vector *keys; 58 | Vector *vals; 59 | } Map; 60 | 61 | typedef struct Node { // 宣言の中でNodeを使ってるのでタグ名Nodeが必要 62 | int ty; 63 | struct Node *lhs; 64 | struct Node *rhs; 65 | int val; // tyがND_NUMの場合のみ使う 66 | char *name; // tyがND_IDENTの場合のみ使う 67 | struct Node *cond; // tyがND_IF/ND_IF_ELSE/ND_WHILE/ND_IFの場合のみ使う 68 | struct Node *then; // tyがND_IF/ND_IF_ELSE/ND_WHILE/ND_FORの場合のみ使う 69 | struct Node *els; // tyがND_IF_ELSEの場合のみ使う 70 | struct Node *init; // tyがND_FORの場合のみ使う 71 | struct Node *iter_expr; // tyがND_FORの場合のみ使う 72 | Vector *statements; // tyがND_BLOCKの場合 73 | Vector *arguments; // tyがND_FUNC_CALLの場合 74 | } Node; 75 | 76 | typedef struct { 77 | char *name; 78 | Map *var_map; 79 | Vector *statements; 80 | } Function; 81 | 82 | // 名前をfunctionsに 83 | extern Function *code[]; 84 | extern Map *ident_map; 85 | extern bool debug_flag; 86 | 87 | void gen_func(); 88 | void program(); 89 | __attribute__((noreturn)) void error(char *fmt, ...); 90 | __attribute__((noreturn)) void error_at(char *loc, char *fmt, ...); 91 | void logging(char *fmt, ...); 92 | 93 | void runtest(); 94 | Vector *new_vector(); 95 | void vec_push(Vector *vec, void *elem); 96 | Map *new_map(); 97 | void map_put(Map *map, char *key, void *val); 98 | void *map_get(Map *map, char *key); 99 | int map_len(Map *map); -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:8 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | install: 3 | docker build -t 9cc . 4 | 5 | .PHONY: build 6 | build: 7 | docker run --rm -v $(shell pwd):/tmp/ 9cc bash -c "cd /tmp/; make -f docker.mk 9cc" 8 | 9 | .PHONY: test 10 | test: 11 | docker run --rm -v $(shell pwd):/tmp/ 9cc bash -c "cd /tmp/; make -f docker.mk test" 12 | 13 | .PHONY: test-verbose 14 | test-verbose: 15 | docker run --rm -v $(shell pwd):/tmp/ 9cc bash -c "cd /tmp/; make -f docker.mk test-verbose" 16 | 17 | .PHONY: test-debug 18 | test-debug: 19 | docker run --rm -v $(shell pwd):/tmp/ 9cc bash -c "cd /tmp/; make -f docker.mk test-debug" 20 | 21 | .PHONY: attach 22 | attach: 23 | docker run -t -i --rm -v $(shell pwd):/tmp/ 9cc bash 24 | 25 | .PHONY: clean 26 | clean: 27 | rm -f 9cc *.o *~ tmp* 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 9cc 2 | 3 | [![Build Status](https://travis-ci.org/dekokun/9cc.svg?branch=master)](https://travis-ci.org/dekokun/9cc) 4 | 5 | C言語のコンパイラです。 6 | [低レイヤを知りたい人のための Cコンパイラ作成入門](https://www.sigbus.info/compilerbook/) を参考に作成しています 7 | 8 | ## EBNF 9 | 10 | ```ebnf 11 | program = function* 12 | function = ident "(" ")" "{" stmt "}" 13 | stmt = expr ";" 14 | | "return" expr ";" 15 | | "if" "(" expr ")" stmt ("else" stmt)? 16 | | "while" "(" expr ")" stmt 17 | | "for" "(" expr? ";" expr? ";" expr? ")" stmt 18 | | "{" stmt* "}" 19 | expr = assign 20 | assign = equality ("=" assign)? 21 | equality = relational ("==" relational | "!=" relational)* 22 | relational = add ("<" add | "<=" add | ">" add | ">=" add)* 23 | add = mul ("+" mul | "-" mul)* 24 | mul = unary ("*" unary | "/" unary)* 25 | unary = ("+" | "-")? term 26 | term = num 27 | | ident 28 | | ident("(" arguments ")")? 29 | | "(" expr ")" 30 | arguments = ε | non_empty_arguments 31 | non_empty_arguments = term | non_empty_arguments 32 | ``` 33 | -------------------------------------------------------------------------------- /codegen.c: -------------------------------------------------------------------------------- 1 | #include "9cc.h" 2 | #include 3 | 4 | void gen(); 5 | int label_num = 0; 6 | 7 | void gen_lval(Node *node) { 8 | if (node->ty != ND_IDENT) { 9 | error("代入の左辺値が変数ではありません"); 10 | } 11 | printf(" mov rax, rbp\n"); 12 | printf(" sub rax, %d\n", (int)map_get(ident_map, node->name) * 8); 13 | printf(" push rax\n"); 14 | return; 15 | } 16 | 17 | void gen_func(Function *function) { 18 | printf("%s:\n", function->name); 19 | // プロローグ 20 | // 変数26個分の領域を確保する 21 | printf(" push rbp\n"); 22 | printf(" mov rbp, rsp\n"); 23 | printf(" sub rsp, %d\n", map_len(ident_map) * 8); 24 | for (int i = 0; i < function->statements->len; i++) { 25 | gen((Node *)function->statements->data[i]); 26 | // 式の評価結果としてスタックに一つの値が残っている 27 | // はずなので、スタックが溢れないようにポップしておく 28 | printf(" pop rax\n"); 29 | } 30 | // エピローグ 31 | // 最後の式の結果がRAXに残っているのでそれが返り値になる 32 | printf(" mov rsp, rbp\n"); 33 | printf(" pop rbp\n"); 34 | printf(" ret\n"); 35 | } 36 | void gen(Node *node) { 37 | if (node->ty == ND_BLOCK) { 38 | Vector *statements = node->statements; 39 | for (int i = 0; i < statements->len; i++) { 40 | Node *node = (Node *)statements->data[i]; 41 | gen(node); 42 | printf(" pop rax\n"); 43 | } 44 | return; 45 | } 46 | if (node->ty == ND_NUM) { 47 | printf(" push %d\n", node->val); 48 | return; 49 | } 50 | if (node->ty == ND_RETURN) { 51 | gen(node->lhs); 52 | printf(" pop rax\n"); 53 | printf(" mov rsp, rbp\n"); 54 | printf(" pop rbp\n"); 55 | printf(" ret\n"); 56 | return; 57 | } 58 | 59 | if (node->ty == ND_WHILE) { 60 | int _label_num = label_num; 61 | label_num += 1; 62 | printf(" .Lbegin%d:\n", _label_num); 63 | gen(node->cond); 64 | printf(" pop rax\n"); 65 | printf(" cmp rax, 0\n"); 66 | printf(" je .Lend%d\n", _label_num); 67 | gen(node->then); 68 | printf(" jmp .Lbegin%d\n", _label_num); 69 | printf(" .Lend%d:\n", _label_num); 70 | return; 71 | } 72 | 73 | if (node->ty == ND_FOR) { 74 | int _label_num = label_num; 75 | label_num += 1; 76 | if (node->init) { 77 | gen(node->init); 78 | } 79 | printf(" .Lforbegin%d:\n", _label_num); 80 | if (node->cond) { 81 | gen(node->cond); 82 | printf(" pop rax\n"); 83 | printf(" cmp rax, 0\n"); 84 | printf(" je .Lforend%d\n", _label_num); 85 | } 86 | gen(node->then); 87 | if (node->iter_expr) { 88 | gen(node->iter_expr); 89 | } 90 | printf(" jmp .Lforbegin%d\n", _label_num); 91 | if (node->cond) { 92 | printf(" .Lforend%d:\n", _label_num); 93 | } 94 | return; 95 | } 96 | 97 | if (node->ty == ND_IF) { 98 | int _label_num = label_num; 99 | label_num += 1; 100 | gen(node->cond); 101 | printf(" pop rax\n"); 102 | printf(" cmp rax, 0\n"); 103 | printf(" je .Lend%d\n", _label_num); 104 | gen(node->then); 105 | printf(" .Lend%d:\n", _label_num); 106 | return; 107 | } 108 | 109 | if (node->ty == ND_IF_ELSE) { 110 | int _label_num = label_num; 111 | label_num += 1; 112 | gen(node->cond); 113 | printf(" pop rax\n"); 114 | printf(" cmp rax, 0\n"); 115 | printf(" je .Lelse%d\n", _label_num); 116 | gen(node->then); 117 | printf(" jmp .Lend%d\n", _label_num); 118 | printf(" .Lelse%d:\n", _label_num); 119 | gen(node->els); 120 | printf(" .Lend%d:\n", _label_num); 121 | return; 122 | } 123 | 124 | if (node->ty == ND_FUNC_CALL) { 125 | Vector *arguments = node->arguments; 126 | // 第1引数~第6引数まで RDI, RSI, RDX, RCX, R8, R9 を順に使用 127 | char *registers[6] = {"RDI", "RSI", "RDX", "RCX", "R8", "R9"}; 128 | for (int i = 0; i < arguments->len; i++) { 129 | Node *node = (Node *)arguments->data[i]; 130 | gen(node); 131 | printf(" pop %s\n", registers[i]); 132 | } 133 | printf(" call %s\n", node->name); 134 | printf(" push rax\n"); 135 | return; 136 | } 137 | if (node->ty == ND_IDENT) { 138 | gen_lval(node); 139 | printf(" pop rax\n"); 140 | printf(" mov rax, [rax]\n"); 141 | printf(" push rax\n"); 142 | return; 143 | } 144 | if (node->ty == '=') { 145 | gen_lval(node->lhs); 146 | gen(node->rhs); 147 | printf(" pop rdi\n"); 148 | printf(" pop rax\n"); 149 | printf(" mov [rax], rdi\n"); 150 | printf(" push rdi\n"); 151 | return; 152 | } 153 | gen(node->lhs); 154 | gen(node->rhs); 155 | 156 | printf(" pop rdi\n"); 157 | printf(" pop rax\n"); 158 | 159 | switch (node->ty) { 160 | case '+': 161 | printf(" add rax, rdi\n"); 162 | break; 163 | case '-': 164 | printf(" sub rax, rdi\n"); 165 | break; 166 | case '*': 167 | printf(" imul rdi\n"); 168 | break; 169 | case '/': 170 | printf(" cqo\n"); 171 | printf(" idiv rdi\n"); 172 | break; 173 | case ND_EQ: 174 | printf(" cmp rax, rdi\n"); 175 | printf(" sete al\n"); 176 | printf(" movzb rax, al\n"); 177 | break; 178 | case ND_NE: 179 | printf(" cmp rax, rdi\n"); 180 | printf(" setne al\n"); 181 | printf(" movzb rax, al\n"); 182 | break; 183 | case ND_LE: 184 | printf(" cmp rax, rdi\n"); 185 | printf(" setle al\n"); 186 | printf(" movzb rax, al\n"); 187 | break; 188 | case ND_LT: 189 | printf(" cmp rax, rdi\n"); 190 | printf(" setl al\n"); 191 | printf(" movzb rax, al\n"); 192 | break; 193 | } 194 | 195 | printf(" push rax\n"); 196 | } 197 | -------------------------------------------------------------------------------- /docker.mk: -------------------------------------------------------------------------------- 1 | CFLAGS=-Wall -std=c11 2 | SRCS=$(wildcard *.c) 3 | OBJS=$(SRCS:.c=.o) 4 | TEST_SRCS=$(wildcard test/*.c) 5 | TEST_OBJS=$(TEST_SRCS:.c=.o) 6 | 7 | 9cc: $(OBJS) 8 | 9 | $(OBJS): 9cc.h 10 | 11 | .PHONY: test 12 | test: 9cc $(TEST_OBJS) 13 | ./9cc -test 14 | ./test.sh 15 | 16 | .PHONY: test-verbose 17 | test-verbose: 9cc 18 | ./9cc -test 19 | ./test.sh -v 20 | 21 | .PHONY: test-debug 22 | test-debug: 9cc 23 | ./9cc -test 24 | ./test.sh -v -debug 25 | 26 | .PHONY: clean 27 | clean: 28 | rm -f 9cc *.o *~ tmp* 29 | -------------------------------------------------------------------------------- /parse.c: -------------------------------------------------------------------------------- 1 | #include "9cc.h" 2 | #include 3 | #include 4 | #include 5 | 6 | Function *function(); 7 | Node *stmt(); 8 | Node *expr(); 9 | Node *equality(); 10 | Node *add(); 11 | Node *relational(); 12 | Node *unary(); 13 | Node *mul(); 14 | Node *term(); 15 | Vector *non_empty_arguments(); 16 | Vector *arguments(); 17 | Node *assign(); 18 | Node *new_node_ident(char *name); 19 | Node *new_node_num(int val); 20 | Node *new_node(int op, Node *lhs, Node *rhs); 21 | 22 | int consume(int ty) { 23 | if (token->ty != ty) 24 | return 0; 25 | token = token->next; 26 | return 1; 27 | } 28 | 29 | int consume_number() { 30 | if (token->ty != TK_NUM) 31 | return -1; 32 | int val = token->val; 33 | token = token->next; 34 | return val; 35 | } 36 | 37 | char *consume_ident() { 38 | if (token->ty != TK_IDENT) 39 | return 0; 40 | char *name = token->name; 41 | token = token->next; 42 | return name; 43 | } 44 | 45 | void expect(char ty) { 46 | if (token->ty != ty) 47 | error_at(token->input, "'%c'ではありません", ty); 48 | token = token->next; 49 | } 50 | 51 | char *expect_ident() { 52 | if (token->ty != TK_IDENT) 53 | error_at(token->input, "TK_IDENTではありません"); 54 | char *name = token->name; 55 | token = token->next; 56 | return name; 57 | } 58 | 59 | bool at_eof() { return token->ty == TK_EOF; } 60 | 61 | void program() { 62 | int i = 0; 63 | while (!at_eof()) { 64 | code[i++] = function(); 65 | } 66 | code[i] = NULL; 67 | } 68 | 69 | Function *function() { 70 | Function *function; 71 | function = malloc(sizeof(Function)); 72 | function->name = expect_ident(); 73 | 74 | expect('('); 75 | // まずは引数なし関数定義のみ 76 | expect(')'); 77 | expect('{'); 78 | Vector *stmts = new_vector(); 79 | while (!consume('}')) { 80 | vec_push(stmts, (void *)stmt()); 81 | } 82 | function->statements = stmts; 83 | return function; 84 | } 85 | 86 | Node *stmt() { 87 | Node *node; 88 | if (consume('{')) { 89 | Vector *vec = new_vector(); 90 | while (!consume('}')) { 91 | vec_push(vec, (void *)stmt()); 92 | } 93 | node = malloc(sizeof(Node)); 94 | node->ty = ND_BLOCK; 95 | node->statements = vec; 96 | return node; 97 | } 98 | if (consume(TK_FOR)) { 99 | node = malloc(sizeof(Node)); 100 | node->ty = ND_FOR; 101 | expect('('); 102 | char first_colon = ';'; 103 | char second_colon = ';'; 104 | if (!consume(first_colon)) { 105 | node->init = expr(); 106 | expect(first_colon); 107 | } else { 108 | node->init = NULL; 109 | } 110 | if (!consume(second_colon)) { 111 | node->cond = expr(); 112 | expect(second_colon); 113 | } else { 114 | node->cond = NULL; 115 | } 116 | if (!consume(')')) { 117 | node->iter_expr = expr(); 118 | expect(')'); 119 | } else { 120 | node->iter_expr = NULL; 121 | } 122 | node->then = stmt(); 123 | return node; 124 | } 125 | if (consume(TK_WHILE)) { 126 | node = malloc(sizeof(Node)); 127 | node->ty = ND_WHILE; 128 | expect('('); 129 | node->cond = expr(); 130 | expect(')'); 131 | node->then = stmt(); 132 | return node; 133 | } 134 | if (consume(TK_IF)) { 135 | expect('('); 136 | Node *if_expr = expr(); 137 | expect(')'); 138 | Node *if_stmt = stmt(); 139 | node = malloc(sizeof(Node)); 140 | node->cond = if_expr; 141 | node->then = if_stmt; 142 | if (consume(TK_ELSE)) { 143 | node->ty = ND_IF_ELSE; 144 | node->els = stmt(); 145 | } else { 146 | node->ty = ND_IF; 147 | } 148 | return node; 149 | } 150 | if (consume(TK_RETURN)) { 151 | node = malloc(sizeof(Node)); 152 | node->ty = ND_RETURN; 153 | node->lhs = expr(); 154 | } else { 155 | node = expr(); 156 | } 157 | expect(';'); 158 | return node; 159 | } 160 | Node *expr() { return assign(); } 161 | 162 | Node *assign() { 163 | Node *lhs = equality(); 164 | if (consume('=')) { 165 | return new_node('=', lhs, assign()); 166 | } 167 | return lhs; 168 | } 169 | 170 | Node *equality() { 171 | Node *lhs = relational(); 172 | if (consume(TK_EQ)) { 173 | return new_node(ND_EQ, lhs, relational()); 174 | } 175 | if (consume(TK_NE)) { 176 | return new_node(ND_NE, lhs, relational()); 177 | } 178 | return lhs; 179 | } 180 | Node *relational() { 181 | Node *lhs = add(); 182 | if (consume(TK_LT)) { 183 | return new_node(ND_LT, lhs, add()); 184 | } 185 | if (consume(TK_GT)) { 186 | return new_node(ND_LT, add(), lhs); 187 | } 188 | if (consume(TK_LE)) { 189 | return new_node(ND_LE, lhs, add()); 190 | } 191 | if (consume(TK_GE)) { 192 | return new_node(ND_LE, add(), lhs); 193 | } 194 | return lhs; 195 | } 196 | Node *add() { 197 | Node *lhs = mul(); 198 | if (consume('+')) { 199 | return new_node('+', lhs, expr()); 200 | } 201 | if (consume('-')) { 202 | return new_node('-', lhs, expr()); 203 | } 204 | return lhs; 205 | } 206 | 207 | Node *mul() { 208 | Node *lhs = unary(); 209 | if (consume('*')) { 210 | return new_node('*', lhs, mul()); 211 | } 212 | if (consume('/')) { 213 | return new_node('/', lhs, mul()); 214 | } 215 | return lhs; 216 | } 217 | 218 | Node *unary() { 219 | if (consume('+')) { 220 | return term(); 221 | } 222 | if (consume('-')) { 223 | return new_node('-', new_node_num(0), term()); 224 | } 225 | return term(); 226 | } 227 | 228 | Node *term() { 229 | int number = consume_number(); 230 | if (number != -1) 231 | return new_node_num(number); 232 | char *name = consume_ident(); 233 | if (name) { 234 | if (!consume('(')) { 235 | // 変数 236 | return new_node_ident(name); 237 | } 238 | // 関数呼び出し 239 | Vector *args = arguments(); 240 | Node *node = malloc(sizeof(Node)); 241 | node->ty = ND_FUNC_CALL; 242 | node->name = name; 243 | node->arguments = args; 244 | return node; 245 | } 246 | expect('('); 247 | Node *node = expr(); 248 | expect(')'); 249 | return node; 250 | } 251 | 252 | Vector *arguments() { 253 | if (consume(')')) { 254 | return new_vector(); 255 | } 256 | Vector *result = non_empty_arguments(); 257 | expect(')'); 258 | return result; 259 | } 260 | Vector *non_empty_arguments() { 261 | Vector *args = new_vector(); 262 | vec_push(args, (void *)term()); 263 | if (consume(',')) { 264 | Vector *after_args = non_empty_arguments(); 265 | for (int i = 0; i < after_args->len; i++) { 266 | vec_push(args, after_args->data[i]); 267 | } 268 | } 269 | return args; 270 | } 271 | 272 | Node *new_node(int op, Node *lhs, Node *rhs) { 273 | Node *node = malloc(sizeof(Node)); 274 | node->ty = op; 275 | node->lhs = lhs; 276 | node->rhs = rhs; 277 | return node; 278 | } 279 | 280 | Node *new_node_num(int val) { 281 | Node *node = malloc(sizeof(Node)); 282 | node->ty = ND_NUM; 283 | node->val = val; 284 | return node; 285 | } 286 | 287 | void node_debug(Node *node) { 288 | printf("ty: %d\n", node->ty); 289 | printf("lhs: %s\n", node->lhs == NULL ? "NULL" : "lhs node"); 290 | printf("rhs: %s\n", node->rhs == NULL ? "NULL" : "rhs node"); 291 | printf("val: %d\n", node->val); 292 | // printf("name: %c\n", node->name); 293 | } 294 | 295 | Node *new_node_ident(char *name) { 296 | if (map_get(ident_map, name) == NULL) { 297 | map_put(ident_map, name, (void *)(map_len(ident_map))); 298 | } 299 | Node *node = malloc(sizeof(Node)); 300 | node->ty = ND_IDENT; 301 | node->name = name; 302 | return node; 303 | } 304 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | verbose= 4 | debug= 5 | 6 | while [[ $# -gt 0 ]] 7 | do 8 | case "$1" in 9 | -v | --v | --ve | --ver | --verb | --verbo | --verbos | --verbose) 10 | verbose=1 11 | shift 12 | ;; 13 | -d | --d | --debug | -debug) 14 | debug=1 15 | shift 16 | ;; 17 | esac 18 | done 19 | 20 | 21 | RED='\033[0;31m' 22 | GREEN='\033[0;32m' 23 | NC='\033[0m' 24 | try() { 25 | expected="$1" 26 | input="$2" 27 | if [ "$debug" = 1 ]; then 28 | ./9cc -debug "$input" >tmp.s 29 | else 30 | ./9cc "$input" >tmp.s 31 | fi 32 | compile_result="$?" 33 | if [ $compile_result != 0 ]; then 34 | echo -e "${RED}compile failed{$NC}" 35 | exit 36 | fi 37 | gcc -static -o tmp tmp.s test/*.o 38 | ./tmp 39 | actual="$?" 40 | 41 | if [ "$actual" == "$expected" ]; then 42 | echo -e "$GREEN$input => $actual$NC" 43 | else 44 | echo -e "$RED$input => $expected expected, but got $actual$NC" 45 | exit 1 46 | fi 47 | } 48 | 49 | try_fail() { 50 | input="$1" 51 | 52 | output=$(./9cc "$input" 2>&1) 53 | result="$?" 54 | 55 | if [ "$result" == "0" ]; then 56 | echo -e "${RED}not failed: $input.$NC" 57 | if [ "$verbose" = 1 ]; then 58 | echo output: 59 | echo "$output" 60 | fi 61 | exit 1 62 | else 63 | echo -e "$GREEN$input failed as expected.$NC" 64 | if [ "$verbose" = 1 ]; then 65 | echo output: 66 | echo "$output" 67 | fi 68 | fi 69 | } 70 | 71 | try 6 'foo() {a = 1; b = 5; return a + b;} main() {return foo();}' 72 | try 5 'foo() {return 2;} main() {return foo() + foo() + 1;}' 73 | try 6 'main() {return 6;}' 74 | try 7 'main() {a = foo_1(2); b = foo_2(2, 3); return a + b;}' 75 | try 2 'main() {return foo_1(2);}' 76 | try 5 'main() {return foo_2(2, 3);}' 77 | try 8 'main() {return foo_3(2, 3, 3);}' 78 | try 12 'main() {return foo_4(2, 3, 3, 4);}' 79 | try 17 'main() {return foo_5(2, 3, 3, 4, 5);}' 80 | try 18 'main() {return foo_6(2, 3, 3, 4, 5, 1);}' 81 | try 1 'main() {return foo();}' 82 | try 28 'main() {a1 = 1;a2 = 1;a3 = 1;a4 = 1;a5 = 1;a6 = 1;a7 = 1;a8 = 1;a9 = 1;a10 = 1;a11 = 1;a12 = 1;a13 = 1;a14 = 1;a15 = 1;a16 = 1;a17 = 1;a18 = 1;a19 = 1;a20 = 1;a21 = 1;a22 = 1;a23 = 1;a24 = 1;a25 = 1;a26 = 1;a27 = 1;a28 = 1;return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a20 + a21 + a22 + a23 + a24 + a25 + a26 + a27 + a28;}' 83 | try 5 'main() {{return 5;}}' 84 | try 5 'main() {{a= 5;return a;}}' 85 | try 3 'main() {a = 1; if (a > 5) {return 1;} else {a = a + 1; return a + 1;}}' 86 | try 5 'main() {for (i = 0; i < 5; i = i + 1) i;return i;}' 87 | try 5 'main() {i = 0; for (;i < 5;) i = i + 1;return i;}' 88 | try 6 'main() {for (i = 0; ;i = i + 1) if (i > 5) return i;}' 89 | try 6 'main() {for (i = 0; ;) if ((i = i + 1) > 5) return i;}' 90 | try 4 'main() {a = 0; while (a <= 3) a = a + 1;return a;}' 91 | try 3 'main() {a = 2; if (1 == 1) a = 3; else a = 4; return a;}' 92 | try 4 'main() {a = 2; if (1 != 1) a = 3; else a = 4; return a;}' 93 | try 4 'main() {a = 2; if (1 == 1) if (1 == 1) a = 4; return a;}' 94 | try 3 'main() {a = 2; if (1 == 1) a = 3; if (1 != 1) a = 4; return a;}' 95 | try 3 'main() {a = 2; if (1 == 1) a = 3; return a;}' 96 | try 2 'main() {a = 2; if (1 != 1) a = 3; return a;}' 97 | try 0 'main() {_foo = 0; return _foo;}' 98 | try 6 'main() {foo = 1; bar = 2 + 3; return foo + bar;}' 99 | try 6 'main() {foo = 1; far = 2 + 3; return foo + far;}' 100 | ## try 0 'return foo + bar};' 101 | try 2 'main() {a = 2; b = 3; return a;}' 102 | try 1 'main() {a = -1; b = -1; return a + b + 3;}' 103 | try 19 'main() {a = 8; b = 5 * 6 - 8; return a + b / 2;}' 104 | try 5 'main() {return 5;return 6;}' 105 | try 42 'main() {42;}' 106 | try 41 'main() { 12 + 34 - 5 ;}' 107 | try 52 'main() { 2 + 10 * 5 ;}' 108 | try 3 'main() { 1 + 10 /5 ;}' 109 | try 56 'main() { 2*(3 +(2+3) *5);}' 110 | try 4 'main() {a = b = 2;a + b;}' 111 | try 0 'main() {-42 + 42;}' 112 | try 10 'main() {-10 * 5 + 60;}' 113 | try 1 'main() {5 == 5;}' 114 | try 0 'main() {5 != 5;}' 115 | try 1 'main() {3 <= 5;}' 116 | try 0 'main() {5 <= 3;}' 117 | try 1 'main() {5 >= 3;}' 118 | try 0 'main() {3 >= 5;}' 119 | try 1 'main() {3 >= 3;}' 120 | try 1 'main() {3 <= 3;}' 121 | try 0 'main() {3 < 3;}' 122 | try 1 'main() {2 < 3;}' 123 | try 0 'main() {3 > 3;}' 124 | try 1 'main() {3 > 2;}' 125 | 126 | try_fail 'main() {();}' 127 | try_fail 'main() {if hoge;}' 128 | try_fail 'main() {if (hoge;}' 129 | try_fail 'main() {1 + (2 + );}' 130 | try_fail 'main() {1 1;}' 131 | try_fail 'main() {1 = 1;}' 132 | try_fail 'main() {(a + 1) = 1;}' 133 | try_fail 'main() {return foo_2(2, );}' 134 | 135 | echo OK 136 | -------------------------------------------------------------------------------- /test/func_call_multi_arg.c: -------------------------------------------------------------------------------- 1 | int foo_1(int a) { 2 | return a; 3 | } 4 | int foo_2(int a, int b) { 5 | return a + b; 6 | } 7 | int foo_3(int a, int b, int c) { 8 | return a + b + c; 9 | } 10 | int foo_4(int a, int b, int c, int d) { 11 | return a + b + c + d; 12 | } 13 | int foo_5(int a, int b, int c, int d, int e) { 14 | return a + b + c + d + e; 15 | } 16 | int foo_6(int a, int b, int c, int d, int e, int f) { 17 | return a + b + c + d + e + f; 18 | } 19 | -------------------------------------------------------------------------------- /test/func_call_test.c: -------------------------------------------------------------------------------- 1 | int foo() { 2 | return 1; 3 | } 4 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include "9cc.h" 2 | #include 3 | #include 4 | #include 5 | 6 | Vector *new_vector() { 7 | Vector *vec = malloc(sizeof(Vector)); 8 | int first_size = 16; 9 | vec->data = malloc(sizeof(void *) * first_size); 10 | vec->capacity = first_size; 11 | vec->len = 0; 12 | return vec; 13 | } 14 | 15 | void vec_push(Vector *vec, void *elem) { 16 | if (vec->capacity == vec->len) { 17 | vec->capacity *= 2; 18 | vec->data = realloc(vec->data, sizeof(void *) * vec->capacity); 19 | } 20 | vec->data[vec->len++] = elem; 21 | } 22 | 23 | Map *new_map() { 24 | Map *map = malloc(sizeof(Map)); 25 | map->keys = new_vector(); 26 | map->vals = new_vector(); 27 | return map; 28 | } 29 | 30 | void map_put(Map *map, char *key, void *val) { 31 | vec_push(map->keys, key); 32 | vec_push(map->vals, val); 33 | } 34 | 35 | void *map_get(Map *map, char *key) { 36 | for (int i = map->keys->len - 1; i >= 0; i--) 37 | if (strcmp(map->keys->data[i], key) == 0) 38 | return map->vals->data[i]; 39 | return NULL; 40 | } 41 | 42 | int map_len(Map *map) { return map->keys->len; } -------------------------------------------------------------------------------- /util_test.c: -------------------------------------------------------------------------------- 1 | #include "9cc.h" 2 | #include 3 | #include 4 | 5 | void expect_check(int line, int expected, int actual) { 6 | if (expected == actual) 7 | return; 8 | fprintf(stderr, "%d: %d expected, but got %d\n", line, expected, actual); 9 | exit(1); 10 | } 11 | 12 | void test_vector() { 13 | Vector *vec = new_vector(); 14 | expect_check(__LINE__, 0, vec->len); 15 | 16 | for (int i = 0; i < 100; i++) 17 | vec_push(vec, (void *)i); 18 | 19 | expect_check(__LINE__, 100, vec->len); 20 | expect_check(__LINE__, 0, (int)vec->data[0]); 21 | expect_check(__LINE__, 50, (int)vec->data[50]); 22 | expect_check(__LINE__, 99, (int)vec->data[99]); 23 | } 24 | 25 | void test_map() { 26 | Map *map = new_map(); 27 | expect_check(__LINE__, 0, (int)map_get(map, "foo")); 28 | expect_check(__LINE__, 0, map_len(map)); 29 | 30 | map_put(map, "foo", (void *)2); 31 | expect_check(__LINE__, 2, (int)map_get(map, "foo")); 32 | expect_check(__LINE__, 1, map_len(map)); 33 | 34 | map_put(map, "bar", (void *)4); 35 | expect_check(__LINE__, 4, (int)map_get(map, "bar")); 36 | expect_check(__LINE__, 2, map_len(map)); 37 | 38 | map_put(map, "foo", (void *)6); 39 | expect_check(__LINE__, 6, (int)map_get(map, "foo")); 40 | expect_check(__LINE__, 3, map_len(map)); 41 | } 42 | 43 | void runtest() { 44 | test_vector(); 45 | test_map(); 46 | printf("OK\n"); 47 | } 48 | --------------------------------------------------------------------------------