BF语言介绍
Brainfuck,是一种极小化的计算机语言,这种 语言,是一种按照"Turing complete(完整图灵机)"思想设计的语言,它的主要设计思路是:用最小的概念实现一种"简单"的语言,BrainFuck 语言只有八种符号,所有的操作都由这八种符号的组合来完成。BF基于一个简单的机器模型,除了八个指令,这个机器还包括:一个以字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。
下面是这八种指令的描述,其中每个指令由一个字符标识:
字符 | 含义 | 用C语言表示 |
> | 指针加一 | ++ptr; |
< | 指针减一 | --ptr; |
+ | 指针指向的字节的值加一 | ++*ptr; |
- | 指针指向的字节的值减一 | --*ptr; |
. | 输出指针指向的单元内容(ASCII码) | putchar(*ptr); |
, | 输入内容到指针指向的单元(ASCII码) | *ptr =getchar(); |
[ | 如果指针指向的单元值为零,向后跳转到对应的]指令的次一指令处 | while (*ptr) { |
] | 如果指针指向的单元值不为零,向前跳转到对应的[指令的次一指令处 | } |
提供一个简单的HelloWord程序
1 ++++++++++[>+++++++>++++++++++>+++>+<<<<-] 2 >++.>+.+++++++..+++.>++.<<+++++++++++++++. 3 >.+++.------.--------.>+.>.
下面提供一个BF的解析器,用C语言模拟的。
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int main(int argc,char *argv[]) 6 { 7 FILE * rfp; 8 FILE * wfp; 9 char filename[64]; 10 char exec[128]; 11 char ch; 12 if(argc!=2) 13 { 14 printf("请输入brainfuck源文件:"); 15 scanf("%s",filename); 16 } 17 else 18 { 19 strcpy(filename,argv[1]); 20 } 21 rfp=fopen(filename,"r"); 22 strcat(filename,".c"); 23 wfp=fopen(filename,"w"); 24 if(rfp==NULL || wfp==NULL) 25 { 26 printf("Can‘t open the file!\n"); 27 return -1; 28 } 29 fputs("#include <stdio.h>\n#include <string.h>\n int main(int argc,char *argv[])\n{",wfp); 30 fputs("char *ptr; \n char memory_tmp[0xffff];\n",wfp); 31 fputs("memset(memory_tmp,0,sizeof(memory_tmp));\nptr=memory_tmp;\n ",wfp); 32 33 while((ch=fgetc(rfp))!=EOF) 34 { 35 //printf("%c",ch); 36 switch(ch) 37 { 38 case ‘>‘: 39 fputs("++ptr;\n",wfp); 40 break; 41 case ‘<‘: 42 fputs("--ptr;\n",wfp); 43 break; 44 case ‘+‘: 45 fputs("++*ptr;\n",wfp); 46 break; 47 case ‘-‘: 48 fputs("--*ptr;\n",wfp); 49 break; 50 case ‘.‘: 51 fputs("putchar(*ptr);\n",wfp); 52 break; 53 case ‘,‘: 54 fputs("*ptr=getchar();\n",wfp); 55 break; 56 case ‘[‘: 57 fputs("while(*ptr)\n{\n",wfp); 58 break; 59 case ‘]‘: 60 fputs("}\n",wfp); 61 break; 62 default: 63 break; 64 } 65 } 66 fputs("\n return 0; \n }",wfp); 67 fclose(rfp); 68 fclose(wfp); 69 memset(exec,0,sizeof(exec)); 70 strcpy(exec,"gcc "); 71 strcat(exec,filename); 72 strcat(exec," -o tmp.tmp; rm "); 73 strcat(exec,filename); 74 strcat(exec,"; chmod 777 tmp.tmp;./tmp.tmp ; rm tmp.tmp;"); 75 //printf("运行的命令是:%s\n",exec); 76 printf("===============brainfuck运行结果=================\n"); 77 system(exec); 78 printf("=================================================\n"); 79 return 0; 80 }
下面对那个Hello World进行解释
1 +++ +++ +++ + initialize counter (cell #0) to 10 2 [ use loop to set the next four cells to 70/100/30/10 3 > +++ +++ + add 7 to cell #1 4 > +++ +++ +++ + add 10 to cell #2 5 > +++ add 3 to cell #3 6 > + add 1 to cell #4 7 <<< < - decrement counter (cell #0) 8 ] 9 >++ . print ‘H‘ 10 >+. print ‘e‘ 11 +++ +++ +. print ‘l‘ 12 . print ‘l‘ 13 +++ . print ‘o‘ 14 >++ . print ‘ ‘ 15 <<+ +++ +++ +++ +++ ++. print ‘W‘ 16 >. print ‘o‘ 17 +++ . print ‘r‘ 18 --- --- . print ‘l‘ 19 --- --- --. print ‘d‘ 20 >+. print ‘!‘ 21 >. print ‘\n‘
下面提供一个小程序用于把玩
++++++++++[>+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++>++++++++++++>+++++++++++++<<<<<<<<<<<<<-]>>>>>>>>>>++++.---->>----..++++<++.--<<<<<--.++<---.+++>>>>>>>-...+<<<<<<<----.++++>>>>>-.+>.<--.++>--.+++.-<+++.--->+++++.-----<<<<<<----.++++>>>>>-.+>+.--.+<<<<<<---.+++>>>>>>>-.+---.+++<.<---.+++>+.->++.--<<---.++++++++.-----<<<<<<<<<.+++.---
Piet语言
Piet 是一种非常深奥的编程语言,使用颜色编写代码。由David Morgan-Mar,其方案是位图,看起来像抽象艺术设计。编译指导图像周围移动,从一个连续颜色的区域下的一个“指针”。通过一个地区的指针退出时的程序进行。
由于原本的Piet语法比较复杂,我对其修改如下,使其比较简单和容易实现。
编号 | 指令 | 含义 | 颜色Red | 颜色Green | 颜色Blue |
1 | start | 表示一个代码段的开始 | 0 | 0 | 0 |
2 | end | 表示一个代码段的结束 | 2 | 2 | 2 |
3 | ++(plusplus) | 将寄存器中的数加1 | 0 | 0 | 1 |
4 | --(minusminus) | 将寄存器中的数减1 | 0 | 0 | 2 |
5 | push | 将寄存器中的数写入栈中 | 0 | 1 | 1 |
6 | pop | 将栈中的数读弹出并不存入寄存器中 | 0 | 1 | 2 |
7 | in | 控制台输入一个字符到寄存器 | 0 | 2 | 1 |
8 | out | 将寄存器的字符打印到控制台 | 0 | 2 | 2 |
9 | turn |
栈顶元素与寄存器比较,如果相等则代码方向指针向前 如果大于则代码方向指针向左,如果小于则代码方向指针向右 |
1 | 0 | 1 |
10 | zero | 将寄存器中的数置零 | 1 | 0 | 0 |
11 | call | 跳转指令,以寄存器为x,以栈顶数为y,跳到图像的(x,y)位置开始执行 | 1 | 1 | 1 |
12 | add | 栈顶元素与寄存器相加,并保存在寄存器中 | 2 | 0 | 0 |
13 | sub | 栈顶元素减去寄存器,差保存在寄存器中 | 2 | 0 | 1 |
14 | mul | 栈顶元素与寄存器相乘,并保存在寄存器中 | 2 | 1 | 0 |
15 | div | 栈顶元素除以寄存器,商保存在寄存器中 | 2 | 1 | 1 |
这个语言包含一个栈,还有一个寄存器,包含IO操作。十五个颜色的对应如下
我给出的程序是使用PPM格式的图片,彩色255颜色,以左上角为(0,0)右下角(sx,xy).
下面这个是用于编译的代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define START 1 6 #define END 2 7 #define PLUSPLUS 3 8 #define MINUSMINUS 4 9 #define PUSH 5 10 #define POP 6 11 #define IN 7 12 #define OUT 8 13 #define TURN 9 14 #define ZERO 10 15 #define CALL 11 16 #define ADD 12 17 #define SUB 13 18 #define MUL 14 19 #define DIV 15 20 21 struct IMG 22 { 23 int channel; 24 int sx,sy; 25 int maxv; 26 unsigned char * img; 27 }; 28 29 int ReadPPM(char * fname,struct IMG * img) 30 { 31 FILE * fp=NULL; 32 char ch; 33 fp=fopen(fname,"rb"); 34 if(fp==NULL) 35 { 36 perror("Can‘t open the file"); 37 exit(-1); 38 } 39 fscanf(fp,"%c%d%d%d%d%*c",&ch,&img->channel,&img->sx,&img->sy,&img->maxv); 40 if(img->channel==6) 41 { 42 img->img=(unsigned char *)malloc(img->sx*img->sy*3); 43 if(img->img==NULL) 44 { 45 perror("Can‘t malloc memory"); 46 exit(-1); 47 } 48 fread(img->img,img->sx*img->sy*3,1,fp); 49 } 50 fclose(fp); 51 return 0; 52 } 53 54 struct Dir 55 { 56 int x; 57 int y; 58 }; 59 60 int to3(int a) 61 { 62 if(a<=50) 63 return 0; 64 if(a>50&&a<150) 65 return 1; 66 if(a>=200) 67 return 2; 68 perror("图片格式模糊不便执行"); 69 } 70 71 int toinc(int r,int g,int b) 72 { 73 int i; 74 if(to3(r)==0 && to3(g)==0 &&to3(b)==0) 75 return START; 76 if(to3(r)==2 && to3(g)==2 &&to3(b)==2) 77 return END; 78 79 if(to3(r)==0 && to3(g)==0 &&to3(b)==1) 80 return PLUSPLUS; 81 if(to3(r)==0 && to3(g)==0 &&to3(b)==2) 82 return MINUSMINUS; 83 84 if(to3(r)==0 && to3(g)==1 &&to3(b)==1) 85 return PUSH; 86 if(to3(r)==0 && to3(g)==1 &&to3(b)==2) 87 return POP; 88 89 if(to3(r)==0 && to3(g)==2 &&to3(b)==1) 90 return IN; 91 if(to3(r)==0 && to3(g)==2 &&to3(b)==2) 92 return OUT; 93 94 if(to3(r)==1 && to3(g)==0 &&to3(b)==1) 95 return TURN; 96 if(to3(r)==1 && to3(g)==0 &&to3(b)==0) 97 return ZERO; 98 if(to3(r)==1 && to3(g)==1 &&to3(b)==1) 99 return CALL; 100 101 if(to3(r)==2 && to3(g)==0 &&to3(b)==0) 102 return ADD; 103 if(to3(r)==2 && to3(g)==0 &&to3(b)==1) 104 return SUB; 105 if(to3(r)==2 && to3(g)==1 &&to3(b)==0) 106 return MUL; 107 if(to3(r)==2 && to3(g)==1 &&to3(b)==1) 108 return DIV; 109 } 110 111 int decode(struct IMG * img) 112 { 113 struct Dir dir;//代码方向向量 114 struct Dir now;//表示当前代码指针 115 int stack[1024]; 116 char ch; 117 int inc;//保存指令 118 int ptr;//栈指针 119 int reg;//这个是寄存器 120 int tmp; 121 memset(stack,0,sizeof(stack)); 122 ptr=0;reg=0; 123 dir.x=1; 124 dir.y=0;//表示向前 125 now.x=0; 126 now.y=0; 127 inc=toinc(img->img[0],img->img[1],img->img[2]); 128 while(1) 129 { 130 tmp=now.y*img->sx*3+now.x*3; 131 inc=toinc(img->img[tmp],img->img[tmp+1],img->img[tmp+2]); 132 //printf("%d x=%d y=%d\n",inc,now.x,now.y); 133 //printf("此时reg=%d stack=%d\n",reg,stack[ptr-1]); 134 //getchar(); 135 switch(inc) 136 { 137 case START: 138 { 139 break; 140 } 141 case END: 142 { 143 return 0; 144 } 145 case PLUSPLUS: 146 { 147 reg++; 148 break; 149 } 150 case MINUSMINUS: 151 { 152 reg--; 153 break; 154 } 155 case PUSH: 156 { 157 stack[ptr]=reg; 158 ptr++; 159 break; 160 } 161 case POP: 162 { 163 ptr--; 164 break; 165 } 166 case IN: 167 { 168 fflush(stdin); 169 ch=getchar(); 170 reg=ch; 171 break; 172 } 173 case OUT: 174 { 175 printf("%c",reg); 176 break; 177 } 178 case TURN: 179 { 180 if(stack[ptr-1]>reg)//左转 181 { 182 //printf("左转\n"); 183 if(dir.x==1&&dir.y==0)//东 184 { 185 dir.x=0;dir.y=-1; 186 } 187 else if(dir.x==-1&&dir.y==0)//西 188 { 189 dir.x=0;dir.y=1; 190 } 191 else if(dir.x==0&&dir.y==1)//南 192 { 193 dir.x=1;dir.y=0; 194 } 195 else if(dir.x==0&&dir.y==-1)//北 196 { 197 dir.x=-1;dir.y=0; 198 } 199 } 200 else if(stack[ptr-1]<reg)//右转 201 { 202 //printf("右转\n"); 203 if(dir.x==1&&dir.y==0)//东 204 { 205 dir.x=0;dir.y=1; 206 } 207 else if(dir.x==-1&&dir.y==0)//西 208 { 209 dir.x=0;dir.y=-1; 210 } 211 else if(dir.x==0&&dir.y==1)//南 212 { 213 dir.x=-1;dir.y=0; 214 } 215 else if(dir.x==0&&dir.y==-1)//北 216 { 217 dir.x=1;dir.y=0; 218 } 219 } 220 else 221 { 222 ;//不变 223 } 224 break; 225 } 226 case ZERO: 227 { 228 reg=0; 229 break; 230 } 231 case CALL: 232 { 233 now.x=reg; 234 now.y=stack[ptr-1]; 235 continue; 236 break; 237 } 238 case ADD: 239 { 240 reg=reg+stack[ptr-1]; 241 break; 242 } 243 case SUB: 244 { 245 reg=stack[ptr-1]-reg; 246 break; 247 } 248 case MUL: 249 { 250 reg=reg*stack[ptr-1]; 251 break; 252 } 253 case DIV: 254 { 255 reg=stack[ptr-1]/reg; 256 break; 257 } 258 } 259 now.x=now.x+dir.x; 260 now.y=now.y+dir.y; 261 } 262 263 return 0; 264 } 265 266 int main(int argc,char *argv[]) 267 { 268 int i,j; 269 struct IMG image; 270 struct IMG *pimg=ℑ 271 char filename[64]; 272 if(argc!=2) 273 { 274 printf("请输入要编译的文件:"); 275 scanf("%s",filename); 276 } 277 else 278 { 279 strcpy(filename,argv[1]); 280 } 281 ReadPPM(filename,pimg); 282 decode(pimg); 283 return 0; 284 }
运行这个程序: ./ppm file.ppm
例如下面这个图片将会输出"He"这两个字符,本来是要输出Helloworld的,但是发现画起来有点麻烦,所以就没有画了。
上面的指令表示如下:
start;++;++;push;mul;mul;mul;mul;mul; push;zero;++;++;push;++turn;--; mul;mul;pop;add;out;++;turn;--; push;zero;++;++;push;mul;mul; mul;mul;pop;add;--;--;--;out;end;
看起来很神奇的样子呢!画了几个图,下面有点小经验,(如果有人真的去画的话。)关于转弯的问题,转弯是紫色,要让当前是左转还是右转,就取决于前一个颜色块是++块还是--块,然后为了恢复,后面就接着用--块或++块,这就实现转弯了。如果有强迫症的想每一行都用上像个已字一样那么就只需同时使用两个转弯就可以了。注意两次右转就实现向后了,而且还是只隔一行。好方便的说。
如果有兴趣还可以根据指令生成一张ppm的图片呢,然后再用程序去执行它。
参考资料: http://coolshell.cn/articles/1142.html
http://www.dangermouse.net/esoteric/piet/samples.html
本文地址: http://www.cnblogs.com/wunaozai/p/3888481.html
用到的程序和图片: http://files.cnblogs.com/wunaozai/BF%26Piet.zip