文字列をトークン列に分割することをトークナイズするという
#include <ctype.h> // 文字の種類の判定や文字の変換 #include <stdarg.h> // 可変長引数 #include <stdbool.h> // bool, true, false #include <stdio.h> #include <stdlib.h> // strtol #include <string.h> typedef enum { TK_RESERVED, // 記号 TK_NUM, // 整数トークン TK_EOF // 入力の終わりを表すトークン } TokenKind; typedef struct Token Token; struck Token { TokenKind kind; // トークンの型 Token *next; // 次の入力トークン int val; // kindがTK_NUMの場合、その数値 char *str; // トークン文字列 }; // 現在着目しているトークン Token *token; // エラーを報告する為の関数 // printfと同じ引数 void error(char *fmt, ...){ va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); exit(1); } // 次のトークが期待している記号の時には、トークンを1つ読み進めて真を返す、それ以外の場合には偽を返す bool consume(char op){ if(token->kind != TK_RESERVED || token->str[0] != op) return false; token = token->next; return true; } // 次のトークが期待している記号の時には、トークンを1つ読み進めて真を返す、それ以外の場合にはエラーを返す void expect(char op){ if(token->kind != TK_RESERVED || token->str[0] != op) error("'%c'ではありません", op); token = token->next; } // 次のトークが数字の場合、トークンを1つ読み進めて真を返す、それ以外の場合にはエラーを返す int expect_number(){ if(token->kind != TK_NUM) error("数ではありません"); int val = token->val; token = token->ext; return val; } bool at_eof(){ return token->kind == TK_EOF; } // 新しいトークンを作成してcurに繋げる Token *new_token(TokenKind, Token *cur, char *str){ Token *tok = calloc(1, sizeof(Token)); tok->kind = kind; tok->str = str; cur->next = tok; return tok; } // 入力文字列pをトークナイズしてそれを返す Token *tokenize(char *p){ Token head; head.next = NULL; Token *cur = &head; while(*p){ 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()); // ldはlong d, strtolは文字列をlongに変換 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; }
新しいプログラミング言語を作る イコール コンパイラを作る ってことなのか。
低レイヤは学習コストが高いけど、一生物の知識がつくな。