> 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码的差值

是不是很好玩?

不如写个解释器更好玩。


解释器

#include <iostream>
#include <stack>
#include <cstdio>
#include <map>
using namespace std; const int SIZE = 300000; bool is_instruction(char);
int main(void)
{ while(1)
{
char instruction[SIZE];
char ch;
int count = 0;
while((ch = getchar()) != EOF) //读取指令,忽略空格回车等非命令字符
if(is_instruction(ch))
instruction[count ++] = ch;
instruction[count] = '\0'; stack<int> left_bracket_stack;
map<int,int> another_bracket_at;
bool instruction_ok = true; for(int i = 0;instruction[i] != '\0';i ++) //检查代码是否有误,同时匹配括号
{
if(instruction[i] == '[')
left_bracket_stack.push(i);
else if(instruction[i] == ']')
{
if(left_bracket_stack.empty())
{
instruction_ok = false;
break;
}
int left_barcket_index = left_bracket_stack.top();;
left_bracket_stack.pop();
another_bracket_at[i] = left_barcket_index;
another_bracket_at[left_barcket_index] = i;
}
}
if(!left_bracket_stack.empty())
instruction_ok = false;
if(!instruction_ok) //如果括号不匹配则输出错误
{
puts("代码有误");
continue;
} cout << endl << "***** BEGIN *****" << endl << endl; int i = 0;
char box[SIZE] = {0};
char * cur = box;
while(instruction[i] != '\0')
{
if(instruction[i] == '>')
cur ++;
else if(instruction[i] == '<')
{
cur --;
if(cur < box) //如果操作会导致数组越界就报错
{
puts("代码有误");
break;
}
}
else if(instruction[i] == '+')
++ (*cur);
else if(instruction[i] == '-')
-- (*cur);
else if(instruction[i] == '.')
putchar(*cur);
else if(instruction[i] == ',')
*cur = getchar();
else if(instruction[i] == '[')
{
if(*cur == 0)
i = another_bracket_at[i];
}
else if(instruction[i] == ']')
if(*cur)
i = another_bracket_at[i];
i ++;
} cout << endl << endl << "***** DONE *****" << endl << endl;
} return 0;
} bool is_instruction(char ch)
{
if(ch == '>' || ch == '<' || ch == '+' || ch == '-' || ch == '.' || ch == ',' || ch == '[' || ch == ']')
return true;
return false;
}

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

测试程序

输出"Hello World!" :

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

输出字符'A' :

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

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

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

提示:在控制台输入完指令后按回车,然后按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. M5加密字符串

    private string GetMD5str(string oldStr) { //将输入转换为ASCII 字符编码 ASCIIEncoding enc = new ASCIIEncoding() ...

  2. Oracle EBS OPM close batch

    --close_batch --created by jenrry DECLARE x_message_count NUMBER; x_message_list VARCHAR2 (4000); x_ ...

  3. SQL Server 跨网段(跨机房)通过备份文件初始化复制

    笔者最近碰到了需要搭建跨网段的SQL Server复制,实际的拓扑结构如下草图所示: 发布端A服务器位于CDC机房中 订阅端B服务器位于阿里云 因为SQL Server复制不支持通过IP连接分发服务器 ...

  4. postgresql排序分页时数据重复问题

    当同时排序又分页时,如果排序的字段X不是唯一字段,当多个记录的X字段有同一个值时顺序是随机的. 这个有可能造成分页时数据重复的问题.某一页又把上一页的数据查出来了,其实数据库只有一条记录. 解决办法: ...

  5. Linux内存寻址之分段机制及分页机制【转】

    前言 本文涉及的硬件平台是X86,如果是其他平台的话,如ARM,是会使用到MMU,但是没有使用到分段机制: 最近在学习Linux内核,读到<深入理解Linux内核>的内存寻址一章.原本以为 ...

  6. 【转】Nginx学习---负载均衡的原理、分类、实现架构,以及使用场景

    [原文]https://www.toutiao.com/i6593604356799463944/ [原文]https://www.toutiao.com/i6592741060194075143/ ...

  7. Servlet 核心接口

    在Servlet体系结构中,除了用于实现Servlet的Servlet接口.GenericServlet类和HttpServlet类外,还有一些辅助Servlet获取相关资源信息的重要接口,了解这些接 ...

  8. 团队作业8--测试与发布(Beta阶段)

    展示博客 一.项目成员: 张慧敏(组长)201421122032 苏晓薇(组员)201421031033 欧阳时康(组员)201421122050 团队仓库: https://git.coding.n ...

  9. Django基础与组件

    第一章:Django系列之web应用与http协议 第二章:基于wsgiref模块DIY一个web框架 第三章:Django下载与简介 第四章:url控制系统 第五章:视图 第六章:Django模板语 ...

  10. [转]OpenCV2.4.12 开启OpenGL启用三维可视化支持

    OpenCV默认情况下是不支持OpenGL的,如果要使OpenCV支持OpenGL,则需要重编译,具体步骤如下: 注意事项:从The OpenCV Reference ManualOpenCV参考手册 ...