Профессиональная разработка.. на Brainfuck
Хотите довести до дурки любимого преподавателя компьютерных наук или навсегда прослыть «особенным» среди коллег, сразу после немедленного увольнения?
Ниже патентованный метод, с которым вас точно не забудут.
Вы наблюдаете самый настоящий ящик Пандоры, который автор для вас любезно приоткрыл.
Электронная дичь
Ладно, допустим вы не имеете отношения к ИТ или не занимаетесь непосредственно разработкой ПО. Возможно вы начинающий веб‑разработчик, умеющий только в лендинги и сайты‑визитки — словом вы никогда прежде не слышали об экзотерических языках программирования и их ярчайшем представителе:
Brainfuck — один из эзотерических языков программирования, придуман Урбаном Мюллером (нем. Urban Müller) в 1993 году, известен своим минимализмом. Название языка можно перевести на русский как вынос мозга, оно напрямую образовано от английского выражения brainfuck (brain — мозг, fuck — вынос), т. е. заниматься ерундой. Язык имеет восемь команд, каждая из которых записывается одним символом. Исходный код программы на Brainfuck представляет собой последовательность этих символов без какого-либо дополнительного синтаксиса.
Понимаю, тяжело воспринять фразу «экстремальный минимализм» по отношению к языку программирования, поэтому вот вам небольшая иллюстрация:
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.>>>++++++++[<++++>-] <.>>>++++++++++[<+++++++++>-]<---.<<<<.+++.------.--------.>>+.>++++++++++.
Да это исходный код программы.
Ну и заодно запредельный треш и ужас, созданный специально для выноса мозга программистам, с чего и получил такое поэтическое название.
Разбираем по шагам:
>++.
в ячейке 1 добавление 2 к 70 и вывод на печать ASCII-кода 72, т.е. буквы «Н».
>+.
в ячейке 2 добавление 1 к 100 = 101, печать буквы «e»
+++++++..
в этой же ячейке добавление 7 к 101 = 108, печать «l» дважды
+++.
в этой же ячейке добавление 3 к 108 = 111, печать «o»
И так далее и тому подобное:
Brainfuck почти не используется для практического программирования (за исключением работ отдельных энтузиастов), а используется преимущественно для головоломок и задач для соревнований.
Теперь, если вы занимаетесь разработкой и более‑менее подкованы в программировании — вернитесь обратно к картинке выше и задумайтесь: что именно автор тут для вас приготовил:)
Транспилеры и компиляторы
Минутка матчасти, прежде чем мы с вами погрузимся в бездну бесконечного ужаса разработки, для лучшего хоть какого-то понимания этой сложной темы.
Начнем с транспилера:
A source-to-source translator, source-to-source compiler (S2S compiler), transcompiler, or transpiler is a type of translator that takes the source code of a program written in a programming language as its input and produces an equivalent source code in the same or a different programming language. A source-to-source translator converts between programming languages that operate at approximately the same level of abstraction, while a traditional compiler translates from a higher level programming language to a lower level programming language.
Максимально упрощая, транспилер — такой специальный компилятор, для превращения исходного кода на одном языке в исходный код на другом языке.
Очень часто используется на практике, в том числе в современных и сверхпопулярных языках вроде Java или.NET.
Чаще всего такую технику применяют для выдачи «желаемого за действительное», например с помощью транспилера реализованы ваши любимые генерики и замыкания в Java.
Теперь про интерпретатор:
In computer science, an interpreter is a computer program that directly executes instructions written in a programming or scripting language, without requiring them previously to have been compiled into a machine language program.
По‑сути это такой «разнорабочий от мира ПО», который сам ничего не умеет, зато отлично выполняет простые последовательные команды.
И наконец «его величество» компилятор:
In computing, a compiler is a computer program that translates computer code written in one programming language (the source language) into another language (the target language). The name «compiler» is primarily used for programs that translate source code from a high-level programming language to a low-level programming language (e.g. assembly language, object code, or machine code) to create an executable program.[1][2]
Да это «та самая программа с помощью которой создаются другие программы», в изначальном смысле этого слова.
Практически любой компилятор — очень сложная программа, даже использование которой требует определенной подготовки. Ну а задача создания компилятора с нуля — предмет для изучения в высших технических заведениях и удел исключительно опытных профессионалов.
В теории.
Теперь совместите в воображении безумный эзотерический язык программирования и связку из транспилера и компилятора.
Именно эту дичь мы сейчас и будем творить.
Но все же объясню чуть подробнее, для обывателей:
программировать на самом Brainfuck безумно тяжело и ничего сложнее «Hello, World» у вас не выйдет
пока вы не рехнетесь, но если использовать транспилер, — становится возможно писать код уже на С (или С‑подобном языке) и гораздо более интересные вещи.
Например можно взять домашнее задание по числам Фибоначчи:
include "std.bfx"
function main()
{
prints("f[0] = ");
let a = scand();
prints("f[1] = ");
let b = scand();
prints("Up to n = ");
let n = scand();
prints("0: "); printd(a); endl();
for (let i = 0; i != n - 1; ++i)
{
printd(i + 1); prints(": ");
printd(b); endl();
let tmp = b;
b += a;
a = tmp;
}
printd(n); prints(": ");
printd(b); endl();
}
И сдать его в таком интересном виде (фрагмент):
>>>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>[-]++++++++++++++++++++++++++++++++++++++++++++++++.>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>[-]++++++++++++++++++++++++++++++++.>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>[-]++++++++++++++++++++++++++++++++.<<<[-]+>>[-]>[-]>[-]+>[-]+>[-]+>[-]>[-]>[-]+>[-]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>>>[>>>>>[-]>>[-]>[-]<<<<<[>>>>+>+<<<<<-]>>>>>[<<<<<+>>>>>-]<<[-]>>>[-]<<<<<[>>+>>>+<<<<<-]>>>>>[<<<<<+>>>>>-]<<<[>[<<[-]+>>[-]]<[-]]>>>>[-]>[-]<<<<<<[>>>>>+>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]>[-]+>[-]>[-]<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<[<[-]>[-]]<<<[>>>>>[-]>>[-]>[-]<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+>+<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>[<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>-]<<[-]>>>[-]<<<<<<<<<<<<<<<[>>>>>>>>>>>>+>>>+<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>[<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>-]<<<[>[<<[-]+>>[-]]<[-]]>>>>[-]>[-]<<<<<<[>>>>>+>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]>[-]+>[-]>[-]<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<[<[-]>[-]]<<<[>>>>>[-]+>[-]+>[-]>>[-]>[-]<<<<<[>>>>+>+<<<<<-]>>>>>[<<<<<+>>>>>-]<<[-]>>>[-]<<<<<[>>+>>>+<<<<<-]>>>>>[<<<<<+>>>>>-]<<<[>[<<[-]+>>[-]]<[-]]>>>>[-]>[-]<<<<<<[>>>>>+>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]>[-]+>[-]>[-]<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<[<[-]>[-]]<<<[>>>>>,>[-]>[-]<<[>+>+<<-]>>[<<+>>-]<<<<<<<[-]]>>[[-]]<<<<<<<<<[-]>[-]>>>>>>>>>>>>[<<<<<<<<<<<<<+>+>>>>>>>>>>>>-]<<<<<<<<<<<<[>>>>>>>>>>>>+<<<<<<<<<<<<-]<<<<<<[-]]>>[[-]]>>>>>[-]>>[-]>[-]<<<
Полный вариант занимает ~1.6Mb одной строкой, представляте как обрадуется любимый преподаватель такой оригинальной работе?
Главное чтобы вас потом не нашли.
Теперь перейдем к инструментарию для наших веселых шалостей.
Гримуар
Поскольку программисты по большей части ребята веселые, оказалось что для языка Brainfuck реализовано очень много всего интересного. Ниже будет небольшой обзор самых отбитых инструментов, которые мы будем использовать во имя Луны для создания тестового проекта.
Bfpy
Автор этого замечательного проекта объявил личный джихад здравому смыслу:
BFPY is an alternative Python runtime that uses Brainfuck as a bytecode.
Для тех кто еще не оценил всю мощь по одной цитате выше, приведу небольшой пример кода:
#!/usr/bin/env python3.4
# coding: utf-8
from bfpy.instruction import Instruction
from bfpy.bytecode import Bytecode
from bfpy.machine import Machine
def test(x,y):
return x + y
def main():
bc = Bytecode.from_function(test, x=40,y=2) #42
print(bc)
vm = Machine(bc)
vm.run()
print(vm.current)
if __name__ == '__main__':
main()
Вот так это чудо выглядит в работе:
Теперь немного разберем логику, для погружения в безумие лучшего понимания.
Вот эта замечательная строка генерирует код Brainfuck из байткода Python:
bc = Bytecode.from_function(test, x=40,y=2) #42
Что вы и видите на скриншоте выше, где сам вывод на печать делает данная инструкция:
print(bc)
Следующий блок интерпретирует код на Brainfuck и выводит на печать:
vm = Machine(bc)
vm.run()
print(vm.current)
Вся эта радость отлично встраивается например в Python‑скрипт работающий с нейросетями, тем самым добавляя новую грань смысла слову «blackbox», которым так любят прикрывать свои провалы ML‑разработчики.
К сожалению этот проект очень далек до завершения:
For instance, BFPY can only translate arithmetic operations (addition, subtraction, multiplication, floor division and power). There is still a lot of features to implement for it to be a proper Python runtime alternative
Поэтому если у вас есть лишний $1млн и ненависть к человечеству — можете проинвестировать в этот замечательный проект. Деньги пойдут автору на лечение в лучшей дурке.
Brainfix
Следующий проект зашел немного дальше в своем безумии, поэтому именно с его помощью мы с вами и сделаем тестовое приложение:
BrainFix is a compiler/language that takes a C-style language (although the syntax slowly evolved to something very close to Javascript for some reason) and compiles this into BrainF*ck, an esoteric programming language consisting of only 8 operations.
Забираем исходный код и пробуем собрать:
git clone https://github.com/jorenheit/brainfix
cd brainfix
make
К сожалению при сборке появится такая ошибка:
Чтобы ее исправить достаточно закомментировать это условие выбора в makefile:
ifeq ($(GAMING_MODE_AVAILABLE),1)
Нет, я тоже не догадываюсь для чего компилятору Brainfuck мог понадобиться игровой режим, но конечный результат должен быть таким:
CC=g++
CFLAGS= -c -O3 -Wall --std=c++2a -fmax-errors=2 #-Wfatal-errors
SOURCES=interpreter/bfint.cc interpreter/main.cc
OBJECTS=$(SOURCES:.cc=.o)
EXECUTABLE=bfint
all: $(SOURCES) $(EXECUTABLE)
#ifeq ($(GAMING_MODE_AVAILABLE),1)
#$(EXECUTABLE):$(OBJECTS)
# $(CC) $(OBJECTS) -o ../$@ -lncurses
#
#.cc.o:
# $(CC) $(CFLAGS) -DUSE_CURSES lt; -o $@
#
#else
$(EXECUTABLE):$(OBJECTS)
$(CC) $(OBJECTS) -o ../$@
.cc.o:
$(CC) $(CFLAGS) lt; -o $@
#endif
После правки сборка успешно завершается:
В результате сборки в корневом каталоге появится готовый бинарник bfx, запускаем и наслаждаемся:
Теперь пробуем собрать тестовый пример, с помощью вот такой простой и очевидной команды:
./bfx -o program.bf -O1 -I ./std -t int16 bfx_examples/hello.bfx
Результат:
Для проверки корректности, вставляем полученный код Brainfuck в онлайн-интерпретатор:
Там еще много интересных примеров, но большие программы будут ощутимо долго компилироваться в код на Brainfuck, имейте ввиду.
Но едем дальше.
BFC
Как лаконично описывает проект его автор: «an industrial‑grade Brainfuck compiler» и он при этом совсем не врет:
bfc includes an extensive range of optimisations. This page discusses the techniques used, and gives examples of BF programs that are transformed in each case.
Написана эта штука разумеется на Rust (неужели вы сомневались?) и сейчас мы будем ее пенетри.. ээ использовать.
Забираем исходники:
git clone https://github.com/Wilfred/bfc.git
Собирается это чудо сумрачного гения вот такой командой:
RUSTFLAGS='-L /usr/local/lib' cargo build --release
Поскольку дело происходит на FreeBSD (неужели я забыл об этом упомянуть?), с помощью переменной окружения RUSTFLAGS необходимо указать путь /usr/local/lib, который почему‑то не учитывается сборщиком cargo.
Также на машине должен быть сам Rust и 14я версия LLVM.
Зато теперь вы тоже знаете как использовать Rust на FreeBSD, надеюсь эти знания помогут вам построить успешную карьеру в ИТ-индустрии
а не наставят на путь экстремального ИТ-терроризма.
В результате успешной сборки в каталоге ./target/debug появится готовый к использованию бинарник bfc:
Что же мы будем делать с оптимизирующим компилятором Brainfuck? Разумеется использовать для всего хорошего и замечательного:
./target/release/bfc /opt/work/tmp/brainfix/program.bf
Это тот самый пример с «Hello world!» сгенерированный выше из С‑подобного кода, который с помощью замечательного оптимизирующего компилятора превратился в настоящее приложение:
Итого
Повторим еще раз всю цепочку для лучшего запоминания:
Код на С‑подобном языке → транспилер Brainfix → компилятор Bfc → готовое приложение
В чем отличие от обычной разработки:
Вы спокойно сдаете
в качестве лабыкод на Brainfuck, который будет компилироваться в запускаемый бинарник и вполне себе подпадать под термин «исходный код».
Разумеется ваш любимый преподаватель будет в полном восторге, пытаясь в этом коде разобраться, в то время как вы никаких сложностей даже не увидите, поскольку настоящий код с читаемой логикой останется на уровне транспилера.
Правда весело?
Примерно из-за таких милых шалостей мои бывшие преподы до сих пор меня помнят мою фамилию.
Ну а если вас попросят написать тестовое задание, вставив в постановку фразу «на любом известном вам языке» — теперь вы знаете как и на чем его делать.
P.S.
Статья была опубликована на Хабре, более трешевый оригинал доступен в нашем блоге.
Само собой вся изложенная информация приведена исключительно в развлекательных целях, не стоит применять подобные технологии на практике в реальных проектах.












