C++BrainFuck и C++


Всем привет.

Сегодня разберем как программировать на самом лучшем эзотерическим языке программирования, а именно BrainFuck.

Немного истории



BrainFuck дословно переводится как «еб**ь мозг» =) из за названия он не очень популярен, а так его можно было бы использвать в школах, дабы преподать людям основы основ.
BrainFuck придумал Урбаном Мюллером (нем) в 2003 году. Сейчас это самый популярный эзотерический язык программирования. BrainFuck предстовляет собой только 8 команд, и этого достаточно что бы написать любую программу как и на С++, это как раз минимальный набор для языка.

Об языке



Как говорилось ранее язык имеент всего лишь 8 комманд.
> ----------------перейти к следующей ячейке

< ----------------перейти к предыдущей ячейке

+ ----------------увеличить значение в текущей ячейке на 1

----------------уменьшить значение в текущей ячейке на 1

. ----------------напечатать значение из текущей ячейки

, ----------------ввести извне значение и сохранить в текущей ячейке

[ ----------------если значение текущей ячейки нуль, перейти вперёд по
тексту программы на ячейку, следующую за соответствующей
] (с учётом вложенности)

] ----------------если значение текущей ячейки не нуль, перейти назад по тексту
программы на символ [ (с учётом вложенности)


Пример программы



Программа печатающая Hello World!

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++
.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
------.--------.>+.>.
Разберем ее позже

Странное место статьи



Как вы уже заметили, что статья находится в разделе C++, не странно ли, нет, так как сдесь пойдет речь именно о С++, а именно написания интерпретатора к BrainFuck. Почему? Просто в BrainFuck всего лишь 8 комманд и пишется все это очень быстро.

Начнем



Нам потребуется только ввод-вывод и чтение исходника, для этого подойдут
iostream и fstream, так что подключаем их

#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char** argv)
{
}

Теперь загрузим наш исходник
ifstream file(argv[1]);

Эти мы создадим поток с файлом в качестве параметра (programm source.bf)

Теперь объявим пару переменных, это:
массив чаров на 30000 элементов (ячейки),
адресную переменную
и переменную в которой будет лежать текущая команда

и того:

unsigned char a[30000];
  unsigned char *b = a;
  char c;


Еще надо обнулить массив ячеек

А теперь не будем замарачиваться какими то замудренными паттернами и тд а просто через switch перебираем наши комманды, читаем первую комманду и начинаем перебирать:

file >> c;
while(!(file.eof()))
{
   switch©
   {
      case '>': break;
      case '<': break;
      case '+': break;
      case '-': break;
      case '.': break;
      case ',': break;
      case '[': break;
      case ']': break;
      default:  break;
   }
   file >> c;
}


переменная b будет отвечать за выполнение комманд
переменная a хранит в себе ячейки значений
переменная c хранит в себе текущую комманду
переменная file содержит поток исходника

Теперь приступим к заполнению реакций на команду

> <
Этими командами мы перемещаемся в ячейках, реализуем их
b++;
и
b--;
все очень просто, b у нас держит адрес первого элемента массива, и если прибавить еденицу то он переместится к следующиму, а если отнять еденицу то переместся к предыдущей (я не делаю проверку на выход за массив, хотя вы можете реализовать)

+ -
Эти команды изменяют значение текущей ячейки, реализуется тоже очень просто
(*b)--;
и
(*b)++;
Сначало разименовываем а потом прибавляем к текущему значению еденицу. Скобочки нужны обязательно так как приоритет у инкрементов больше чем у разименовывания.

. ,
Вывод —
cout << *c;
Ввод —
cin >> *c;
тоже все просто, разименовываем и записываем в ячейку.

[ ]
ну и наконец то циклы, тоже ничего сложного не прелставляют, как они работают можно почитать выше

Итак, начало цикла —
int n = 1;
if((*b) == 0)
   while(n != 0)
   {
      file >> c;
      if(с == ']')
         n--;
      else if(с == '[')
	 n++;
   }

тут мы смотрим, если значение равно 0 то пропускаем все что находится между []
n — уровень вложения

Конец цикла-
int n = 1;
if((*b) != 0)
   while(n != 0)
   {
      int k = file.tellg();
      file.seekg(k - 2);
      file >> c;
      if(c == ']')
	 n++;
      else if(c == '[')
	 n--;
   }

сдесь все тоже самое, нашли закрывающуюся скобку, перемещаямся на 2 позиции назад, читаем символ, если закрывающая скобка то прибавляем вложение если открывающаяся то убавляем

Вот и все, 8 комманд, теперь компилируем и смотрим выполнение примера

исходник: http://codepaste.ru/9027/

Попишем



Итак давайте для начала разберем исходник вот этот

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
++++++++++++++++++++++++++++++++++++++++++++++.
------------------.
+++++++++++++++++++.
---------------.
-------------------------------------------------------.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
+++.
----------------------------------------------------------------------
-------------------------------------.


сдесь не применяются ячейки, про них может позже
стандартная ячейка стоит 0 и все изменения применяются к ней
вначале мы прибавляем 69 и выводим на экран, это получается большое E (69)
дальше прибавляем 46 и выводим получаем s(115), потом отнимаем от 115 18 и выводим получаем a(97) и так далее и получаем на выходе Esate.ru и новоя строчка (10)
таблицу можно посмотреть тут

В примере выше, где hello world там используются ячейки, можете поизучать.

Заключение



Есть еще такой язык pbrain там все тоже самое только добавлены 3 комманды — ( ):
() — открытие закрытие функции: — вызов функции

Ну вот и все, жду ваших интересных программ на BrainFuck =)
  • +2
  • isaer
  • 15 января 2012, 20:47

Вставка изображения

Комментарии (2)

rss свернуть / развернуть
Жесткий язык!=)
Наверное пока не решусь воспользоваться), сильно сложно, лучше буду вводить команды ассемблера Нex-ами)))) так хоть мозг цел будет=))))
Супер!!!
+1 к статьи, только плиз мат убери=)
это как раз минимальный набор бля(для) языка
))

свернуть ветку
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.