Воскресенье, 17 Ноября 2024, 11:45

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 1
  • 1
[Урок]Интерпретатор brainfuck
SaiteiДата: Вторник, 09 Июня 2015, 16:58 | Сообщение # 1
старожил
Сейчас нет на сайте
Существует один очень забавный эзотерический язык программирования, имя которому - brainfuck.
Что же в нём такого мозговыносящего? smile А то, что весь язык состоит лишь из простейших команд:
Цитата
">" - перейти к следующей ячейке
"<" - перейти к предыдущей ячейке
"+" - прибавить единицу текущей ячейке
"-" - отнять единицу у текущей ячейки
"." - вывести значение текущей ячейки на экран
"," - ввести с клавиатуры значение ячейки
"[" - начало цикла (если значение ячейки == 0, то переходим к "]")
"]" - конец цикла (если значение ячейки == 0, то идём дальше. Иначе - возвращаемся к "[")

В языке мы работаем с ячейками некоторой ленты. Обычно размер ленты - это 30000 ячеек.
Язык полон по Тьюрингу, а значит на нём возможно реализовать тоже самое, что и на других языках! Теперь, думаю, ясно почему язык назвали "brainfuck"? happy

Пример программы и результат:
Код
++++[>++++[.-]>++++++++++.----------<<-]



Команды языка крайне просты, так почему бы не написать для него интерпретатор? Что ж, приступим!
Нам понадобятся следующие библиотеки:
Код
#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;
     }
    }


Что ж, вот и всё! Интерпретатор написан. Основная трудность, как вы заметили, может возникнуть лишь при обработке циклов. А так - всё элементарно smile
Надеюсь, что урок Вам понравился!

SaiteiДата: Вторник, 09 Июня 2015, 17:00 | Сообщение # 2
старожил
Сейчас нет на сайте
Вот с подсветкой синтаксиса и правильным выравниванием: pastebin.
FumleadДата: Вторник, 09 Июня 2015, 20:13 | Сообщение # 3
участник
Сейчас нет на сайте
Не боишься читать файл без проверки его валидности? А так вроде неплохо, ну, для новичков пойдет. Можно еще замутить такой же, но в С++-like way (с классами, блэкджеком виртуальными методами и программистками)

Параноик с гениальным планом по захвату мира.
SaiteiДата: Вторник, 09 Июня 2015, 20:18 | Сообщение # 4
старожил
Сейчас нет на сайте
Fumlead, да, всё верно говоришь))
Просто мне хотелось написать что-нибудь маленькое wink
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, это само собой. Интерпретатор я писал просто ради пробы, решил поделиться со всеми.
И да, можно много чего допилить - но эту возможность я даю людям, которых мой урок заинтересовал.
  • Страница 1 из 1
  • 1
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг