- Выберите язык: выберите язык высокого уровня, который вы хотите скомпилировать. Это определит синтаксис и особенности языка, с которым вы будете работать.
Для этого примера давайте выберем простой вымышленный язык под названием «Простой». Синтаксис 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;
- Генерация кодов: Генератор кода берет синтаксическое дерево из синтаксического анализатора и генерирует машинный код, реализующий программу.
- Объединить все: вам нужно интегрировать лексер, синтаксический анализатор и генератор кода в единую программу, которая считывает исходный код, генерирует машинный код и выводит исполняемый файл.
генерация кода для 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
Если вы только начинаете, вы можете начать с небольшого проекта, который фокусируется на конкретном аспекте дизайна компилятора, таком как лексер или синтаксический анализатор, прежде чем пытаться написать полный компилятор.