software-development
December 3, 2023

Профессиональная разработка на.. 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, Node.js.

Чаще всего такую технику применяют для выдачи «желаемого за действительное», например что генерики или знаменитые лямбды в 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][3]

Да это та самая программа с помощью которой создаются другие программы, в изначальном смысле этого слова.

Практически любой компилятор — очень сложно устроенная штука, даже просто использование которой требует определенной подготовки. А задача создания компилятора с нуля — предмет для изучения в высших технических заведениях и удел исключительно опытных программистов.

Оценили-осознали?

Теперь совместите в голове безумный эзотерический язык программирования и связку из транспилера (из нормального языка) и компилятора — для запуска и демонстрации.

Ладно ладно, вижу что не осознали. Поэтому объясню чуть подробнее:

программировать на самом 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()

Запускается оно вот так:

Давайте немного разберем логику, для лучшего понимания.

Вот эта строка:

bc = Bytecode.from_function(test, x=40,y=2)   #42

генерирует код Brainfuck из байткода Python(!)

Что вы и видите на скриншоте выше, вывод на печать делает вот эта инструкция:

print(bc)

А вот этот блок интерпритирует код на Brainfuck и выводит на печать:

vm = Machine(bc)
vm.run()
print(vm.current)

К сожалению этот проект очень далек до завершения:

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

При сборке появится вот такая ошибка:

Чтобы ее исправить достаточно закомментировать условие выбора:

ifeq ($(GAMING_MODE_AVAILABLE),1)

Результат должен быть таким:

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.

В результате успешной сборки в каталоге ./target/debug появится бинарник bfc:

Что мы будем делать с оптимизирующим компилятором Brainfuck?

Разумеется использовать:

./target/release/bfc /opt/work/tmp/brainfix/program.bf 

Да это тот самый пример с "Hello world!" сгенеренный выше из С-подобного кода, который с помощью нашего замечательного оптимизирующего компилятора превратился в настоящий бинарник:

Итого

С помощью Божьей воли и запрещенных препаратов я дал вам в руки по-настоящему страшное психическое оружие для форматирования мозга, еще раз повторим всю цепочку:

Код на С-подобном языке -> транспилер Brainfix -> компилятор Bfc -> готовый нативный бинарник

В чем отличие от нормальной разработки?

В том что вы спокойно можете сдать в качестве лабы код на Brainfuck, который спокойно будет компилироваться в запускаемый бинарник и вполне себе подпадать под термин «исходный код».

Несчастный препод разобьет башку об стол, пытаясь в этом коде разобраться а вы никаких сложностей даже не увидите, поскольку настоящий код с логикой останется на уровне транспилера.

Правда весело?

Вот примерно поэтому мои бывшие преподаватели в ВУЗе до сих пор меня помнят и резко бледнеют услышав фамилию — не дай Бог опять такой появится ;)

Ну а если вас попросили написать что-то вроде FizzBuzz в качестве тестового задания при приеме на работу - теперь вы знаете как и на чем его делать.

Но повторюсь:

главное после таких экспериментов это успеть убежать достаточно далеко, чтоб не догнали.