> Brainfuck是一种极小化的计算机语言,只含有8种运算符,由于fuck在英语中是脏话,这种语言有时被称为brainf*ck或brainf***,甚至被简称为BF。正如它的名字所暗示,brainfuck程序很难读懂,尽管如此,brainfuck却是图灵完备的,也就是说它能够完成所有可计算的任务。
- - -
# 简介
以下摘自[维基百科](https://zh.wikipedia.org/wiki/Brainfuck)。

Müller的目标是创建一种简单的、可以用最小的编译器来实现的、符合图灵完全思想的编程语言。这种语言由八种运算符构成,为Amiga机器编写的编译器(第二版)只有240个字节大小。

这种语言基于一个简单的机器模型,除了指令,这个机器还包括:一个以字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。

下面是这八种状态的描述,其中每个状态由一个字符标识:(博客园的markdown居然不支持表格,我实在是...)

如果把这些指令翻译成C语言就是下面这样的:


示例程序

所以,Brainfuck的程序就是长这样的:

  • ,. 这段代码的意思是,从键盘读取一个字符并输出到屏幕
  • ,>++++++++[<-->-]<-. 这也是我自己写的第一个BrainFuck程序啊哈哈,功能是,从键盘读取一个大写字母,然后转化成对应的数字,#比如A就输出0,B输出1。

代码解释

解释下,>++++++++[<-->-]<-.

首先,读取一个大写字母放到[0]里,然后把指针指向[1],接下来把[1]里的值增加8次,也就是变成8。

然后进入循环,指针左移一位,指向[0],把[0]的值减两次,又右移一位回到[1],把[1]的值减少1。因为一开始[1]被赋值为8,所以循环一共能执行8次,因此[0]里的值会被减少8 * 2 = 16,最后[1]变成了0,循环不再执行,这时候指针指向的是[1],将其左移一位指向[0],然后将[0]的值减1,所以[0]的值一共减少了16 + 1 = 17此,这刚好是‘A’的ACSII码与'0'的ACSII码的差值

是不是很好玩?

不如写个解释器更好玩。


解释器

  1. #include <iostream>
  2. #include <stack>
  3. #include <cstdio>
  4. #include <map>
  5. using namespace std;
  6. const int SIZE = 300000;
  7. bool is_instruction(char);
  8. int main(void)
  9. {
  10. while(1)
  11. {
  12. char instruction[SIZE];
  13. char ch;
  14. int count = 0;
  15. while((ch = getchar()) != EOF) //读取指令,忽略空格回车等非命令字符
  16. if(is_instruction(ch))
  17. instruction[count ++] = ch;
  18. instruction[count] = '\0';
  19. stack<int> left_bracket_stack;
  20. map<int,int> another_bracket_at;
  21. bool instruction_ok = true;
  22. for(int i = 0;instruction[i] != '\0';i ++) //检查代码是否有误,同时匹配括号
  23. {
  24. if(instruction[i] == '[')
  25. left_bracket_stack.push(i);
  26. else if(instruction[i] == ']')
  27. {
  28. if(left_bracket_stack.empty())
  29. {
  30. instruction_ok = false;
  31. break;
  32. }
  33. int left_barcket_index = left_bracket_stack.top();;
  34. left_bracket_stack.pop();
  35. another_bracket_at[i] = left_barcket_index;
  36. another_bracket_at[left_barcket_index] = i;
  37. }
  38. }
  39. if(!left_bracket_stack.empty())
  40. instruction_ok = false;
  41. if(!instruction_ok) //如果括号不匹配则输出错误
  42. {
  43. puts("代码有误");
  44. continue;
  45. }
  46. cout << endl << "***** BEGIN *****" << endl << endl;
  47. int i = 0;
  48. char box[SIZE] = {0};
  49. char * cur = box;
  50. while(instruction[i] != '\0')
  51. {
  52. if(instruction[i] == '>')
  53. cur ++;
  54. else if(instruction[i] == '<')
  55. {
  56. cur --;
  57. if(cur < box) //如果操作会导致数组越界就报错
  58. {
  59. puts("代码有误");
  60. break;
  61. }
  62. }
  63. else if(instruction[i] == '+')
  64. ++ (*cur);
  65. else if(instruction[i] == '-')
  66. -- (*cur);
  67. else if(instruction[i] == '.')
  68. putchar(*cur);
  69. else if(instruction[i] == ',')
  70. *cur = getchar();
  71. else if(instruction[i] == '[')
  72. {
  73. if(*cur == 0)
  74. i = another_bracket_at[i];
  75. }
  76. else if(instruction[i] == ']')
  77. if(*cur)
  78. i = another_bracket_at[i];
  79. i ++;
  80. }
  81. cout << endl << endl << "***** DONE *****" << endl << endl;
  82. }
  83. return 0;
  84. }
  85. bool is_instruction(char ch)
  86. {
  87. if(ch == '>' || ch == '<' || ch == '+' || ch == '-' || ch == '.' || ch == ',' || ch == '[' || ch == ']')
  88. return true;
  89. return false;
  90. }

代码应该很好懂,逐个字符读入,同时忽略非命令的字符,处理到文件末尾为止。稍微复杂点的就是用到了mapstack来记录每一个括号对应的另外一半的位置。首先每遇到一个[就压进栈里,然后遇到]就从栈顶取出一个[来和它配对,这时候用map来记录他们的位置。

测试程序

输出"Hello World!" :

  1. ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
  2. >++.>+.+++++++..+++.>++.<<+++++++++++++++.
  3. >.+++.------.--------.>+.>.

输出字符'A' :

  1. ++++++ [ > ++++++++++ < - ] > +++++ .

把小写字母转换成大写,按回车结束:

  1. ,----------[----------------------.,----------]

提示:在控制台输入完指令后按回车,然后按ctrl+z可以模拟EOF


效果

如有BUG欢迎指出~

用C++实现一个Brainfuck解释器的更多相关文章

  1. Licp - 一个玩具解释器的实现

    纸上得来终觉浅,绝知此事要躬行. 最近看了 SICP,其第四章讲述了一个简单的 Scheme 解释器的实现.粗看了一遍后决定自己用 C 语言实现一个残疾的 Scheme 解释器,想来这样的学习效果应该 ...

  2. 打包一个python解释器

    利用python的exec语句,可以很方便地动态执行python语句.如果一个python代码打包为了exe,其原先的代码就很难更改了.一个好的解决方法就是import相应的库,然后把主程序段放到一个 ...

  3. 前端与编译原理——用JS写一个JS解释器

    说起编译原理,印象往往只停留在本科时那些枯燥的课程和晦涩的概念.作为前端开发者,编译原理似乎离我们很远,对它的理解很可能仅仅局限于"抽象语法树(AST)".但这仅仅是个开头而已.编 ...

  4. brainfuck 解释器

    #include <cstdio>#include <cmath>#include <cstring>#include <ctime>#include ...

  5. pwnable.kr之brainf*ck

    pwnable.kr之brainf*ck 今天又是被难倒的一天Orz,个人感觉pwnable.kr上的题都比较剑走偏锋,仔细做过去,一定会有很大的收获. 不多说了,今天看的是第二关的第一道题:brai ...

  6. [开源项目]Shell4Win,一个在Windows下执行shell命令的解释器

    背景 顺利拿到心目中的理想offer之后,心里的负担一下减轻了很多,希望利用还没毕业之前这段难得的悠闲时间做一点有意义的事情.于是希望能做一个长久以来都想做的开源项目,就是题中提到的Windows下的 ...

  7. shell脚本就是由Shell命令组成的执行文件,将一些命令整合到一个文件中,进行处理业务逻辑,脚本不用编译即可运行。它通过解释器解释运行,所以速度相对来说比较慢。

    shell脚本?在说什么是shell脚本之前,先说说什么是shell. shell是外壳的意思,就是操作系统的外壳.我们可以通过shell命令来操作和控制操作系统,比如Linux中的Shell命令就包 ...

  8. 以鶸ice为例,手撸一个解释器(一)明确目标

    代码地址 # HelloWorld.ice print("hello, world") 前言(废话) 其实从开始学习编译原理到现在已经有快半年的时间了,但是其间常常不能坚持看下去龙 ...

  9. 一门能让你五分钟学会的语言-Brainfuck

    看到标题,不出意外的话,你肯定开始骂我了:**标题党,什么编程语言五分钟就能学会? 其实我本来也是不相信的,但是学过了才知道这是真的. 1.Brainfuck 看到这个小标题,不要误会,我没有骂人. ...

随机推荐

  1. 详解JNDI的lookup资源引用java:/comp/env

    ENC的概念:     The application component environment is referred to as the ENC, the enterprise naming c ...

  2. 解决:Determining IP Information for eth0...问题

    环境:Centos 6.2     VMWare Workstation 7.1.2  故障现象: 在虚拟机中启动Centos,在启动页面中停留在Determining IP Information ...

  3. Linux命令(自学)

    1.立刻关机: shutdown -h now 2.立刻重启: shutdown -r now reboot 3.注销: logout 4.进入vi编辑器,写一个hello的java程序: vi he ...

  4. sysbench安装、使用、结果解读

    sysbench是一个模块化的.跨平台.多线程基准测试工具,主要用于评估测试各种不同系统参数下的数据库负载情况.目前sysbench代码托管在github上,项目地址:https://github.c ...

  5. Huawei vlan 配置及vlan 间通讯

    Huawei Vlan配置及vlan 间通讯实例 组网需求:汇聚层交换机做为 PC 电脑的网关, PC3直连 SW2 属于 vlan 2,网关为 vlanif 2 接口地址192.168.2.1/24 ...

  6. rsync 故障排查整理

    Rsync服务常见问题汇总 ================================================================== 1 客户端的错误现象:No route ...

  7. leetcode 121 买卖股票的最佳时机

    题目 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润. 注意你不能在买入股票前卖出股票. ...

  8. PyQt5--ShowTips

    # -*- coding:utf-8 -*- ''' Created on Sep 13, 2018 @author: SaShuangYiBing ''' import sys from PyQt5 ...

  9. JAVA引用的种类

    最近在进行Java项目开发的时候,由于业务的原因,有时候new的对象会比较多,这个时候我总是有一个疑惑?那就是JVM在何时决定回收一个Java对象所占据的内存?这个问题其实对整个web系统来说是一个比 ...

  10. Effective MySQL之SQL语句最优化——读书笔记之二

    第二章,基本的分析命令 本章简单介绍了如下几个基本的MySQL分析命令: EXPLAIN命令 生成QEP不是确定的 QEP不会绑定给一个SQL或者存储过程,而是在执行的时候根据实际情况生成 可以通过Q ...