> 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. Android应用耗电量统计,无需USB连接

    Android应用耗电量统计一直是一个很头疼的问题,手工统计耗时太长,自动化统计又不是非常精准(执行自动化代码需要通过USB连接,而USB又会充电,这就造成统计数据不准).后来从前辈那里得知可以通过a ...

  2. pages bookmarks for machine learning domain

    http://www.ai-start.com/dl2017/html/lesson4-week2.html  达叔深度学习笔记 http://cs231n.github.io/convolution ...

  3. .NET笔试题集(五)

    转载于:http://www.cnblogs.com/ForEvErNoME/archive/2012/09/15/2684938.html 1.什么是受管制的代码? 答:unsafe:非托管代码.不 ...

  4. elasticsearch DSL查询

    总结一个DSL的用法吧,语法网上查去,只记录一点心得,也是研究了半天,太麻烦了 先附上python代码 #!/usr/bin/env python # _*_ coding:utf-8 _*_ fro ...

  5. 红帽7配置samba文件共享服务

    samba软件主要功能是为客户机提供共享使用的文件夹. 使用的协议是SMB(TCP 139).CIFS(TCP445). 所需的软件包:samba 系统服务:smb 1.安装samba服务 ~]#yu ...

  6. LabVIEW TCP/IP 断开重连问题

    LabVIEW的TCP/IP函数库非常好用,但是不恰当地设置打开连接结点的参数将带来一些问题,麻烦.如下图的打开连接的参数设置: 上图中指定了本地的端口,会发生这样的情况.当我们关闭应用程序之后,连接 ...

  7. Software Engineer’s path to the best annual performance review

    http://michaelscodingspot.com/2017/06/04/software-engineers-path-best-annual-performance-review/ How ...

  8. PHP类继承、接口继承关系概述

    PHP类继承: PHP类不支持多继承,也就是子类只能继承一个父类,但是支持多层次继承,比如: class frist{ public function __construct(){ echo &quo ...

  9. Rancher 添加主机无法显示、添加主机无效的解决办法

    在 Rancher UI 中,添加主机,在 Shell ssh 运行了,然后 点击 “关闭” 按钮,发现没有显示如何主机. 第一步,先去查看应用是否正常,就是 应用 - 全部应用 如果显示是 unhe ...

  10. DevExpress04、LayoutControl、GalleryControl

    首先需求是通过LayoutControl控件设计下图所示的窗体: 从该界面的设计过程 1.向窗体中添加LayoutControl控件 在将该控件拖入窗体后,最好立即设置该控件的尺寸和位置.拖入后,在如 ...