Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@maehrm
Created November 8, 2018 11:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maehrm/79aa3ed8374cdf324e7888ffdcfdeb4a to your computer and use it in GitHub Desktop.
Save maehrm/79aa3ed8374cdf324e7888ffdcfdeb4a to your computer and use it in GitHub Desktop.
低レイヤを知りたい人のための Cコンパイラ作成入門(ステップ4:四則演算のできる言語の作成) https://www.sigbus.info/compilerbook/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* トークンの型を表す値 */
enum {
TK_NUM = 256, /* 整数トークン */
TK_EOF, /* 入力の終わりを表すトークン */
};
/* トークンの型 */
typedef struct {
int ty; /* トークンの型 */
int val; /* tyがTK_NUMの場合、その数値 */
char *input; /* トークン文字列(エラーメッセージ用) */
} Token;
enum {
ND_NUM = 256, /* 整数ノードの型 */
};
typedef struct Node {
int op; /* 演算子かND_NUM */
struct Node *lhs; /* 左辺 */
struct Node *rhs; /* 右辺 */
int val; /* tyがND_NUMの場合のみ使う */
} Node;
Node *new_node(int op, Node *lhs, Node *rhs);
Node *new_node_num(int val);
Node *expr();
Node *mul();
Node *term();
void gen(Node *node);
void tokenize(char *p);
void error(int i);
/* トークナイズした結果のトークン列はこの配列に保存する */
/* 100個以上のトークンは来ないものとする */
Token tokens[100];
int pos;
Node *new_node(int op, Node *lhs, Node *rhs) {
Node *node = malloc(sizeof(Node));
node->op = op;
node->lhs = lhs;
node->rhs = rhs;
return node;
}
Node *new_node_num(int val) {
Node *node = malloc(sizeof(Node));
node->op = ND_NUM;
node->val = val;
return node;
}
Node *expr() {
Node *lhs = mul();
if (tokens[pos].ty == TK_EOF)
return lhs;
if (tokens[pos].ty == '+') {
pos++;
return new_node('+', lhs, expr());
}
if (tokens[pos].ty == '-') {
pos++;
return new_node('-', lhs, expr());
}
return lhs;
//error(pos);
}
Node *mul() {
Node *lhs = term();
if (tokens[pos].ty == TK_EOF)
return lhs;
if (tokens[pos].ty == '*') {
pos++;
return new_node('*', lhs, mul());
}
if (tokens[pos].ty == '/') {
pos++;
return new_node('/', lhs, mul());
}
return lhs;
//error(pos);
}
Node *term() {
if (tokens[pos].ty == TK_NUM)
return new_node_num(tokens[pos++].val);
if (tokens[pos].ty == '(') {
pos++;
Node *node = expr();
if (tokens[pos].ty != ')')
error(pos);
pos++;
return node;
}
error(pos);
}
void gen(Node *node) {
if (node->op == ND_NUM) {
printf(" push %d\n", node->val);
return;
}
gen(node->lhs);
gen(node->rhs);
printf(" pop rdi\n");
printf(" pop rax\n");
switch (node->op) {
case '+':
printf(" add rax, rdi\n");
break;
case '-':
printf(" sub rax, rdi\n");
break;
case '*':
printf(" mul rdi\n");
break;
case '/':
printf(" mov rdx, 0\n");
printf(" div rdi\n");
break;
}
printf(" push rax\n");
}
/* pが指している文字列をトークンに分割してtokensに保存する */
void tokenize(char *p) {
int i = 0;
while (*p) {
/* 空白をスキップ */
if (isspace(*p)) {
p++;
continue;
}
if (*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '(' || *p == ')') {
tokens[i].ty = *p;
tokens[i].input = p;
i++;
p++;
continue;
}
if (isdigit(*p)) {
tokens[i].ty = TK_NUM;
tokens[i].input = p;
tokens[i].val = strtol(p, &p, 10);
i++;
continue;
}
fprintf(stderr, "トークナイズできません: %s\n", p);
exit(1);
}
tokens[i].ty = TK_EOF;
tokens[i].input = p;
}
/* エラーを報告するための関数 */
void error(int i) {
fprintf(stderr, "予期せぬトークンです: %s\n",
tokens[i].input);
exit(1);
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "引数の個数が正しくありません\n");
return 1;
}
/* トークナイズしてパースする */
tokenize(argv[1]);
Node *node = expr();
/* アセンブリの前半部分を出力 */
printf(".intel_syntax noprefix\n");
printf(".global main\n");
printf("main:\n");
/* 抽象構文木を下りながらコード生成 */
gen(node);
/* スタックトップに式全体の値が残っているはずなので */
/* それをRAXにロードして関数からの返り値とする */
printf(" pop rax\n");
printf(" ret\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment