Advertisement
Guest User

Brainfuck/Pbrain interpreter

a guest
Nov 24th, 2017
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.00 KB | None | 0 0
  1. //  ____            _        __            _
  2. // |  _ \          (_)      / _|          | |
  3. // | |_) |_ __ __ _ _ _ __ | |_ _   _  ___| | __
  4. // |  _ <| '__/ _` | | '_ \|  _| | | |/ __| |/ /
  5. // | |_) | | | (_| | | | | | | | |_| | (__|   <
  6. // |____/|_|  \__,_|_|_| |_|_|  \__,_|\___|_|\_|
  7. //
  8. // Интерпретатор языка Brainfuck с процедурным расширением Pbrain
  9. // на языке С++ (стандарт ISO/IEC 14882:2011)
  10. //
  11. // Автор программы: Д.А.Куценко, 2017
  12. // Программа распространяется по лицензии GNU GPLv3
  13. // См. http://www.gnu.org/licenses/
  14. //
  15. // Компиляция: g++ -std=c++11 -o bf main.cpp
  16. //
  17. // Запуск: ./bf '<текст_программы_на_языке_Brainfuck_или_Pbrain>'
  18. //
  19. // Ссылки на описания языков Brainfuck и Pbrain:
  20. // https://esolangs.org/wiki/Brainfuck
  21. // https://esolangs.org/wiki/Pbrain
  22.  
  23. #include <iostream>
  24. #include <cassert>
  25. #include <string>
  26. #include <vector>
  27. #include <map>
  28. #include <stack>
  29. #include <cctype>
  30.  
  31. // Класс, описывающий Brainfuck-машину
  32. class BFMachine {
  33.     public:
  34.         // Создать машину для указанной программы
  35.         BFMachine(const std::string &newProg):
  36.             program(newProg),
  37.             tapeSize(30000),
  38.             tape(tapeSize, '\0'),
  39.             head(0),
  40.             ip(0)
  41.         {
  42.             // Определить границы циклов в программе
  43.             getLoopRanges();
  44.             // Определить границы подпрограмм в программе
  45.             getSubRanges();
  46.         }
  47.         // Выполнить программу
  48.         void run();
  49.     private:
  50.         // Определить границы циклов в программе
  51.         void getLoopRanges();
  52.         // Определить границы подпрограмм в программе
  53.         void getSubRanges();
  54.         // Выдать соответствующую данной другую границу подпрограммы
  55.         size_t getEndOfSub(size_t pos) const {
  56.             assert (subRanges.count(pos) > 0);
  57.             return subRanges.at(pos);
  58.         }
  59.         // Выдать соответствующую данной другую границу цикла
  60.         size_t getAnotherRangeOfLoop(size_t pos) const {
  61.             assert (loopRanges.count(pos) > 0);
  62.             return loopRanges.at(pos);
  63.         }
  64.         // Напечатать содержимое ячейки ленты
  65.         void printCell() const;
  66.         // Ввести с клавиатуры содержимое ячейки ленты
  67.         void inputCell();
  68.         // Увеличить значение ячейки ленты на 1
  69.         void incCell();
  70.         // Уменьшить значение ячейки ленты на 1
  71.         void decCell();
  72.         // Перейти на следующую ячейку ленты
  73.         void nextCell();
  74.         // Перейти на предыдущую ячейку ленты
  75.         void prevCell();
  76.         // Начало тела цикла
  77.         void beginLoop();
  78.         // Конец тела цикла
  79.         void endLoop();
  80.         // Определить подпрограмму
  81.         void declareSub();
  82.         // Перейти в подпрограмму
  83.         void goSub();
  84.         // Выйти из подпрограммы
  85.         void returnSub();
  86.         // Головка находится на ленте?
  87.         bool isHeadOnTape() const {
  88.             return (head >= 0) && (head < tapeSize);
  89.         }
  90.         // Головка указывает на первую ячейку ленты?
  91.         bool isHeadOnFirstCell() const {
  92.             return head == 0;
  93.         }
  94.         // Головка указывает на последнюю ячейку ленты?
  95.         bool isHeadOnLastCell() const {
  96.             return head == tapeSize - 1;
  97.         }
  98.         // Программа (строка команд)
  99.         const std::string program;
  100.         // Размер ленты
  101.         const size_t tapeSize;
  102.         // Лента
  103.         std::vector<char> tape;
  104.         // Позиция головки
  105.         size_t head;
  106.         // Номер команды в программе
  107.         size_t ip;
  108.         // Пары границ циклов в программе
  109.         std::map<size_t, size_t> loopRanges;
  110.         // Стек вызовов для подпрограмм
  111.         std::stack<size_t> callStack;
  112.         // Определения подпрограмм
  113.         std::map<char, size_t> subRegistry;
  114.         // Пары границ подпрограмм в программе
  115.         std::map<size_t, size_t> subRanges;
  116. };
  117.  
  118. // Определить границы циклов в программе
  119. void BFMachine::getLoopRanges() {
  120.     // Стек для корректного определения вложенности циклов
  121.     std::stack<size_t> s;
  122.     // Номер команды в программе
  123.     size_t pos = 0;
  124.     // Для каждой команды в программе:
  125.     for (const char& c : program) {
  126.         // Если это команда начала цикла, то:
  127.         if (c == '[') {
  128.             // Поместить № команды в стек
  129.             s.push(pos);
  130.         // Иначе, если это команда конца цикла, то:
  131.         } else if (c == ']') {
  132.             // Проверить, что стек не пуст (иначе ошибка в программе)
  133.             assert (!s.empty());
  134.             // Запомнить границы найденного цикла
  135.             // (одна граница - текущая команда, другая - в стеке)
  136.             loopRanges[s.top()] = pos;
  137.             loopRanges[pos] = s.top();
  138.             // Вытолкнуть № команды из стека
  139.             s.pop();
  140.         }
  141.         // Увеличить № команды
  142.         ++pos;
  143.     }
  144. }
  145.  
  146. // Определить границы подпрограмм в программе (аналогично getLoopRanges)
  147. void BFMachine::getSubRanges() {
  148.     std::stack<size_t> s;
  149.     size_t pos = 0;
  150.     for (const char& c : program) {
  151.         if (c == '(') {
  152.             s.push(pos);
  153.         } else if (c == ')') {
  154.             assert (!s.empty());
  155.             subRanges[s.top()] = pos;
  156.             subRanges[pos] = s.top();
  157.             s.pop();
  158.         }
  159.         ++pos;
  160.     }
  161. }
  162.  
  163. // Запустить Brainfuck-машину (выполнить программу)
  164. void BFMachine::run() {
  165.     // Установить счётчик команд в 0.
  166.     // Пока не конец программы, увеличивать счётчик на каждом такте
  167.     for (ip = 0; ip < program.size(); ++ip) {
  168.         // Получить очередную команду
  169.         char c = program[ip];
  170.         // Выполнить действие, соответствующее команде
  171.         switch (c) {
  172.             case '.':
  173.                 printCell();
  174.                 break;
  175.             case ',':
  176.                 inputCell();
  177.                 break;
  178.             case '>':
  179.                 nextCell();
  180.                 break;
  181.             case '<':
  182.                 prevCell();
  183.                 break;
  184.             case '+':
  185.                 incCell();
  186.                 break;
  187.             case '-':
  188.                 decCell();
  189.                 break;
  190.             case '[':
  191.                 beginLoop();
  192.                 break;
  193.             case ']':
  194.                 endLoop();
  195.                 break;
  196.             case '(':
  197.                 declareSub();
  198.                 break;
  199.             case ')':
  200.                 returnSub();
  201.                 break;
  202.             case ':':
  203.                 goSub();
  204.                 break;
  205.         }
  206.     }
  207. }
  208.  
  209. // Определить подпрограмму с номером, хранящимся в ячейке ленты,
  210. // на которую сейчас указывает головка
  211. void BFMachine::declareSub() {
  212.     // Зарегистрировать подпрограмму № tape[head]
  213.     // Если подпрограмма с таким номером уже есть, то обновить запись
  214.     subRegistry[tape[head]] = ip;
  215.     // Переставить ip на )
  216.     ip = getEndOfSub(ip);
  217. }
  218.  
  219. // Вызвать подпрограмму с номером, хранящимся в ячейке ленты,
  220. // на которую сейчас указывает головка
  221. void BFMachine::goSub() {
  222.     // Предусловие: подпрограмма № tape[head] зарегистрирована
  223.     assert (subRegistry.count(tape[head]) > 0);
  224.     // Сохранить в стеке вызовов текущее значение ip
  225.     callStack.push(ip);
  226.     // Перевести ip на начало подпрограммы № tape[head]
  227.     ip = subRegistry[tape[head]];
  228. }
  229.  
  230. // Выйти из подпрограммы в точку вызова
  231. void BFMachine::returnSub() {
  232.     // Предусловие: стек вызовов не пуст
  233.     assert (!callStack.empty());
  234.     // Извлечь из стека вызовов ip
  235.     ip = callStack.top();
  236.     callStack.pop();
  237. }
  238.  
  239. // Напечатать значение ячейки, на которую установлена головка
  240. void BFMachine::printCell() const {
  241.     // Вывести значение ячейки на экран
  242.     std::cout << tape[head];
  243. }
  244.  
  245. // Ввести значение ячейки, на которую установлена головка, с клавиатуры
  246. void BFMachine::inputCell() {
  247.     char in;
  248.     std::cin >> in;
  249.     tape[head] = in;
  250. }
  251.  
  252. // Увеличить значение ячейки, на которую установлена головка, на 1
  253. void BFMachine::incCell() {
  254.     ++tape[head];
  255. }
  256.  
  257. // Уменьшить значение ячейки, на которую установлена головка, на 1
  258. void BFMachine::decCell() {
  259.     --tape[head];
  260. }
  261.  
  262. // Переставить головку на следующую ячейку ленты
  263. void BFMachine::nextCell() {
  264.     if (isHeadOnLastCell()) {
  265.         // Если была последняя ячейка, то переставить головку на 1-ю
  266.         head = 0;
  267.     } else {
  268.         // Иначе переставить головку на следующую ячейку
  269.         ++head;
  270.     }
  271. }
  272.  
  273. // Переставить головку на предыдущую ячейку ленты
  274. void BFMachine::prevCell() {
  275.     if (isHeadOnFirstCell()) {
  276.         // Если была первая ячейка, то переставить головку на последнюю
  277.         head = 0;
  278.     } else {
  279.         // Иначе переставить головку на предыдущую ячейку
  280.         --head;
  281.     }
  282. }
  283.  
  284. // Начало тела цикла
  285. void BFMachine::beginLoop() {
  286.     if (tape[head] == '\0') {
  287.         // Если значение текущей ячейки ленты равно нулю,
  288.         // то выйти из цикла
  289.         ip = getAnotherRangeOfLoop(ip);
  290.     }
  291. }
  292.  
  293. // Конец тела цикла
  294. void BFMachine::endLoop() {
  295.     // Если значение текущей ячейки ленты не равно нулю,
  296.     // то начать новую итерацию цикла
  297.     if (tape[head] != '\0') {
  298.         ip = getAnotherRangeOfLoop(ip);
  299.     }
  300. }
  301.  
  302. // Основная программа
  303. int main(int argc, char *argv[]) {
  304.     // Если у программы есть параметры (кроме имени самой программы), то:
  305.     if (argc > 1) {
  306.         // Получить программу из 1-го параметра
  307.         std::string c = argv[1];
  308.         // Создать Brainfuck-машину с этой программой
  309.         BFMachine bfm(c);
  310.         // Запустить машину
  311.         bfm.run();
  312.         // Вывести пустую строку
  313.         // (т.к. она часто не предусмотрена в Brainfuck-программе)
  314.         std::cout << std::endl;
  315.     }
  316.     return 0;
  317. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement