#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// token type 構造体
typedef enum {
TK_RESERVED, // op
TK_NUM,
TK_EOF, // end of input
} TokenKind;
typedef struct Token Token;
// token
struct Token {
TokenKind kind; // token type
Token *next; // next token
int val; // TK_NUM value
char *str; // token char
};
// now focus token
Token *token;
void error(char *fmt, ...){
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
exit(1);
}
// if the next token is expected symbol, read forward one token and
// return true.
bool consume(char op) {
if(token->kind != TK_RESERVED | token->str[0] != op)
return false;
token = token->next;
return true;
}
// if the next token is expected symbol, read forward one token and
// otherwise report an error.
void expect(char op) {
if(token->kind != TK_RESERVED | token->str[0] != op)
error("'%c'ではありません。", op);
token = token->next;
}
// if the next token is expected number, read forward one token and
// otherwise report an error.
int expect_number() {
if(token->kind != TK_NUM)
error("数ではありません");
int val = token->val;
token = token->next;
return val;
}
bool at_eof() {
return token->kind == TK_EOF;
}
// create a new token and connect it to curl.
Token *new_token(TokenKind kind, Token *cur, char *str){
Token *tok = calloc(1, sizeof(token));
tok->kind = kind;
tok->str = str;
cur->next = tok;
return tok;
}
// Tokenize the input string p and return it
Token *tokenize(char *p) {
Token head;
head.next = NULL;
Token *cur = &head;
while (*p) {
// skip space
if(isspace(*p)){
p++;
continue;
}
if(*p == '+' || *p == '-') {
cur = new_token(TK_RESERVED, cur, p++);
continue;
}
if(isdigit(*p)) {
cur = new_token(TK_NUM, cur, p);
cur->val = strtol(p, &p, 10);
continue;
}
error("トークナイズできません");
}
new_token(TK_EOF, cur, p);
return head.next;
}
int main(int argc, char **argv){
if(argc != 2) {
fprintf(stderr, "引数の個数が正しくありません\n");
return 1;
}
token = tokenize(argv[1]);
printf(".intel_syntax noprefix\n");
printf(".global main\n");
printf("main:\n");
printf(" mov rax, %ld\n", expect_number());
while(!at_eof()){
if(consume('+')){
printf(" add rax, %d\n", expect_number());
}
expect('-');
printf(" sub rax, %d\n", expect_number());
}
printf(" ret\n");
return 0;
}
コンパイラはまず構文解析を行なってトークン列を抽象構文木に変換し、その構文木をアセンブリに変換
### 再帰下降構文解析
生成規則による演算子の優先順位
expr = mul (“+” mul | “-” mul)*
mul = num (“*” num | “/” num)*
// 足し算よりも掛け算が常に下に現れる
expr = mul (“+” mul | “-” mul)*
mul = primary (“*” primary | “/” primary)*
primary = num | “(” expr “)”