[Advance C Programming] Как написать свой собственный компилятор
- Выберите язык:
Определите язык высокого уровня, который вы хотите скомпилировать. Это определит синтаксис и особенности языка, с которым вы будете работать.
Для этого примера выберем простой вымышленный язык под названием «Простой». Синтаксис Simple похож на C и имеет несколько основных конструкций, таких как переменные, операторы if-else и циклы.
- Напишите код лексера:
Первым шагом в написании компилятора является написание лексера, который представляет собой программу, которая разбивает исходный код на поток токенов, таких как ключевые слова, символы и идентификаторы.
Вот пример лексера для Simple:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
enum TokenType {
T_INT,
T_FLOAT,
T_PLUS,
T_MINUS,
T_MUL,
T_DIV,
T_LPAREN,
T_RPAREN,
T_ID,
T_IF,
T_ELSE,
T_WHILE,
T_EOL
};
struct Token {
enum TokenType type;
char value[100];
};
int is_keyword(char *value)
{
if (strcmp(value, "if") == 0) {
return T_IF;
} else if (strcmp(value, "else") == 0) {
return T_ELSE;
} else if (strcmp(value, "while") == 0) {
return T_WHILE;
}
return T_ID;
}
int get_token(struct Token *token)
{
int i = 0;
char c;
while (isspace(c = getchar()))
;
if (isdigit(c)) {
while (isdigit(c)) {
token->value[i++] = c;
c = getchar();
}
ungetc(c, stdin);
token->value[i] = '\0';
token->type = T_INT;
} else if (c == '+') {
token->value[0] = '+';
token->value[1] = '\0';
token->type = T_PLUS;
} else if (c == '-') {
token->value[0] = '-';
token->value[1] = '\0';
token->type = T_MINUS;
} else if (c == '*') {
token->value[0] = '*';
token->value[1] = '\0';
token->type = T_MUL;
} else if (c == '/') {
token->value[0] = '/';
token->value[1] = '\0';
token->type = T_DIV;
} else if (c == '(') {
token->value[0] = '(';
token->value[1] = '\0';
token->type = T_LPAREN;
} else if (c == ')') {
token->value[0] = ')';
token->value[1] = '\0';
token->type = T_RPAREN;
}
Note : this will turn into a long code so only a short snippet is given
- Написать код парсера:
Следующим шагом является написание синтаксического анализатора, который берет поток токенов из лексера и создает синтаксическое дерево, представляющее структуру программы.
Вот пример парсера для Simple:
#include <stdio.h>
#include <string.h>
struct Token {
enum TokenType type;
char value[100];
};
struct Node {
int type;
struct Node *left;
struct Node *right;
int value;
};
struct Node *expression();
struct Node *term();
struct Node *factor();
int get_token(struct Token *token);
struct Node *expression()
{
struct Node *node = term();
struct Token token;
while (get_token(&token) == T_PLUS || get_token(&token) == T_MINUS) {
struct Node *new_node = (struct Node *)malloc(sizeof(struct Node));
new_node->type = get_token(&token);
new_node->left = node;
new_node->right = term();
node = new_node;
}
return node;
}
struct Node *term()
{
struct Node *node = factor();
struct Token token;
while (get_token(&token) == T_MUL || get_token(&token) == T_DIV) {
struct Node *new_node = (struct Node *)malloc(sizeof(struct Node));
new_node->type = get_token(&token);
new_node->left = node;
new_node->right = factor();
node = new_node;
}
return node;
}
struct Node *factor()
{
struct Token token;
get_token(&token);
if (token.type == T_INT) {
struct Node *node = (struct Node *)malloc(sizeof(struct Node));
node->type = T_INT;
node->value = atoi(token.value);
return node;
} else if (token.type == T_LPAREN) {
struct Node *node = expression();
get_token(&token);
return node;
}
return 0;
}
- Генерация кодов: Генератор кода берет синтаксическое дерево из синтаксического анализатора и генерирует машинный код, реализующий программу.
генерация кода для Simple:
void generate_code(struct Node *node)
{
if (node->type == T_INT) {
printf(" push %d\n", node->value);
} else if (node->type == T_PLUS) {
generate_code(node->left);
generate_code(node->right);
printf(" pop rbx\n");
printf(" pop rax\n");
printf(" add rax, rbx\n");
printf(" push rax\n");
} else if (node->type == T_MINUS) {
generate_code(node->left);
generate_code(node->right);
printf(" pop rbx\n");
printf(" pop rax\n");
printf(" sub rax, rbx\n");
printf(" push rax\n");
}
- Объединить все :
Вам потребуется интегрировать лексер, синтаксический анализатор и генератор кода в единую программу, которая считывает исходный код, генерирует машинный код и выводит исполняемый файл.
Если вы только начинаете, вы можете начать с небольшого проекта, который фокусируется на определенном аспекте конструкции компилятора, таком как лексер или синтаксический анализатор, прежде чем пытаться написать полный компилятор.
Или, если вы только начинаете изучать какие-либо языки компьютерного программирования, следуйте основной серии C в моих предыдущих постах.