В последнее время я очень интересовался компиляторами, низкоуровневыми языками программирования и архитектурой компьютера. Однако на хабре про компиляторы низкоуровневых языков, нашел очень мало информации. Поэтому я решил рассказать о ассемблере и написать свой. Весь код в статье написан на языке Си из-за быстроты программ, написанных на нем, и его простоты.
Набор команд и грамматика
Набор команд не должен быть большим. У меня получилось всего 18 команд. Теперь, давайте определимся что наш виртуальный процессор должен уметь делать. Прежде всего, это пересылка данных, работа со стеком и базовая арифметика.
Но как же процессор будет давать/получать данные о своей работе? Для это нам нужно будет сделать ввод-вывод и возврат результата выполнения. Все. Никаких функций, переменных и переходов. Только хардкор. Однако язык будет условным, как ARM condition language. Раз он условный, нужно вырабатывать условия, пусть это будут делать сравнения.
Теперь простейший пример программы на языке:
al mov r00, #0
al cmp r00, #400
av return #0
le mul r01, r00, r00
le swi #5
le inc r00
le mov r14, #1
Разбор кода:
al mov r00, #0
— в любом случае r00 = 0al cmp r00, #400
— в любом случае сравним r00 и 400av return 0
— если r00 больше, завершаем выполнение с кодом 0le mul r01, r00, r00
— если r00 меньше или равно, r01 = r00 * r00le swi #5
— если r00 меньше или равно, закрашиваем пиксель(r00, r01)le inc r00
— если r00 меньше или равно, увеличим r00 на 1.le mov r14, #1
— если r00 меньше или равно, счетчик программы = 1
Для начала напишем парсер, создающий из входной строки объектное (общее) представление кода. Самый простой способ это сделать — сохранить условие, команду и аргументы в структуру.
Заголовочный файл:
#ifndef PARSER_H
#define PARSER_H
struct operation {
char cond[32]; // Условие выполнения
char cmd[32]; // Сама команда
char arg0[32]; // Первый аргумент
char arg1[32]; // Второй аргумент
char arg2[32]; // Третий агрумент
};
void dump_operation(struct operation *op); // Вывод данных операции
struct operation *parse_code(const char *code); // Парсит строку в общее представление кода
#endif // PARSER_H
Исходный файл:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "parser.h"
#define SON(x) (((x) && (*(x) != '\0'))?(x):"NONE") //Если строка пуста или указатель нулевой, то выдаем NONE (для dump_operation)
void dump_operation(struct operation *op)
{
if (!op) { // Если дан неверный указатель
printf("OP: NULL\n");
return;
}
// Если указатель в порядке
printf("COND: \"%s\"\nOP: \"%s\"\nARGS: \"%s\", \"%s\", \"%s\"\n\n",
SON(op->cond), SON(op->cmd), SON(op->arg0),
SON(op->arg1), SON(op->arg2));
}
static inline const char *skip(const char *line, char *skips) // Пропуск строки в строке
{
line += strlen(skips); // Увеличим указатель на пропарсиваемую строку на длину пропускаемой
while ((*line == ' ') || (*line == ',')) // Пропустим ненужные символы
line++;
if ((*line == '\0') || (*line == '\n') || (*line == ';')) // Если конец строки
return 0; // Вернем нулевой указатель
return line; // Иначе вернем полученный указатель
}
static inline void str_to_lower(char *str) // Переводит строку в нижний регистр
{
unsigned int l = strlen(str);
unsigned int i = 0;
for (i = 0; i < l; i++)
str[i] = (char)tolower(str[i]);
}
struct operation *parse_code(const char *code)
{
struct operation *op = (struct operation*)calloc(1, sizeof(*op)); // Выделим память под результат
char *tokens[5] = {op->cond, op->cmd, op->arg0, op->arg1, op->arg2};
unsigned int i = 0;
for (i = 0; i < 5; i++) {
if (sscanf(code, "%[0-9a-zA-Z@#$]", tokens[i]) <= 0/*Получаем строку в текущий токен*/) { // Если встретили запрещенный символ
fprintf(stderr, "Error!\nUnknown symbol!\n"); // Говорим об этом
free((void *)op); // Освобождаем память
return 0; // Возвращаем нулевой указатель
}
str_to_lower(tokens[i]); // Переводим строку в нижний регистр
code = skip(code, tokens[i]); // Пропускаем полученный токен
if (!code) // Если код закончился
break; // Завершаем цикл
}
return op; // Возвращаем объектное представление кода
}
В следующей части расскажу об анализе кода и переводе его в байт-код.
Литература по теме
- Э. Таненбаум Т.Остин Архитектура компьютера А. Ахо, Р. Сети, Д.
- Ульман Компиляторы: принципы, технологии и инструменты
P.S. Народ, комментируйте, что не нравится. Постараюсь исправить. Заранее спасибо!
Комментарии к статье: 11
Возможность комментировать эту статью отключена автором. Возможно, во всем виновата её провокационная тематика или большое обилие флейма от предыдущих комментаторов.
Если у вас есть вопросы по содержанию статьи, рекомендуем вам обратиться за помощью на наш форум.