1. Выберите язык: выберите язык высокого уровня, который вы хотите скомпилировать. Это определит синтаксис и особенности языка, с которым вы будете работать.

Для этого примера давайте выберем простой вымышленный язык под названием «Простой». Синтаксис 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 (значение, «иначе») == 0) {
return T_ELSE;
} else if (strcmp(значение, «пока») == 0) {
return T_WHILE;
> }
вернуть T_ID;

int get_token(struct Token *token)
{
int i = 0;
char c;

в то время как (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 ;

Примечание: это превратится в длинный код, поэтому дается только короткий фрагмент.

Напишите код лексера. Первым шагом в написании компилятора является написание лексера, который представляет собой программу, которая разбивает исходный код на поток токенов, таких как ключевые слова, символы и идентификаторы.

Вот пример парсера для Simple:

#include ‹stdio.h›
#include ‹string.h›

struct Token {
enum TokenType type;
char value[100];
};

узел структуры {
int type;
узел структуры *left;
узел структуры *right;
значение int;
};

Узел структуры *expression();
Узел структуры *term();
Узел структуры *factor();

int get_token(struct Token *token);

узел структуры *expression()
{
узел структуры *node = term();

токен struct 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;

узел возврата;

узел структуры *term()
{
узел структуры *node = factor();

токен struct 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;

узел возврата;

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();< br /> get_token(&token);
вернуть узел;
}
вернуть 0;

  1. Генерация кодов: Генератор кода берет синтаксическое дерево из синтаксического анализатора и генерирует машинный код, реализующий программу.
  2. Объединить все: вам нужно интегрировать лексер, синтаксический анализатор и генератор кода в единую программу, которая считывает исходный код, генерирует машинный код и выводит исполняемый файл.

генерация кода для 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(“отправить rax\n”);
printf(“добавить rax, rbx\n”);
printf(“отправить rax\n”);
} else if (node-›type == T_MINUS) {
generate_code(node-›left);
generate_code(node-›right);
printf(“ pop rbx \n”);
printf(“поп rax\n”);
printf(“sub rax, rbx\n”);
printf(” push rax\n”);< br /> } else if (node-›type

Если вы только начинаете, вы можете начать с небольшого проекта, который фокусируется на конкретном аспекте дизайна компилятора, таком как лексер или синтаксический анализатор, прежде чем пытаться написать полный компилятор.