Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ____ _ __ _
- // | _ \ (_) / _| | |
- // | |_) |_ __ __ _ _ _ __ | |_ _ _ ___| | __
- // | _ <| '__/ _` | | '_ \| _| | | |/ __| |/ /
- // | |_) | | | (_| | | | | | | | |_| | (__| <
- // |____/|_| \__,_|_|_| |_|_| \__,_|\___|_|\_|
- //
- // Интерпретатор языка Brainfuck с процедурным расширением Pbrain
- // на языке С++ (стандарт ISO/IEC 14882:2011)
- //
- // Автор программы: Д.А.Куценко, 2017
- // Программа распространяется по лицензии GNU GPLv3
- // См. http://www.gnu.org/licenses/
- //
- // Компиляция: g++ -std=c++11 -o bf main.cpp
- //
- // Запуск: ./bf '<текст_программы_на_языке_Brainfuck_или_Pbrain>'
- //
- // Ссылки на описания языков Brainfuck и Pbrain:
- // https://esolangs.org/wiki/Brainfuck
- // https://esolangs.org/wiki/Pbrain
- #include <iostream>
- #include <cassert>
- #include <string>
- #include <vector>
- #include <map>
- #include <stack>
- #include <cctype>
- // Класс, описывающий Brainfuck-машину
- class BFMachine {
- public:
- // Создать машину для указанной программы
- BFMachine(const std::string &newProg):
- program(newProg),
- tapeSize(30000),
- tape(tapeSize, '\0'),
- head(0),
- ip(0)
- {
- // Определить границы циклов в программе
- getLoopRanges();
- // Определить границы подпрограмм в программе
- getSubRanges();
- }
- // Выполнить программу
- void run();
- private:
- // Определить границы циклов в программе
- void getLoopRanges();
- // Определить границы подпрограмм в программе
- void getSubRanges();
- // Выдать соответствующую данной другую границу подпрограммы
- size_t getEndOfSub(size_t pos) const {
- assert (subRanges.count(pos) > 0);
- return subRanges.at(pos);
- }
- // Выдать соответствующую данной другую границу цикла
- size_t getAnotherRangeOfLoop(size_t pos) const {
- assert (loopRanges.count(pos) > 0);
- return loopRanges.at(pos);
- }
- // Напечатать содержимое ячейки ленты
- void printCell() const;
- // Ввести с клавиатуры содержимое ячейки ленты
- void inputCell();
- // Увеличить значение ячейки ленты на 1
- void incCell();
- // Уменьшить значение ячейки ленты на 1
- void decCell();
- // Перейти на следующую ячейку ленты
- void nextCell();
- // Перейти на предыдущую ячейку ленты
- void prevCell();
- // Начало тела цикла
- void beginLoop();
- // Конец тела цикла
- void endLoop();
- // Определить подпрограмму
- void declareSub();
- // Перейти в подпрограмму
- void goSub();
- // Выйти из подпрограммы
- void returnSub();
- // Головка находится на ленте?
- bool isHeadOnTape() const {
- return (head >= 0) && (head < tapeSize);
- }
- // Головка указывает на первую ячейку ленты?
- bool isHeadOnFirstCell() const {
- return head == 0;
- }
- // Головка указывает на последнюю ячейку ленты?
- bool isHeadOnLastCell() const {
- return head == tapeSize - 1;
- }
- // Программа (строка команд)
- const std::string program;
- // Размер ленты
- const size_t tapeSize;
- // Лента
- std::vector<char> tape;
- // Позиция головки
- size_t head;
- // Номер команды в программе
- size_t ip;
- // Пары границ циклов в программе
- std::map<size_t, size_t> loopRanges;
- // Стек вызовов для подпрограмм
- std::stack<size_t> callStack;
- // Определения подпрограмм
- std::map<char, size_t> subRegistry;
- // Пары границ подпрограмм в программе
- std::map<size_t, size_t> subRanges;
- };
- // Определить границы циклов в программе
- void BFMachine::getLoopRanges() {
- // Стек для корректного определения вложенности циклов
- std::stack<size_t> s;
- // Номер команды в программе
- size_t pos = 0;
- // Для каждой команды в программе:
- for (const char& c : program) {
- // Если это команда начала цикла, то:
- if (c == '[') {
- // Поместить № команды в стек
- s.push(pos);
- // Иначе, если это команда конца цикла, то:
- } else if (c == ']') {
- // Проверить, что стек не пуст (иначе ошибка в программе)
- assert (!s.empty());
- // Запомнить границы найденного цикла
- // (одна граница - текущая команда, другая - в стеке)
- loopRanges[s.top()] = pos;
- loopRanges[pos] = s.top();
- // Вытолкнуть № команды из стека
- s.pop();
- }
- // Увеличить № команды
- ++pos;
- }
- }
- // Определить границы подпрограмм в программе (аналогично getLoopRanges)
- void BFMachine::getSubRanges() {
- std::stack<size_t> s;
- size_t pos = 0;
- for (const char& c : program) {
- if (c == '(') {
- s.push(pos);
- } else if (c == ')') {
- assert (!s.empty());
- subRanges[s.top()] = pos;
- subRanges[pos] = s.top();
- s.pop();
- }
- ++pos;
- }
- }
- // Запустить Brainfuck-машину (выполнить программу)
- void BFMachine::run() {
- // Установить счётчик команд в 0.
- // Пока не конец программы, увеличивать счётчик на каждом такте
- for (ip = 0; ip < program.size(); ++ip) {
- // Получить очередную команду
- char c = program[ip];
- // Выполнить действие, соответствующее команде
- switch (c) {
- case '.':
- printCell();
- break;
- case ',':
- inputCell();
- break;
- case '>':
- nextCell();
- break;
- case '<':
- prevCell();
- break;
- case '+':
- incCell();
- break;
- case '-':
- decCell();
- break;
- case '[':
- beginLoop();
- break;
- case ']':
- endLoop();
- break;
- case '(':
- declareSub();
- break;
- case ')':
- returnSub();
- break;
- case ':':
- goSub();
- break;
- }
- }
- }
- // Определить подпрограмму с номером, хранящимся в ячейке ленты,
- // на которую сейчас указывает головка
- void BFMachine::declareSub() {
- // Зарегистрировать подпрограмму № tape[head]
- // Если подпрограмма с таким номером уже есть, то обновить запись
- subRegistry[tape[head]] = ip;
- // Переставить ip на )
- ip = getEndOfSub(ip);
- }
- // Вызвать подпрограмму с номером, хранящимся в ячейке ленты,
- // на которую сейчас указывает головка
- void BFMachine::goSub() {
- // Предусловие: подпрограмма № tape[head] зарегистрирована
- assert (subRegistry.count(tape[head]) > 0);
- // Сохранить в стеке вызовов текущее значение ip
- callStack.push(ip);
- // Перевести ip на начало подпрограммы № tape[head]
- ip = subRegistry[tape[head]];
- }
- // Выйти из подпрограммы в точку вызова
- void BFMachine::returnSub() {
- // Предусловие: стек вызовов не пуст
- assert (!callStack.empty());
- // Извлечь из стека вызовов ip
- ip = callStack.top();
- callStack.pop();
- }
- // Напечатать значение ячейки, на которую установлена головка
- void BFMachine::printCell() const {
- // Вывести значение ячейки на экран
- std::cout << tape[head];
- }
- // Ввести значение ячейки, на которую установлена головка, с клавиатуры
- void BFMachine::inputCell() {
- char in;
- std::cin >> in;
- tape[head] = in;
- }
- // Увеличить значение ячейки, на которую установлена головка, на 1
- void BFMachine::incCell() {
- ++tape[head];
- }
- // Уменьшить значение ячейки, на которую установлена головка, на 1
- void BFMachine::decCell() {
- --tape[head];
- }
- // Переставить головку на следующую ячейку ленты
- void BFMachine::nextCell() {
- if (isHeadOnLastCell()) {
- // Если была последняя ячейка, то переставить головку на 1-ю
- head = 0;
- } else {
- // Иначе переставить головку на следующую ячейку
- ++head;
- }
- }
- // Переставить головку на предыдущую ячейку ленты
- void BFMachine::prevCell() {
- if (isHeadOnFirstCell()) {
- // Если была первая ячейка, то переставить головку на последнюю
- head = 0;
- } else {
- // Иначе переставить головку на предыдущую ячейку
- --head;
- }
- }
- // Начало тела цикла
- void BFMachine::beginLoop() {
- if (tape[head] == '\0') {
- // Если значение текущей ячейки ленты равно нулю,
- // то выйти из цикла
- ip = getAnotherRangeOfLoop(ip);
- }
- }
- // Конец тела цикла
- void BFMachine::endLoop() {
- // Если значение текущей ячейки ленты не равно нулю,
- // то начать новую итерацию цикла
- if (tape[head] != '\0') {
- ip = getAnotherRangeOfLoop(ip);
- }
- }
- // Основная программа
- int main(int argc, char *argv[]) {
- // Если у программы есть параметры (кроме имени самой программы), то:
- if (argc > 1) {
- // Получить программу из 1-го параметра
- std::string c = argv[1];
- // Создать Brainfuck-машину с этой программой
- BFMachine bfm(c);
- // Запустить машину
- bfm.run();
- // Вывести пустую строку
- // (т.к. она часто не предусмотрена в Brainfuck-программе)
- std::cout << std::endl;
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement