Вступление

Сидел я тут как-то вечером и думал, что можно такое интересное написать на Python’е и тут я вспомнил, что когда-то давно, ещё на C#, пытался написать интерпретатор какого-нибудь простого языка, того же BASIC например, но особо ничего не получалось. В момент этих попыток я наткнулся на такой язык, как Brainfuck. Сейчас я решил написать интерпретатор именно этого языка, так как он является одновременно простым для написания и сложным для понимания))

О самом языке

Brainfuck – по истине мозговыносящий (и это ещё мягко сказано) эзотерический язык программирования (нестандартный язык, который создаётся с целью развлечения или исследования особенностей программирования. Он обычно имеет необычный синтаксис и принципы работы, что делает его сложным для понимания и использования в реальных проектах).

Он имеет всего 8 простых команд:

Команда

Описание

>

Следующая ячейка

<

Предыдущая ячейка

+

Значение текущей ячейки увеличивает на 1

-

Значение текущей ячейки уменьшают на 1

.

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

,

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

[

Начало цикла

]

Конец цикла

Как всё писалось?

Я начал смотреть видеоролики, просматривал статью на Википедии, чтобы понять, что это за язык и с чем его едят)) Это оказалось не таким уж лёгким занятием, но я справился.

Как только я создал проект, я начал с создания класса Pyfuck, в который добавлял следующие функции:

Функция инициализации:

def __init__(self):
        self.memory = [0] * 30000 # Память для хранения данных
        self.pointer = 0 # Указатель текущей ячейки памяти
        self.output = "" # Результирующая строка

Интерпретатор имеет память под 30.000 однобайтовых ячеек с нулевыми начальными значениями (всё, как в оригинальном Brainfuck).

Имеется указатель текущей ячейки памяти (его переключение осуществляется с помощью команд > и < в коде программы) и результирующая строка (она выводит итоговый результат программы).

Следующей является функция интерпретации:

def interpret(self, program):
        self.output = ""

        for i in range(len(program)):
            command = program[i]

            if command == ">": # Следующая ячейка
                self.pointer += 1

            elif command == "<": # Предыдущая ячейка
                self.pointer -= 1

            elif command == "+": # Значение текущей ячейки увеличивает на 1
                self.memory[self.pointer] += 1

            elif command == "-": # Значение текущей ячейки уменьшают на 1
                self.memory[self.pointer] -= 1

            elif command == ".": # Напечатать значение из текущей ячейки
                self.output += chr(self.memory[self.pointer])

            elif command == ",": # Ввести извне значение и сохранить в текущей ячейке
                input_value = input("Введите значение: ")
                if len(input_value) <= 1:
                    self.memory[self.pointer] = ord(input_value)
                else:
                    return

            elif command == "[": # Начало цикла
                if self.memory[self.pointer] == 0:
                    nested = 1
                    while nested > 0:
                        i += 1
                        if program[i] == "[":
                            nested += 1
                        elif program[i] == "]":
                            nested -= 1

            elif command == "]": # Конец цикла
                if self.memory[self.pointer] != 0:
                    nested = 1
                    while nested > 0:
                        i -= 1
                        if program[i] == "]":
                            nested += 1
                        elif program[i] == "[":
                            nested -= 1

            else:
                print("Ошибка: неизвестная команда!")
        return self.output

В цикле for происходит итерация по каждой команде программы. В зависимости от команды выполняются различные действия: перемещение указателя на следующую или предыдущую ячейку памяти, увеличение или уменьшение значения текущей ячейки, печать символа из текущей ячейки памяти и считывание введённого значения в текущую ячейку.

Также реализована поддержка циклов: если значение текущей ячейки равно 0, программа пропускает все команды до соответствующей закрывающей скобки. Если же значение текущей ячейки памяти не равно 0, то происходит возврат к соответствующей открывающей скобке, чтобы продолжить выполнение цикла.

Как составляется программа?

Тут всё довольно интересно) Для составления программ удобно использовать таблицу символов ASCII:

Мы должны обратить внимание на Decimal (это число, которое соответствует определённому символу. Это десятичное представление числа символа в таблице ASCII. Например, символ «A» имеет значение 65 в ASCII). В Brainfuck это будет выглядеть, как 65 команд «+».

Вот пример программы, печатающая «Hello World!»:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.+++++++++++++++++++++++++++++.+++++++..+++.>++++++++++++++++++++++++++++++++.<------------------------.++++++++++++++++++++++++.+++.------.--------.>+.
Результат программы
Результат программы

Заключение

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

Полный код можно посмотреть на моём GitHub.

С вами был Yura_FX. Спасибо, что дочитали данную статью до конца. Не забывайте делиться своим мнением в комментариях :)

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


  1. bfDeveloper
    28.11.2023 11:11
    +1

    Очень рекомендую что-нибудь написать на самом Brainfuck и запустить в вашем эмуляторе. Мне в своё время очень помогло получить ощущение контроля:

    • память это ячейки, интерпретируй как хочешь,

    • алгоритмы это про логику, а не конструкции языка

    • в программировании есть свой юмор


    1. Yura_FX Автор
      28.11.2023 11:11

      Спасибо за совет, обязательно попробую написать что-нибудь на Brainfuck'е и запустить в моём интерпретаторе (оговорочка: не эмулятор). Согласен с вами, контроль над программой - это очень важная вещь в программировании)


  1. Alexandroppolus
    28.11.2023 11:11
    +3

    На кодоварсах есть сумасшедшая ката с компиляцией "ассемблера" в код на брейнфаке.


    1. Yura_FX Автор
      28.11.2023 11:11
      +1

      Звучит довольно интересно)


  1. evgeniya_oku53
    28.11.2023 11:11

    Друг мой, статья оказалась очень интересной и познавательной. Я узнала много нового о данном языке программирования. Оказывается, вот оно как, язык, имеющий всего 8 команд, которые позволяют написать что-то простенькое. Мне особенно понравилось, как легко можно читать и понимать ваш исходный код. Как будет свободное время, скачаю ваш интерпретатор и начну пытаться что-то на нём писать.


    1. Yura_FX Автор
      28.11.2023 11:11

      Я рад, что Вам понравилось)


  1. andruino99Reg
    28.11.2023 11:11

    У меня где то лежала старенькая Raspberry Pi 3 с Ubuntu, может попробую, так сказать, портировать туда вашу прогу. Авось, что то и получится


    1. includedlibrary
      28.11.2023 11:11

      В смысле портировать, код же платформонезависимый, он итак будет работать без всяких усилий с вашей стороны? Можно портировать на малинку какой-нибудь компилятор brainfuck.


    1. Yura_FX Автор
      28.11.2023 11:11

      Извиняюсь, но я особо за Raspberry не шарю, так что могу только пожелать Вам удачи в этом деле.