[Урок]Интерпретатор brainfuck
| |
Saitei | Дата: Вторник, 09 Июня 2015, 16:58 | Сообщение # 1 |
старожил
Сейчас нет на сайте
| Существует один очень забавный эзотерический язык программирования, имя которому - brainfuck. Что же в нём такого мозговыносящего? А то, что весь язык состоит лишь из простейших команд: Цитата ">" - перейти к следующей ячейке "<" - перейти к предыдущей ячейке "+" - прибавить единицу текущей ячейке "-" - отнять единицу у текущей ячейки "." - вывести значение текущей ячейки на экран "," - ввести с клавиатуры значение ячейки "[" - начало цикла (если значение ячейки == 0, то переходим к "]") "]" - конец цикла (если значение ячейки == 0, то идём дальше. Иначе - возвращаемся к "[") В языке мы работаем с ячейками некоторой ленты. Обычно размер ленты - это 30000 ячеек. Язык полон по Тьюрингу, а значит на нём возможно реализовать тоже самое, что и на других языках! Теперь, думаю, ясно почему язык назвали "brainfuck"?
Пример программы и результат: Код ++++[>++++[.-]>++++++++++.----------<<-]
Команды языка крайне просты, так почему бы не написать для него интерпретатор? Что ж, приступим! Нам понадобятся следующие библиотеки: Код #include <iostream> #include <string> #include <fstream> //т.к. программу будем брать из файла #include <vector>
Так же нам понадобится массив ячеек (переменных типа char) и индекс текущей ячейки: Код #define CELLS_NUM 30000 unsigned char arr[CELLS_NUM] = {}; unsigned int current_cell = 0; //по-умолчанию мы находимся на первой ячейке
Далее нам необходимо получить текст программы на языке brainfuck: Код using namespace std;
string filename; cout << "Source:"; cin >> filename; fstream source_file(filename.c_str(), ios_base::in); vector<unsigned char>source; while (!source_file.eof()) { char ch = 0; source_file.get(ch); source.push_back(ch); }
source_file.close();
Как вы уже догадались, текст программы лежит в векторе source. Осталось пробежаться по каждому символу вектора: Код for (unsigned i = 0; i < static_cast<unsigned>(source.size()); ++i) { unsigned char ch = source[i]; switch (ch) { case '>': current_cell++; break; case '<': current_cell--; break; case '+': arr[current_cell]++; break; case '-': arr[current_cell]--; break; case '.': cout << arr[current_cell]; break; case ',': cin >> ch; arr[current_cell] = ch; break; case '[': if (arr[current_cell] == 0) { unsigned j = 1; while (j != 0) { i++; switch (source[i]) { case '[': j++; break; case ']': j--; break; default: break; } } } break; case ']': if (arr[current_cell] != 0) { unsigned j = 1; while (j != 0) { i--; switch (source[i]) { case '[': j--; break; case ']': j++; break; default: break; } } } break; default: break; } }
Что ж, вот и всё! Интерпретатор написан. Основная трудность, как вы заметили, может возникнуть лишь при обработке циклов. А так - всё элементарно Надеюсь, что урок Вам понравился!
|
|
| |
Saitei | Дата: Вторник, 09 Июня 2015, 17:00 | Сообщение # 2 |
старожил
Сейчас нет на сайте
| Вот с подсветкой синтаксиса и правильным выравниванием: pastebin.
|
|
| |
Fumlead | Дата: Вторник, 09 Июня 2015, 20:13 | Сообщение # 3 |
участник
Сейчас нет на сайте
| Не боишься читать файл без проверки его валидности? А так вроде неплохо, ну, для новичков пойдет. Можно еще замутить такой же, но в С++-like way (с классами, блэкджеком виртуальными методами и программистками)
Параноик с гениальным планом по захвату мира.
|
|
| |
Saitei | Дата: Вторник, 09 Июня 2015, 20:18 | Сообщение # 4 |
старожил
Сейчас нет на сайте
| Fumlead, да, всё верно говоришь)) Просто мне хотелось написать что-нибудь маленькое
|
|
| |
Gudleifr | Дата: Вторник, 09 Июня 2015, 20:35 | Сообщение # 5 |
почти ветеран
Сейчас нет на сайте
| Компилятор выглядит гораздо красивее (здесь, понятно, только основная процедура):: Код switch(c){ case '>':fprintf(outfile,"ptr++;\n");break; case '<':fprintf(outfile,"ptr--;\n");break; case '+':fprintf(outfile,"++*ptr;\n");break; case '-':fprintf(outfile,"--*ptr;\n");break; case '[':fprintf(outfile,"while(*ptr){\n");break; case ']':fprintf(outfile,"}\n");break; case '.':fprintf(outfile,"putchar(*ptr);\n");break; case ',':fprintf(outfile,"*ptr=getchar();\n");break;}
Загадка. Допустим, мы не знаем никаких языков, кроме BrainFuck. Поэтому, нам надо как-то его улучшить, чтобы выжить. Не писать же эти мегатонны закорючек. Первое, что приходит в голову - задействовать цифры, заменив длинные последовательности символов набором <Число><Символ>, что означает "повторить символ нужное число раз". Второе очевидное улучшение - ввести макросы, например в виде <Имя>(<Строка>), далее любое вхождение имени заменяется строкой. Однако, действие наших макросов сильно зависит от того в какой точке памяти их вызвали. Хочется ввести оператор "перейти по абсолютному адресу", пусть наш компилятор посчитает, где остановился и скомпилирует нужное число стрелочек. Опаньки! Что случилось?
Быдлокодеры любят повторять: "логика, убивающая мозг",- когда их пытаются заставить программировать.
Сообщение отредактировал Gudleifr - Вторник, 09 Июня 2015, 21:59 |
|
| |
Saitei | Дата: Вторник, 09 Июня 2015, 21:55 | Сообщение # 6 |
старожил
Сейчас нет на сайте
| Gudleifr, это само собой. Интерпретатор я писал просто ради пробы, решил поделиться со всеми. И да, можно много чего допилить - но эту возможность я даю людям, которых мой урок заинтересовал.
|
|
| |
|