Skip to content
myutman edited this page Jun 12, 2017 · 38 revisions

Проект “Компилятор языка Ять”.

Постановка задачи и идея решения

Задача

Требуется написать консольное приложение, которое будет преобразовывать код на язке "Ять" в машинный код.

Идея решения

  1. Разделение компиляции на следующие этапы: Лексический анализ. Синтаксический анализ. Построение абстрактного синтаксического дерева (АСД). Преобразование АСД в код на llvm assembly language. Преобразование кода на llvm assembly language в бинарный исполняемый файл.
  2. Реализация грамматики для лексера с помощью flex.
  3. Реализация грамматики для парсера с помощью bison.
  4. Реализация классов для АСД на языке C++ и последующее добавление кода на C++ к парсеру.
  5. Добавление каждому классу для АСД метод для вывода команд на llvm с использованием специального API.

Описание готового решения

Текущее решение строит по исходному коду на языке Ять АСД, преобразует построенное дерево в исходный код на языке C и компилирует этот код в машинный код.

Попытки что-то сделать с llvm находятся в ветке llvm_stuff.

Makefile

Здесь прописаны зависимости для файлов, которые генерируются в процессе сборки проекта.

include/model.h

Здесь описаны классы вершин АСД.

src/model.cpp

Здесь реализованы методы классов абстрактного синтаксического дерева.

src/main.cpp

Здесь реализованно всё, что связано с пользовательским интерфейсом.

src/ypl.lex

Здесь описана грамматика лексического анализа.

src/ypl.y

Здесь описана грамматика синтаксического анализа.

Сборка и эксплуатация проекта

Сборка

  1. Перейдите в корневую директорию проекта
  2. В терминале запустите следующую команду:
$ make

Эксплуатация

Чтобы скомпилировать файл на языке Ять, необходимо запустить в терминале следующую команду:

$ ./yc <имя входного файла> <имя выходного файла>

Если имя выходного файла не указано, то по умолчанию код будет компилироваться в файл "a.out".

Примеры работы.

code.ypl (считывает число из stdin, выводит его факториал):

def fact(a){
	//print(a);
	if (a){
		fact(a-1)*(a);
	} else {
		1;
	};
};
read a;
print fact(a);

Собираем и запускаем (дадим на вход число 5, потом число 8):

$ ./yc code.ypl
$ ./a.out
5
120
$ ./a.out <<< "8"
40320

Компилятор поддерживает комментарии, закомментированная строка не выполняется.

code1.ypl (выводит 12-ое число Фибоначчи):

def foo(a){
	if (a < 2){
		1;
	} else {
		foo(a - 1) + foo(a - 2);
	};
};

print foo(12);

code2.ypl (выводит gcd считанных из стандартного ввода чисел):

def gcd(a, b){
	if (b == 0){
		a;
	} else {
		gcd(b, a % b);
	};
};

read a;
read b;
print gcd(a, b);

Собираем и запускаем:

$ ./yc code2.ypl
$ ./a.out <<< "1998 1971"
27

Тестирование

CXX-тесты

В файле unittest/test.h находятся тесты для классов АСД, реализованные с помощью фреймворка CXX. Все тесты имеют следующий вид:

class MyTest : public CxxTest::TestSuite {
public:
    void testMethod() {
        ...
    }
};

Сборка тестов

  1. Перейдите в корневую директорию проекта
  2. В терминале запустите следующую команду:
$ make test

Запуск тестов

В терминале запустите следующую команду:

$ ./test

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

Running cxxtest tests (15 tests)...............OK!