用C++实现一个Brainfuck解释器
> 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;
}
代码应该很好懂,逐个字符读入,同时忽略非命令的字符,处理到文件末尾为止。稍微复杂点的就是用到了map和stack来记录每一个括号对应的另外一半的位置。首先每遇到一个[就压进栈里,然后遇到]就从栈顶取出一个[来和它配对,这时候用map来记录他们的位置。
测试程序
输出"Hello World!" :
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
输出字符'A' :
++++++ [ > ++++++++++ < - ] > +++++ .
把小写字母转换成大写,按回车结束:
,----------[----------------------.,----------]
提示:在控制台输入完指令后按回车,然后按ctrl+z可以模拟EOF
效果
如有BUG欢迎指出~
用C++实现一个Brainfuck解释器的更多相关文章
- Licp - 一个玩具解释器的实现
纸上得来终觉浅,绝知此事要躬行. 最近看了 SICP,其第四章讲述了一个简单的 Scheme 解释器的实现.粗看了一遍后决定自己用 C 语言实现一个残疾的 Scheme 解释器,想来这样的学习效果应该 ...
- 打包一个python解释器
利用python的exec语句,可以很方便地动态执行python语句.如果一个python代码打包为了exe,其原先的代码就很难更改了.一个好的解决方法就是import相应的库,然后把主程序段放到一个 ...
- 前端与编译原理——用JS写一个JS解释器
说起编译原理,印象往往只停留在本科时那些枯燥的课程和晦涩的概念.作为前端开发者,编译原理似乎离我们很远,对它的理解很可能仅仅局限于"抽象语法树(AST)".但这仅仅是个开头而已.编 ...
- brainfuck 解释器
#include <cstdio>#include <cmath>#include <cstring>#include <ctime>#include ...
- pwnable.kr之brainf*ck
pwnable.kr之brainf*ck 今天又是被难倒的一天Orz,个人感觉pwnable.kr上的题都比较剑走偏锋,仔细做过去,一定会有很大的收获. 不多说了,今天看的是第二关的第一道题:brai ...
- [开源项目]Shell4Win,一个在Windows下执行shell命令的解释器
背景 顺利拿到心目中的理想offer之后,心里的负担一下减轻了很多,希望利用还没毕业之前这段难得的悠闲时间做一点有意义的事情.于是希望能做一个长久以来都想做的开源项目,就是题中提到的Windows下的 ...
- shell脚本就是由Shell命令组成的执行文件,将一些命令整合到一个文件中,进行处理业务逻辑,脚本不用编译即可运行。它通过解释器解释运行,所以速度相对来说比较慢。
shell脚本?在说什么是shell脚本之前,先说说什么是shell. shell是外壳的意思,就是操作系统的外壳.我们可以通过shell命令来操作和控制操作系统,比如Linux中的Shell命令就包 ...
- 以鶸ice为例,手撸一个解释器(一)明确目标
代码地址 # HelloWorld.ice print("hello, world") 前言(废话) 其实从开始学习编译原理到现在已经有快半年的时间了,但是其间常常不能坚持看下去龙 ...
- 一门能让你五分钟学会的语言-Brainfuck
看到标题,不出意外的话,你肯定开始骂我了:**标题党,什么编程语言五分钟就能学会? 其实我本来也是不相信的,但是学过了才知道这是真的. 1.Brainfuck 看到这个小标题,不要误会,我没有骂人. ...
随机推荐
- Oracle EBS GL 总账日记账打开报错此职责无可用函数
系统管理员下,跑请求:
- js实现双向链表
1.概念 上一个文章里我们已经了解到链表结构,链表的特点是长度不固定,不用担心插入新元素的时候新增位置的问题.插入一个元素的时候,只要找到插入点就可以了,不需要整体移动整个结构. 这里我们了解一下双向 ...
- 关于QT的QCombox的掉坑出坑
最近项目中开发在用到QCombox,然而在开发中,踩到了一个坑,花了一个晚上,一直在想,好在最后找到问题所在了. 这是业务的流程.直接说重点:QCombox在下拉窗更新数据的时候,会默认把下拉窗的第一 ...
- MySQL基础值 存储过程和函数
一.创建存储过程和函数 什么是创建存储过程和函数? 就是将经常使用的一组SQL语句组合在一起,并将这些SQL语句当做一个整体存储在MYSQL服务器中. 创建存储过程的语句是:CREATE PROCE ...
- def chi(*food,**kw):
def chi(*food,**kw): print(food,kw)chi("cong","蒜",'姜','可乐',"J=Juice",a ...
- 【Alpha 冲刺】 6/12
今日任务总结 人员 今日原定任务 完成情况 遇到问题 贡献值 胡武成 建立数据库 未完成 表结构文档已设计好,服务器mysql刚配置完成,但是,SpringMVC框架还没有熟络,不清楚如何使用该框架去 ...
- PyQt5--QLineEdit
# -*- coding:utf-8 -*- ''' Created on Sep 20, 2018 @author: SaShuangYiBing Comment: ''' import sys f ...
- 【Ansible 文档】【译文】主机清单文件
Inventory 主机清单文件 Ansible 可以对你的基础设施中多个主机系统同时进行操作.通过选择在Ansible的inventory列出的一部分主机来实现.inventory默认保存在/etc ...
- jQuery 1.11 / 2.1 beta 版发布
jQuery开发团队近日发布了jQuery 1.11和2.1的beta版本. jQuery 1.x版本支持IE 6/7/8,jQuery 2.x 不支持,如果你已经抛弃了IE 6/7/8用户,可以升级 ...
- 基于汇编的 C/C++ 协程 - 背景知识
近几年来,协程在 C/C++ 服务器中的解决方案开始涌现.本文主要阐述以汇编实现上下文切换的协程方案,并且说明其在异步开发模式中的应用. 本文地址:https://segmentfault.com/a ...