BFコンパイラー
以下のようにonelinerで可能です。
ただしLanguage::BF 0.03が必要です。
CodeRepos経由
で、
- svn co svn.coderepos.org/share/lang/perl/Language-BF
- cd Language-BF/trunk
- perl Makefile.PL
- make install
するか、CPANにVersion 0.03が現れるのをお待ち下さい。
Dan the Brainf.cker
1 2 3 4 | perl -MLanguage::BF \
-e 'print Language::BF->new_from_file(shift)->as_perl' t/hello.bf \
| perl
Hello World!
|
Posted feedbacks - Flatten
Nested Hiddenひさびさの一番かな? 入力を文字列にしたい場合なんかは Console.withInを使うとできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import scala.io.Source.fromFile
object BF{
def main(args:Array[String]) = {
if(args.size == 0) print("scala BF [sourcecode filename]")
else
args.foreach{f=> (new Machine).interpret(fromFile(f).mkString("")) }
}
}
object Machine {
final val MEM = 0xffffff
final val VAL = 0xff
}
class Machine{
var _p = 0
val _mem = new Array[int](Machine.MEM)
def p_=(v:int) = _p = v&Machine.MEM
def p = _p
def mem_=(v:int) = _mem(p)=v&Machine.VAL
def mem = _mem(p)
def abort = error("Missing corresponding parenthesis.")
def interpret(code:String):unit = interpret(code.toArray)
def interpret(code:Array[char]):unit = {
val (s,m) = ((List[int](), List[(int,int)]()) /: code.zipWithIndex){
(r,c) => c._1 match {
case '[' => (c._2::r._1, r._2)
case ']' => r._1 match {
case x::xs => (xs, (x,c._2)::(c._2,x)::r._2)
case _ => abort
}
case _ => r
}}
if(s.size > 0) abort
val parenMap = Map(m:_*)
var i = -1;while({i=i+1;i<code.size}) code(i) match {
case '>' => p = p+1
case '<' => p = p-1
case '+' => mem = mem+1
case '-' => mem = mem-1
case '.' => print(mem.asInstanceOf[char])
case ',' => mem = readChar.asInstanceOf[int]
case '[' if mem == 0 => i = parenMap(i)
case ']' if mem != 0 => i = parenMap(i)
case _ => ()
}
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.CSharp;
static class BFCompiler {
public static void Main(String[] args) {
if (0 < args.Length && File.Exists(args[0])) {
using(StringWriter sw = new StringWriter())
using(StreamReader sr = new StreamReader(args[0])) {
Stack<string> labels = new Stack<string>();
int label_num = 0;
sw.WriteLine("using System; static class BF {");
sw.WriteLine("public static void Main() {");
sw.WriteLine("byte[] m = new byte[256]; int p = 0;");
foreach(char c in sr.ReadToEnd()) {
switch(c) {
case '+': sw.WriteLine("m[p]++;"); break;
case '-': sw.WriteLine("m[p]--;"); break;
case '>': sw.WriteLine("p++;"); break;
case '<': sw.WriteLine("p--;"); break;
case '.': sw.WriteLine("Console.Write((char)m[p]);"); break;
case ',': sw.WriteLine("m[p] = (byte)Console.Read();"); break;
case '[': {
string ll = "L" + (label_num++);
labels.Push(ll);
sw.WriteLine("if (m[p] == 0) goto {0}_END;", ll);
sw.WriteLine("{0}_START:;", ll);
break;
}
case ']': {
string ll = labels.Pop();
sw.WriteLine("if (m[p] != 0) goto {0}_START;", ll);
sw.WriteLine("{0}_END:;", ll);
break;
}
}
}
sw.WriteLine("}}");
Generate(Path.GetFileNameWithoutExtension(args[0]), sw.ToString());
}
}
else {
Console.WriteLine("usage: bfc [sourcefile]");
}
}
private static void Generate(string filename, string cs_code) {
// ソースファイル生成
using (StreamWriter sw = new StreamWriter(filename + ".cs")) {
sw.Write(cs_code);
}
// 実行ファイル生成
CompilerParameters param = new CompilerParameters();
param.OutputAssembly = filename + ".exe";
param.GenerateExecutable = true;
CompilerResults rs = new CSharpCodeProvider().CompileAssemblyFromSource(param, cs_code);
// 実行
rs.CompiledAssembly.GetType("BF").GetMethod("Main").Invoke(null, null);
}
}
|
以下のようにonelinerで可能です。
ただしLanguage::BF 0.03が必要です。
CodeRepos経由
で、
- svn co svn.coderepos.org/share/lang/perl/Language-BF
- cd Language-BF/trunk
- perl Makefile.PL
- make install
するか、CPANにVersion 0.03が現れるのをお待ち下さい。
Dan the Brainf.cker
1 2 3 4 | perl -MLanguage::BF \
-e 'print Language::BF->new_from_file(shift)->as_perl' t/hello.bf \
| perl
Hello World!
|
>お題を見て「BF→言語Aへのトランスレータを言語Aで製作する」のだと思ったんですけど
それであっていると思いますよ。お手本は 「Language::BF->new_from_file(shift)->as_perl」というコードで「t/hello.bf」というBrainf*ckで書かれたコードを読んでPerlに変換し、その出力をさいごの「| perl」でもう一度Perlに食わせて実行させているわけです。
perl -MLanguage::BF \ -e 'print Language::BF->new_from_file(shift)->as_perl' t/hello.bf \ | perl Hello World!
SiroKuroさんがおっしゃっているお手本は http://blog.livedoor.jp/dankogai/archives/50545151.html のほうだと思います。投稿時間を見てみるとわかります。 僕もURLの方だけみて勘違いしてました・・・ 今から書き直します(^^;
ひねり無し。 python bf.py -o hello.py hello.bf みたいな感じで使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import sys
from getopt import getopt
def encode(bfcode):
depth = code = 0
pycode = []
stack = []
f = file(default['-o'], 'w')
pycode.append('import sys')
pycode.append('tape, ptr, code = {}, 0, 0')
while code != len(bfcode):
c = bfcode[code]
if c == '>':
pycode.append('\t' * depth + 'ptr += 1')
elif c == '<':
pycode.append('\t' * depth + 'ptr -= 1')
elif c == '+':
pycode.append('\t' * depth + 'tape[ptr] = tape.get(ptr, 0) + 1')
elif c == '-':
pycode.append('\t' * depth + 'tape[ptr] = tape.get(ptr, 0) - 1')
elif c == ',':
pycode.append('\t' * depth + 'tape[ptr] = sys.stdin.read(1)')
elif c == '.':
pycode.append('\t' * depth + 'sys.stdout.write(chr(tape.get(ptr, 0)))')
elif c == '[':
pycode.append('\t' * depth + 'while tape.get(ptr, 0):')
stack.append(depth)
depth += 1
elif c == ']':
depth = stack.pop()
code += 1
file(default['-o'], 'w').write('\n'.join(pycode))
if __name__ == '__main__':
default = {'-o': 'a.py'}
optlist, args = getopt(sys.argv[1:], 'o:', [])
if args:
default.update(optlist)
encode(''.join(file(args[0]).readlines()))
|
簡単の為、標準出力を使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <stdio.h>
#include <stdlib.h>
int main(int arg_c, char **arg_v) {
FILE *fp;
int c;
if (*++arg_v == NULL) {
fprintf(stderr, "usage: bfc [source file]\n");
exit(EXIT_FAILURE);
}
if ((fp = fopen(*arg_v, "r")) == NULL) {
fprintf(stderr, "file open error.\n");
exit(EXIT_FAILURE);
}
puts("#include <stdio.h>");
puts("#include <stdlib.h>");
puts("int main(void) {");
puts(" char *s, *p;");
puts(" p = s = (char *)calloc(1024, 1);");
while ((c = getc(fp)) != EOF) {
switch (c) {
case '>': puts(" ++p;"); break;
case '<': puts(" --p;"); break;
case '+': puts(" ++*p;"); break;
case '-': puts(" --*p;"); break;
case '.': puts(" putchar(*p);"); break;
case ',': puts(" *p = getchar();"); break;
case '[': puts(" while (*p) {"); break;
case ']': puts(" }"); break;
default: break;
}
}
puts(" free(s);");
puts("}");
fclose(fp);
}
|
./bf2c hello.bf > hello.c
または
./bf2c hello.bf 128 > hello.c
のように使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #include <stdio.h>
#include <stdlib.h>
char *code[] = { "sp++;", "sp--;", "(*sp)++;", "(*sp)--;", "putchar(*sp);",
"*sp = getchar();", "while(*sp){", "}" };
int main( int argc, char *argv[] ){
int c,i,j,n=0;
int indent = 1;
int length;
FILE *fp;
if( argc < 2 ){
fprintf(stderr,"Usage: %s sourcefile\n", argv[0] );
return EXIT_FAILURE;
}
if( (fp = fopen( argv[1], "r" )) == NULL ){
fprintf(stderr,"Error: %s cannot opened\n", argv[1] );
return EXIT_FAILURE;
}
if( !argv[2] || (length = atoi( argv[2] )) <= 0 ){
length = 256;
}
puts("#include <stdio.h>");
puts("#include <stdlib.h>");
printf("#define DATA_LEN %d\n", length);
puts("char code[DATA_LEN];");
puts("int main (void){");
puts(" char *sp = code;");
while( (c=fgetc( fp )) != EOF ){
switch(c){
case '>': i = 0; break;
case '<': i = 1; break;
case '+': i = 2; break;
case '-': i = 3; break;
case '.': i = 4; break;
case ',': i = 5; break;
case '[': i = 6; indent++; break;
case ']': i = 7; indent--; break;
default:
/* skip other characters */
continue;
}
for( j = 0; j < (indent+(i==6?-1:0)); j++ ){
printf(" ");
}
printf("%s\n", code[i] );
}
puts(" return EXIT_SUCCESS;");
puts("}");
fclose(fp);
return EXIT_SUCCESS;
}
|
コードをラベルと命令の列みたいなのにして、 prog の中へ放り込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | (defun compile-bf (str &optional (size 30000) (offset 0))
(let ((tags ()))
(flet ((translate (c)
(case c
(#\> '((incf ptr)))
(#\< '((decf ptr)))
(#\+ '((setf *ptr (logand (1+ *ptr) #xff))))
(#\- '((setf *ptr (logand (1- *ptr) #xff))))
(#\. '((write-char (code-char *ptr))))
(#\, '((setf *ptr (char-code (read-char)))))
(#\[ (let ((t1 (gensym)) (t2 (gensym)))
(setf tags (list* t1 t2 tags))
`(,t1 (if (= *ptr 0) (go ,t2)))))
(#\] (let ((t1 (pop tags)) (t2 (pop tags)))
`((if (/= *ptr 0) (go ,t1)) ,t2))))))
`(symbol-macrolet ((*ptr (aref array ptr)))
(prog ((array ,(make-array size :initial-element 0))
(ptr ,offset))
,@(loop for c across str append (translate c)))))))
;;; test
(eval (compile-bf "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.
+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+."))
|
はっきりしなくてごめんなさい。にしおさんが言う通り。
コンパイラー(トランスレーター)
BF ====> 言語A
言語A
というのが本来の趣旨で、そしてBFの場合こちらの方が
インタープリター(ランタイム)
BF => 言語Aで書かれた実行環境
よりもずっと実装が簡単なので。
ちなみにLanguage::BFはどちらの機能も持っています。
Dan the Brainf.cker
題意を読み間違えてたので。 こっちのほうがかなり楽です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import scala.io.Source.fromFile
object BFC{
def main(args:Array[String]) = {
if(args.size == 0){
println("Usage:scala BFC [sourcecode filename]")
}else{
println("""
object Machine {
final val MEM = 0xffffff
final val VAL = 0xff
}
class Machine{
var _p = 0
val _mem = new Array[int](Machine.MEM)
def p_=(v:int) = _p = v&Machine.MEM
def p = _p
def mem_=(v:int) = _mem(p)=v&Machine.VAL
def mem = _mem(p)
def eval = {
""")
val code = fromFile(args(0)).mkString("").toList
if(code.count('['==_) != code.count(']'==_)) {
error("Missing corresponding parenthesis.")
}
code.foreach(c=>println(c match {
case '>' => "p = p+1"
case '<' => "p = p-1"
case '+' => "mem = mem+1"
case '-' => "mem = mem-1"
case '.' => "print(mem.asInstanceOf[char])"
case ',' => "mem = readChar.asInstanceOf[int]"
case '[' => "while(mem != 0){"
case ']' => "}"
case _ => ""
}))
println("}};(new Machine).eval;")
}
}
}
|
ケースが嫌いな私は、以下のように書き換えてしまいました。機能的には#3955と互換ですが、出力されたCコードのコンパイルには差し支えないのでインデントは省略しました。
Dan the Brainf.cker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] ){
int c;
char *code[256];
int length;
FILE *fp;
if( argc < 2 ){
fprintf(stderr,"Usage: %s sourcefile\n", argv[0] );
return EXIT_FAILURE;
}
if( (fp = fopen( argv[1], "r" )) == NULL ){
fprintf(stderr,"Error: %s cannot opened\n", argv[1] );
return EXIT_FAILURE;
}
if( !argv[2] || (length = atoi( argv[2] )) <= 0 ){
length = 256;
}
for (c = 0; c < 256; c++) code[c] = NULL;
code['>'] = "sp++;";
code['<'] = "sp--;";
code['+'] = "(*sp)++;";
code['-'] = "(*sp)--;";
code['.'] = "putchar(*sp);";
code[','] = "*sp = getchar();";
code['['] = "while(*sp){";
code[']'] = "}";
puts("#include <stdio.h>");
puts("#include <stdlib.h>");
printf("#define DATA_LEN %d\n", length);
puts("char code[DATA_LEN];");
puts("int main (void){");
puts(" char *sp = code;");
while( (c=fgetc( fp )) != EOF ) if (code[c]) printf("%s\n", code[c]);
puts(" return EXIT_SUCCESS;");
puts("}");
fclose(fp);
return EXIT_SUCCESS;
}
|
ひねりなし。 配列の値がbyte型を越える場合についてはとりあえず考慮しない方向で…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | class BF
def compile(str)
depth = 0
code = []
code << "mem = [0]"
code << "ptr = 0"
str.each_byte do |ch|
if ch == ?>
code << "\t" * depth + "ptr += 1; mem[ptr] = 0 if ptr >= mem.size"
elsif ch == ?<
code << "\t" * depth + "(ptr == 0)? mem.unshift(0) : ptr -= 1"
elsif ch == ?+
code << "\t" * depth + "mem[ptr] += 1"
elsif ch == ?-
code << "\t" * depth + "mem[ptr] -= 1"
elsif ch == ?.
code << "\t" * depth + "putc(mem[ptr])"
elsif ch == ?,
code << "\t" * depth + "mem[ptr] = STDIN.getc"
elsif ch == ?[
code << "\t" * depth + "while(mem[ptr] != 0) do"
depth += 1
elsif ch == ?]
depth -= 1
code << "\t" * depth + "end"
end
end
code.join "\n"
end
end
eval BF.new.compile("++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.") # => Hello World!
|
自己ツッコミ。 このままだと"~[]~" のようなBFコードがあった際にwhile文のところで文法エラーを起こすので、 ']'が出た時にはpassをダミーで放り込んだ方がよさそうです。 あと、最初間違えてインタプリタを書いてしまった名残で変なやり方になっていました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | def encode(bfcode):
- depth = code = 0
+ depth = 0
pycode = []
stack = []
f = file(default['-o'], 'w')
pycode.append('import sys')
- pycode.append('tape, ptr, code = {}, 0, 0')
+ pycode.append('tape, ptr = {}, 0')
- while code != len(bfcode):
- c = bfcode[code]
+ for c in bfcode:
if c == '>':
pycode.append('\t' * depth + 'ptr += 1')
elif c == '<':
pycode.append('\t' * depth + 'ptr -= 1')
elif c == '+':
pycode.append('\t' * depth + 'tape[ptr] = tape.get(ptr, 0) + 1')
elif c == '-':
pycode.append('\t' * depth + 'tape[ptr] = tape.get(ptr, 0) - 1')
elif c == ',':
pycode.append('\t' * depth + 'tape[ptr] = sys.stdin.read(1)')
elif c == '.':
pycode.append('\t' * depth + 'sys.stdout.write(chr(tape.get(ptr, 0)))')
elif c == '[':
pycode.append('\t' * depth + 'while tape.get(ptr, 0):')
stack.append(depth)
depth += 1
elif c == ']':
+ pycode.append('\t' * depth + 'pass')
depth = stack.pop()
- code += 1
|
久しぶりに投稿します。
-v optimize=1 とするとオプティマイズされます(笑
helloworld.bf:
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
% awk -f bf2awk.awk helloworld.bf > helloworld.awk
% awk -f helloworld.awk
Hello World!
helloworld.awk:
BEGIN {
ix = 0
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
while (st[ix]) {
ix++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
ix++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
ix++
st[ix]++
st[ix]++
st[ix]++
ix++
st[ix]++
ix--
ix--
ix--
ix--
st[ix]--
}
ix++
st[ix]++
st[ix]++
printf("%c", st[ix])
ix++
st[ix]++
printf("%c", st[ix])
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
printf("%c", st[ix])
printf("%c", st[ix])
st[ix]++
st[ix]++
st[ix]++
printf("%c", st[ix])
ix++
st[ix]++
st[ix]++
printf("%c", st[ix])
ix--
ix--
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
st[ix]++
printf("%c", st[ix])
ix++
printf("%c", st[ix])
st[ix]++
st[ix]++
st[ix]++
printf("%c", st[ix])
st[ix]--
st[ix]--
st[ix]--
st[ix]--
st[ix]--
st[ix]--
printf("%c", st[ix])
st[ix]--
st[ix]--
st[ix]--
st[ix]--
st[ix]--
st[ix]--
st[ix]--
st[ix]--
printf("%c", st[ix])
ix++
st[ix]++
printf("%c", st[ix])
ix++
printf("%c", st[ix])
}
% awk -v optimize=1 -f bf2awk.awk helloworld.bf > helloworld-optimized.awk
% awk -f helloworld-optimized.awk
Hello World!
helloworld-optimized.awk:
BEGIN {
ix = 0
st[ix] += 10
while (st[ix]) {
st[++ix]++
st[ix] += 6
st[++ix]++
st[ix] += 9
st[++ix]++
st[ix] += 2
st[++ix]++
ix -= 4
st[ix]--
}
st[++ix]++
st[ix]++
printf("%c", st[ix])
st[++ix]++
printf("%c", st[ix])
st[ix] += 7
printf("%c", st[ix])
printf("%c", st[ix])
st[ix] += 3
printf("%c", st[ix])
st[++ix]++
st[ix]++
printf("%c", st[ix])
ix -= 2
st[ix] += 15
printf("%c", st[ix])
printf("%c", st[++ix])
st[ix] += 3
printf("%c", st[ix])
st[ix] -= 6
printf("%c", st[ix])
st[ix] -= 8
printf("%c", st[ix])
st[++ix]++
printf("%c", st[ix])
printf("%c", st[++ix])
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | BEGIN {
read_buf = ""
if (optimize) reset_last()
indent = ""
indent_print("BEGIN {")
indent_incr()
indent_print("ix = 0")
}
{
gsub(/#.*$/,""); # comment
gsub(/[^\[\]<>+\-.,]/,""); if (/^$/) next
N = split($0, op, "")
for (i=1; i<=N; i++) {
if (op[i] == ">") {
if (optimize) {
if (last_inst == "ix") {
last_arg++
} else {
out_last()
set_last("ix", 1)
}
} else {
indent_print("ix++")
}
} else if (op[i] == "<") {
if (optimize) {
if (last_inst == "ix") {
last_arg--
} else {
out_last()
set_last("ix", -1)
}
} else {
indent_print("ix--")
}
} else if (op[i] == "+") {
if (optimize) {
if (last_inst == "st[ix]") {
last_arg++
} else {
s = set_incr_decr("st[ix]")
if (last_inst) out_last()
set_last(s, 1)
}
} else {
indent_print("st[ix]++")
}
} else if (op[i] == "-") {
if (optimize) {
if (last_inst == "st[ix]") {
last_arg--
} else {
s = set_incr_decr("st[ix]")
if (last_inst) out_last()
set_last(s, -1)
}
} else {
indent_print("st[ix]--")
}
} else if (op[i] == ".") {
if (optimize) {
s = set_incr_decr("printf(\"%c\", st[ix])")
if (last_inst) out_last()
indent_print(s)
} else {
indent_print("printf(\"%c\", st[ix])")
}
} else if (op[i] == ",") {
if (optimize) {
s = set_incr_decr("st[ix] = getchar()")
if (last_inst) out_last()
indent_print(s)
} else {
indent_print("st[ix] = getchar()")
}
} else if (op[i] == "[") {
if (optimize) {
s = set_incr_decr("while (st[ix]) {")
if (last_inst) out_last()
indent_print(s)
indent_incr()
} else {
indent_print("while (st[ix]) {")
indent_incr()
}
} else if (op[i] == "]") {
if (optimize) out_last()
indent_decr()
indent_print("}")
} else {
;
}
}
}
END {
if (optimize) out_last()
indent_decr()
indent_print("}")
}
function getchar( ch)
{
if (read_buf ~ /^$/) getline read_buf
ch = substr(read_buf,1,1)
read_buf = substr(read_buf,2)
return ch
}
function indent_incr()
{
indent = " " indent
}
function indent_decr()
{
indent = substr(indent, 3)
}
function indent_print(line)
{
print indent line
}
function set_last(inst,arg)
{
last_inst = inst
last_arg = arg
}
function reset_last()
{
last_inst = ""
last_arg = 0
}
function out_last( diff)
{
if (last_inst ~ /ix/) {
if (last_arg > 1)
diff = " += " last_arg
else if (last_arg == 1)
diff = "++"
else if (last_arg == 0)
diff = ""
else if (last_arg == -1)
diff = "--"
else if (last_arg < -1)
diff = " -= " (0 - last_arg)
if (diff) indent_print(last_inst diff)
}
last_inst = ""
}
function set_incr_decr(s)
{
if (last_inst == "ix") {
if (last_arg == 1) { gsub(/ix/, "++ix", s); last_inst = "" }
else if (last_arg == -1) { gsub(/ix/, "--ix", s); last_inst = "" }
}
return s
}
|
これまたif文の羅列を置き換える方向で書き換えてみました。
Cと違ってHashが気軽に使えるのがうれしい。
next unless が使えるのもPerl Mongerとしてはうれしい。
str.unpack("C*").map{|c| c.chr}.each do |ch|
は、単に{}とdo endを両方使ってみたかったから。
Dan the Occasional Rubyist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class BF
@@opcode = {
'>' => "ptr += 1; mem[ptr] = 0 if ptr >= mem.size",
'<' => "(ptr == 0)? mem.unshift(0) : ptr -= 1",
'+' => "mem[ptr] += 1",
'-' => "mem[ptr] -= 1",
'.' => "putc(mem[ptr])",
',' => "mem[ptr] = STDIN.getc",
'[' => "while(mem[ptr] != 0) do",
']' => "end"
}
def compile(str)
code = ["mem = [0]", "ptr = 0"]
str.unpack("C*").map{|c| c.chr}.each do |ch|
next unless @@opcode[ch]
code << @@opcode[ch]
end
code.join "\n"
end
end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | | bf st op in |
bf := FileStream fileNamed: 'hello.bf'.
in := FileStream fileNamed: 'in.txt'.
in binary.
st := WriteStream with: '| ss | ss := ReadWriteStream with: #(0). ss reset'.
[(op := bf next) notNil] whileTrue: [
st nextPutAll: (op caseOf: {
[$>] -> ['. ss next. ss atEnd ifTrue: [ss nextPut: 0; back]'].
[$<] -> ['. ss back'].
[$+] -> ['. ss nextPut: ss peek + 1; back'].
[$-] -> ['. ss nextPut: ss peek - 1; back'].
[$.] -> ['. Transcript show: (ss peek asCharacter)'].
[$,] -> ['. ss nextPut: ', in next printString,' value; back'].
[$[] -> ['. [ss peek isZero] whileFalse: ['].
[$]] -> [']']} otherwise: [''])].
bf close. in close.
World findATranscript: nil.
Compiler evaluate: st contents
|
おぉ、なるほど。勉強になります。
a2p的にもきれいなawkコードを吐きますねwww
Dan the Cyberpolyglot
1 2 | % awk -f bfcc.awk helloworld.bf | a2p | perl
Hello, World!
|
optimize=1 の時に while(st[ix]) の直前に ix++ や ix-- が来ると while(st[++ix]) のように誤ったオプティマイズが行われるバグを修正
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | --- bf2awk.awk.orig 2007-11-14 00:43:07.000000000 +0900
+++ bf2awk.awk 2007-11-14 01:46:46.000000000 +0900
@@ -77,15 +77,9 @@
indent_print("st[ix] = getchar()")
}
} else if (op[i] == "[") {
- if (optimize) {
- s = set_incr_decr("while (st[ix]) {")
- if (last_inst) out_last()
- indent_print(s)
- indent_incr()
- } else {
- indent_print("while (st[ix]) {")
- indent_incr()
- }
+ if (optimize) out_last()
+ indent_print("while (st[ix]) {")
+ indent_incr()
} else if (op[i] == "]") {
if (optimize) out_last()
indent_decr()
|
オプティマイズ時に ++ix st[ix]++ st[ix]++ st[ix]++ が st[++ix]++ st[ix] += 2 となっていたのを st[++ix] += 3 のようにちゃんとまとめるように修正するパッチ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | --- bf2awk.awk.orig 2007-11-14 00:43:07.000000000 +0900
+++ bf2awk.awk 2007-11-14 01:55:50.000000000 +0900
@@ -38,7 +38,7 @@
}
} else if (op[i] == "+") {
if (optimize) {
- if (last_inst == "st[ix]") {
+ if (last_inst ~ /^st\[/) {
last_arg++
} else {
s = set_incr_decr("st[ix]")
@@ -50,7 +50,7 @@
}
} else if (op[i] == "-") {
if (optimize) {
- if (last_inst == "st[ix]") {
+ if (last_inst ~ /^st\[/) {
last_arg--
} else {
s = set_incr_decr("st[ix]")
|
1 | ,+[-.,+]
|
これまたifの羅列をHashに置き換え。ただし、pythonの場合、[]は少し特別扱いが必要。こちらはindent不要とは行かないので。
あと、関数名やインターフェースも好みにあわせて変えました。
Dan the Novice Snake Tamer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #!/usr/bin/env python
import sys
from getopt import getopt
def bf2py(bfcode):
depth = 0
opcode = {
'>':'ptr += 1',
'<':'ptr -= 1',
'+':'tape[ptr] = tape.get(ptr, 0) + 1',
'-':'tape[ptr] = tape.get(ptr, 0) - 1',
',':'tape[ptr] = sys.stdin.read(1)',
'.':'sys.stdout.write(chr(tape.get(ptr, 0)))',
'[':'while tape.get(ptr, 0):',
']':'pass'
}
pycode = []
stack = []
pycode.append('import sys')
pycode.append('tape, ptr = {}, 0')
for c in bfcode:
if opcode.has_key(c):
pycode.append( '\t' * depth + opcode[c])
if c == '[':
stack.append(depth)
depth += 1
elif c == ']':
depth = stack.pop()
return '\n'.join(pycode)
if __name__ == '__main__':
defout = 'a.py'
optlist, args = getopt(sys.argv[1:], 'o:', [])
if args:
pysrc = bf2py(''.join(file(args[0]).readlines()));
file(defout or ops['-o'], 'w').write(pysrc)
|
うう駄目だ、getchar() の定義が出力されないので、#3976の ,+[-.,+] をコンパイルしても動かない。 しかも getchar() が文字コードを返すようになってない。ord() を実装。 全面書き換え。 a2pをかましても動くように getline, index 回りを微調整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | BEGIN {
if (optimize) reset_last()
indent = ""
print "BEGIN {"
indent_incr()
indent_print("read_buf = \"\"")
indent_print("ix = 0")
}
{
gsub(/#.*$/,""); # comment
gsub(/[^\[\]<>+\-.,]/,""); if (/^$/) next
N = split($0, op, "")
for (i=1; i<=N; i++) {
if (op[i] == ">") {
if (optimize) {
if (last_inst == "ix") {
last_arg++
} else {
out_last()
set_last("ix", 1)
}
} else {
indent_print("ix++")
}
} else if (op[i] == "<") {
if (optimize) {
if (last_inst == "ix") {
last_arg--
} else {
out_last()
set_last("ix", -1)
}
} else {
indent_print("ix--")
}
} else if (op[i] == "+") {
if (optimize) {
if (last_inst ~ /^st\[/) {
last_arg++
} else {
s = set_incr_decr("st[ix]")
if (last_inst) out_last()
set_last(s, 1)
}
} else {
indent_print("st[ix]++")
}
} else if (op[i] == "-") {
if (optimize) {
if (last_inst ~ /^st\[/) {
last_arg--
} else {
s = set_incr_decr("st[ix]")
if (last_inst) out_last()
set_last(s, -1)
}
} else {
indent_print("st[ix]--")
}
} else if (op[i] == ".") {
if (optimize) {
s = set_incr_decr("printf(\"%c\", st[ix])")
if (last_inst) out_last()
indent_print(s)
} else {
indent_print("printf(\"%c\", st[ix])")
}
} else if (op[i] == ",") {
if (optimize) {
s = set_incr_decr("st[ix] = getchar()")
if (last_inst) out_last()
indent_print(s)
} else {
indent_print("st[ix] = getchar()")
}
} else if (op[i] == "[") {
if (optimize) {
s = set_incr_decr("while (st[ix]) {")
if (last_inst) out_last()
indent_print(s)
indent_incr()
} else {
indent_print("while (st[ix]) {")
indent_incr()
}
} else if (op[i] == "]") {
if (optimize) out_last()
indent_decr()
indent_print("}")
} else {
;
}
}
}
END {
if (optimize) out_last()
indent_decr()
indent_print("}")
print "function getchar( ch)"
print "{"
print " if (read_buf ~ /^$/) {"
print " if (eof) return -1"
# print " if ((getline read_buf) < 1) { eof = 1; return -1 }"
print " if (getline != 1) { eof = 1; return -1 }" # a2p
print " read_buf = $0" # a2p
print " }"
print " ch = ord(substr(read_buf,1,1))"
print " read_buf = substr(read_buf,2)"
print " return ch"
print "}"
print "function ord(ch, ofs)"
print "{"
print " if (ch == \"\\t\") return 9"
print " if (ch == \"\\n\") return 13"
# print " ofs = index(\" !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\", ch)"
print " if (ch == \"@\") return 64" # a2p
print " ofs = index(\" !\\\"#$%&'()*+,-./0123456789:;<=>?.ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\", ch)" # a2p
print " return ofs ? 31 + ofs : 0"
print "}"
}
function indent_incr()
{
indent = " " indent
}
function indent_decr()
{
indent = substr(indent, 3)
}
function indent_print(line)
{
print indent line
}
function set_last(inst,arg)
{
last_inst = inst
last_arg = arg
}
function reset_last()
{
last_inst = ""
last_arg = 0
}
function out_last( diff)
{
if (last_inst ~ /ix/) {
if (last_arg > 1)
diff = " += " last_arg
else if (last_arg == 1)
diff = "++"
else if (last_arg == 0)
diff = ""
else if (last_arg == -1)
diff = "--"
else if (last_arg < -1)
diff = " -= " (0 - last_arg)
if (diff) indent_print(last_inst diff)
}
last_inst = ""
}
function set_incr_decr(s)
{
if (last_inst == "ix") {
if (last_arg == 1) { gsub(/ix/, "++ix", s); last_inst = "" }
else if (last_arg == -1) { gsub(/ix/, "--ix", s); last_inst = "" }
}
return s
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function BF(code){
var s = '', o = {
'>': 'm[p+=?]|=0;',
'<': 'm[p-=?]|=0;',
'+': 'm[p]+=?;',
'-': 'm[p]-=?;',
',': 'm[p]=get().charCodeAt(0);',
'.': 'put(c(m[p]));',
'[': 'while(m[p]){',
']': '}' };
code.replace(/>+|<+|\++|-+|([,.[\]])/g, function($, _){
s += _ ? o[$] : o[$.charAt(0)].replace('?', $.length) });
return eval('0,function(get,put){var m=[0],p=0,c=String.fromCharCode;'+ s +'}');
}
/// Rhinoで「hello world」のテスト ///
BF('++++++++++[>+++++++>++++++++++>+++>+<<<<-]\
>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.'
)(function(){ return '?' }, function(c){ java.lang.System.out.print(c) });
/// WSHで「echo」のテスト ///
//BF(',+[-.,+]')(function(){ return WSH.stdIn.read(1) }, function(c){ WSH.stdOut.write(c) });
|
あまり綺麗なコードではないですが...。 メモリは左右に伸びる無限リストで表現しました。 >=>演算子 (Kleisli composition)は非常に便利です。 GHC6.8で新しく追加されましたが,一応定義を書きました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import Data.List
import System.Environment
putCode '>' = putStr "incP >=> "
putCode '<' = putStr "decP >=> "
putCode '+' = putStr "inc >=> "
putCode '-' = putStr "dec >=> "
putCode '.' = putStr "put >=> "
putCode ',' = putStr "get >=> "
putCode '[' = putStr "loop("
putCode ']' = putStr "return) >=> "
genCode source
= do putStr "runBF = "
mapM_ putCode (source `intersect` "><+-.,[]")
putStrLn "return"
main = do
source <- readFile . head =<< getArgs
putStrLn "import Data.Char"
putStrLn "(>=>) m1 m2 = \\s -> m1 s >>= m2"
putStrLn "incP (ps, n:ns) = return (n:ps, ns)"
putStrLn "decP (p:ps, ns) = return (ps, p:ns)"
putStrLn "inc (ps, n:ns) = return (ps, n+1:ns)"
putStrLn "dec (ps, n:ns) = return (ps, n-1:ns)"
putStrLn "put mem@(_, n:_) = putChar (chr n) >> return mem"
putStrLn "get mem@(ps, _:ns) = getChar >> (\\c -> return (ps, ord c:ns))"
putStrLn "loop code mem@(_, 0:_) = return mem"
putStrLn "loop code mem = code mem >>= loop code"
genCode source
putStrLn "main = runBF ([0,0..], [0,0..])"
|
see: Xbyak
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | /*
出力例
#include <stdio.h>
static int stack[32768];
static const unsigned char code[] = {
0x55,0x56,0x57,0x8b,0x74,0x24,0x10,0x8b,0x7c,0x24,0x14,0x8b,0x6c,0x24,0x18,0x83,
...
};
main()
{
((void (*)(void*, void*, int *))code)((void*)putchar, (void*)getchar, stack);
}
*/
#include "xbyak/xbyak.h"
#include <stdio.h>
#include <stack>
#include <fstream>
struct Brainfuck : public Xbyak::CodeGenerator {
Brainfuck(std::istream& is) : CodeGenerator(10000)
{
push(ebp); // stack
push(esi);
push(edi);
const int _P = 4 * 3;
mov(esi, ptr[esp + _P + 4]);
mov(edi, ptr[esp + _P + 8]);
mov(ebp, ptr[esp + _P + 12]);
int labelNo = 0;
std::stack<int> keepLabelNo;
char label[32];
char c;
while (is >> c) {
switch (c) {
case '+': inc(dword [ebp]); break;
case '-': dec(dword [ebp]); break;
case '.': push(dword [ebp]); call(esi); pop(eax); break;
case ',': call(edi); mov(dword [ebp], eax); break;
case '>': add(ebp, 4); break;
case '<': sub(ebp, 4); break;
case '[':
sprintf(label, "B%d", labelNo); L(label);
mov(eax, dword [ebp]);
test(eax, eax);
sprintf(label, "F%d", labelNo); jz(label, T_NEAR);
keepLabelNo.push(labelNo++);
break;
case ']':
{
int no = keepLabelNo.top(); keepLabelNo.pop();
sprintf(label, "B%d", no); jmp(label);
sprintf(label, "F%d", no); L(label);
}
break;
default:
break;
}
}
pop(edi);
pop(esi);
pop(ebp);
ret();
}
};
void dump(const Xbyak::uint8 *code, size_t size)
{
puts("#include <stdio.h>\nstatic int stack[32768];\nstatic const unsigned char code[] = {");
for (size_t i = 0; i < size; i++) {
printf("0x%02x,", code[i]); if ((i % 16) == 15) putchar('\n');
}
puts("\n};");
#ifdef __linux__
puts("#include <unistd.h>");
puts("#include <sys/mman.h>");
#endif
puts("main()\n{");
#ifdef __linux__
puts("\tlong pageSize = sysconf(_SC_PAGESIZE) - 1;");
puts("\tmprotect((void*)code, (sizeof(code) + pageSize) & ~pageSize, PROT_READ | PROT_EXEC);");
#endif
puts(
"\t((void (*)(void*, void*, int *))code)((void*)putchar, (void*)getchar, stack);\n"
"}"
);
}
int main(int argc, char *argv[])
{
if (argc == 1) {
fprintf(stderr, "bf filename.bf [0|1]\n");
return 1;
}
std::ifstream ifs(argv[1]);
int mode = argc == 3 ? atoi(argv[2]) : 0;
Brainfuck bf(ifs);
if (mode == 0) {
static int stack[32768];
((void (*)(void*, void*, int *))bf.getCode())((void*)putchar, (void*)getchar, stack);
} else {
dump(bf.getCode(), bf.getSize());
}
return 0;
}
|
仮想機械(機械状態とインストラクションセット)を明示的に定義した.
機械状態は,プログラムカウンタのスタック,プログラムカウンタ,ヒープ,ヒープポインタの4つ組み.インストラクションはBFのインストラクションに対応する.
- '>' → incp
- '<' → decp
- '+' → incp
- '-' → decc
- '.' → putc
- ',' → getc
- ']' → jmpb
- '[' → jmpf <PC>
jmpf 以外はオペランドのないインストラクション.jmpfは飛び先をオペランドとする.
% runghc bfc.hs hello_world
とやるとhello_world.bというBFコードファイルを読み,インストラクション列をbfrts.templというランタイムテンプレートの最後に追加して,hello_world.hsというHaskellのファイルを作成する.上のhello_world.bをコンパイルしてできたインストラクション列は,
codeL :: [Instruction]
codeL = [incc,incc,incc,incc,incc,incc,incc,incc,jmpf 22,incp,incc,incc,incc,incc,incc,incc,incc,incc,incc,decp,decc,jmpb,incp,putc,decp,incc,incc,incc,incc,incc,jmpf 41,incp,incc,incc,incc,incc,incc,incc,decp,decc,jmpb,incp,decc,putc,incc,incc,incc,incc,incc,incc,incc,putc,putc,incc,incc,incc,putc,decp,incc,incc,incc,incc,incc,incc,incc,incc,jmpf 77,incp,incp,incc,incc,incc,incc,decp,decp,decc,jmpb,incp,incp,putc,decp,decp,incc,incc,incc,incc,jmpf 97,incp,decc,decc,decc,decc,decc,decc,decp,decc,jmpb,incp,putc,decp,incc,incc,incc,incc,jmpf 115,incp,incc,incc,incc,incc,incc,incc,decp,decc,jmpb,incp,putc,incc,incc,incc,putc,decc,decc,decc,decc,decc,decc,putc,decc,decc,decc,decc,decc,decc,decc,decc,putc,incp,incc,putc]
で,これがbfrts.templの最後の追加されて hello_world.hs が出きる.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | -- bfc.hs : BF コンパイラ ---------------------------------------------------
module Main where
import Data.Char
import Data.List
import System.IO
import System.Environment
main = do { args <- getArgs
; case args of
[] -> runcompiler stdin stdout
s:_ -> do { ih <- openFile (s++".b") ReadMode
; oh <- openFile (s++".hs") WriteMode
; runcompiler ih oh
}
}
runcompiler ih oh =
do { cs <- hGetContents ih
; let cs' = zip [0..] $ filter (not . isSpace) cs
; outputCode oh $ compile cs'
}
compile [] = []
compile (c:cs) = case c of
(_,'>') -> "incp" : compile cs
(_,'<') -> "decp" : compile cs
(_,'+') -> "incc" : compile cs
(_,'-') -> "decc" : compile cs
(_,'.') -> "putc" : compile cs
(_,',') -> "getc" : compile cs
(_,']') -> "jmpb" : compile cs
(_,'[') -> ("jmpf "++show pc) : compile cs
where pc = findpc [] cs
_ -> error "invalid input"
findpc s [] = error "syntax error"
findpc s (c:cs) = case c of
(_,'[') -> findpc ('[':s) cs
(p,']') -> case s of
[] -> p+1
_ -> findpc (tail s) cs
_ -> findpc s cs
outputCode h s
= do { hd <- readFile "bfrts.tmpl"
; hPutStrLn h hd
; hPutStrLn h "codeL :: [Instruction]"
; hPutStr h "codeL = ["
; hPutStr h (concat (intersperse "," s))
; hPutStr h "]\n"
}
-- bfrts.templ : ランタイムテンプレート -------------------------------------
module Main where
import Control.Monad.State
import Data.Array as A
import Data.Char
import Data.IntMap as M
import Debug.Trace
type PC = Int
type Code = Array PC Instruction
type Heap = M.IntMap Char
type Stack = [PC]
type Pointer = Int
type VM = (Stack,PC,Heap,Pointer)
type Instruction = VM -> StateT VM IO ()
top = head
pop = tail
push = (:)
-- initial machine state
iStat = ([],0,M.fromList $ zip [0..29999] (repeat $ chr 0),0)
-- instruction set
incp,decp,incc,decc,putc,getc,jmpb :: Instruction
jmpf :: PC -> Instruction
incp (s,c,h,p) = put (s,succ c,h,succ p)
decp (s,c,h,p) = put (s,succ c,h,pred p)
incc (s,c,h,p) = put (s,succ c,M.update (Just . succ) p h,p)
decc (s,c,h,p) = put (s,succ c,M.update (Just . pred) p h,p)
putc (s,c,h,p) = liftIO (putChar (h M.! p)) >> put (s,succ c,h,p)
getc (s,c,h,p) = liftIO (getChar >>= \ ch -> return (s,succ c,M.update (const (Just ch)) p h,p)) >>= put
jmpb (s,c,h,p) = if h M.! p == chr 0 then put (pop s,succ c,h,p) else put (s,top s,h,p)
jmpf pc (s,c,h,p) = if h M.! p == chr 0 then put (s,pc,h,p) else put (push (succ c) s,succ c,h,p)
-- run compiled bf code
main :: IO ()
main = evalStateT execute iStat >> putStrLn ""
run = do { stat@(s,c,h,p) <- get
; if inRange (bounds code) c then (code A.! c) stat >> run
else return ()
}
code :: Code
code = listArray (0,length codeL - 1) codeL
-- この下にコンパイル済みの codeL :: [Instruction] が追加される
|
せっかくなので++++がbf.inc(4)に置き換わるような設計にしてみました。
この程度の規模なら正規表現でも十分いけるんじゃないかと思いつつ勉強がてらにlexとyaccを使いました。
PLY (Python Lex-Yacc)
あとインデントうんぬんを考慮しないといけないのはそもそも直接while文を使うからなので式で表現しました。
,+[-.,+]を入力するとbf.get()or bf.inc(1)or bf.loop(lambda: bf.inc(-1)or bf.put()or bf.get()or bf.inc (1))と出力されます。
下のFizzBuzzコードを食わせると2638バイトの出力で、処理時間はあっという間でした。 http://d.hatena.ne.jp/n_shuyo/20070516/fizzbuzz
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | from ply import lex, yacc
import sys
# lex
tokens = "PLUS MINUS LEFT RIGHT WHILE WEND PUT GET COMMENT".split()
t_WHILE = "\["
t_WEND = "]"
t_PUT = "\."
t_GET = ","
def t_PLUS(t):
"\++"
t.value = len(t.value)
return t
def t_MINUS(t):
"-+"
t.value = len(t.value)
return t
def t_LEFT(t):
"<+"
t.value = len(t.value)
return t
def t_RIGHT(t):
">+"
t.value = len(t.value)
return t
def t_COMMENT(t):
'[^[\]><+-,.]' # return nothing
def t_error(t): pass
# yacc
"""*** grammar definition
sequence : sequence command
| command
command : PLUS
| MINUS
| LEFT
| RIGHT
| PUT
| GET
| loop
loop : WHILE sequence WEND
"""
def p_sequence(p):
"sequence : sequence command"
p[0] = p[1] + "or " + p[2]
def p_sequence_command(p):
"sequence : command"
p[0] = p[1]
def p_command_PLUS(p):
"command : PLUS"
p[0] = "bf.inc(%s)" % p[1]
def p_command_MINUS(p):
"command : MINUS"
p[0] = "bf.inc(%s)" % -p[1]
def p_command_LEFT(p):
"command : LEFT"
p[0] = "bf.mov(%s)" % -p[1]
def p_command_RIGHT(p):
"command : RIGHT"
p[0] = "bf.mov(%s)" % p[1]
def p_command_PUT(p):
"command : PUT"
p[0] = "bf.put()"
def p_command_GET(p):
"command : GET"
p[0] = "bf.get()"
def p_command_loop(p):
"command : loop"
p[0] = p[1]
def p_loop(p):
"loop : WHILE sequence WEND"
p[0] = "bf.loop(lambda: %s)" % p[2]
def p_error(p): pass
# input and parse
data = sys.stdin.read()
lex.lex()
yacc.yacc()
result = yacc.parse(data)
# output
print """
from collections import defaultdict
import sys
class BF(object):
mem = defaultdict(int)
cur = 0
def inc(self, n):
self.mem[self.cur] += n
self.mem[self.cur] %= 256
def mov(self, n):
self.cur += n
def put(self):
c = chr(self.mem[self.cur])
sys.stdout.write(c)
def get(self):
c = sys.stdin.read(1)
self.mem[self.cur] = ord(c)
def loop(self, seq):
while self.mem[self.cur]:
seq()
bf = BF()
"""
print result
|
素数探索のコード:
http://labs.cybozu.co.jp/blog/kazuho/archives/2006/06/bf_prime.php
さすがに結構時間がかかるなー。Core2Duo @2.4GHzで1分くらい。
real 1m2.317s user 0m0.015s sys 0m0.031s
8行修正して[-]を単体で「リセット命令」にしたらreal 0m54.380sになった。
うーん、これ以上高速化するとなるとコピーの処理を置き換えるとかになりそうだけど、それは正規表現では無理な気がするなぁ。パースしながら出力文字列を作るのをやめて、sequenceを文字列に変換する際にmovとincだけで構成されているかどうかをチェックして…となるのかな。面倒だな。
#3964-#3978を、optimize=2 を指定すると身も蓋もなく最適化を行うように改良(?)したもの。
- http://namazu.org/~takesako/ppencode/bpencode.html
- http://pc11.2ch.net/test/read.cgi/tech/1177988460/135
あたりのソースを通してみると面白いです。
% cat helloworld.bf
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
% awk -v optimize=1 -f bf2awk.awk < helloworld.bf
BEGIN {
ix = 0
st[ix] += 10
while (st[ix]) {
st[++ix] += 7
st[++ix] += 10
st[++ix] += 3
st[++ix]++
ix -= 4
st[ix]--
}
st[++ix] += 2
printf("%c", st[ix])
printf("%c", ++st[++ix])
st[ix] += 7
printf("%c", st[ix])
printf("%c", st[ix])
st[ix] += 3
printf("%c", st[ix])
st[++ix] += 2
printf("%c", st[ix])
ix -= 2
st[ix] += 15
printf("%c", st[ix])
printf("%c", st[++ix])
st[ix] += 3
printf("%c", st[ix])
st[ix] -= 6
printf("%c", st[ix])
st[ix] -= 8
printf("%c", st[ix])
printf("%c", ++st[++ix])
printf("%c", st[++ix])
}
% awk -v optimize=2 -f bf2awk.awk < helloworld.bf
BEGIN {
print "Hello World!"
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | #
# Brainf*ck to AWK compiler, written in AWK by naoya_t/
#
BEGIN {
inst_buf = ""
ix = 0
}
{
gsub(/#.*$/,""); # comment
gsub(/[^\[\]<>+\-.,]/,""); if (/^$/) next
inst_buf = inst_buf $0
}
END {
if (inst_buf ~ /,/) getchar_used = 1
# compile
if (optimize >= 1) reset_last()
output_buf = ""
indent = ""
print "BEGIN {"
indent_incr()
if (getchar_used) indent_print("read_buf = \"\"")
if (getchar_used || optimize < 2) indent_print("ix = 0")
diff_value[">"] = diff_value["+"] = 1
diff_value["<"] = diff_value["-"] = -1
incr_decr[">"] = incr_decr["+"] = "++"
incr_decr["<"] = incr_decr["-"] = "--"
N = split(inst_buf, ops, "")
eval(ops,1,N)
if (optimize >= 1) out_last()
flush_buf(0)
indent_decr()
indent_print("}")
if (getchar_used) output_getchar_funcs()
}
function eval(ops,begin,end, i,j,op,s,till)
{
for (i=begin; i<=end; i++) {
op = ops[i]
if (op ~ /[><]/) {
ix += diff_value[op] # emulation
if (optimize >= 1) {
if (last_inst == "ix") {
last_arg += diff_value[op]
} else {
out_last()
set_last("ix", diff_value[op])
}
} else {
if (getchar_used || optimize < 2) indent_print("ix" incr_decr[op])
}
} else if (op ~ /[+-]/) {
st[ix] += diff_value[op] # emulation
if (optimize >= 1) {
if (last_inst ~ /^st\[/) {
last_arg += diff_value[op]
} else {
s = set_incr_decr("st[ix]")
if (last_inst) out_last()
set_last(s, diff_value[op])
}
} else {
if (getchar_used || optimize < 2) indent_print("st[ix]" incr_decr[op])
}
} else if (op == ".") {
s = "printf(\"%c\", st[ix])"
if (optimize >= 1) {
s = set_incr_decr(s)
if (last_inst) out_last()
}
if (getchar_used || optimize < 2)
indent_print(s)
else
putchar(st[ix])
} else if (op == ",") {
s = "st[ix] = getchar()"
if (optimize >= 1) {
s = set_incr_decr(s)
if (last_inst) out_last()
}
indent_print(s)
} else if (op == "[") {
nest = 1
for (j=i+1; j<=N; j++) {
if (ops[j] == "[") nest++
if (ops[j] == "]") nest--
if (nest == 0) { till = j ; break }
}
while_loop(ops, i+1, till-1)
i = till
} else {
;
}
}
}
function while_loop(ops,begin,end, i,j,putchar_used)
{
putchar_used = 0
for (j=begin; j<=end; j++) if (ops[j] == ".") { putchar_used = 1; break }
if (getchar_used || optimize < 2) {
out_last()
indent_print("while (st[ix]) {")
indent_incr()
eval(ops,begin,end)
if (optimize >= 1) out_last()
indent_decr()
indent_print("}")
} else {
while (st[ix]) eval(ops,begin,end)
}
}
function putchar(ch)
{
if (ch == 10 || ch == 13) {
flush_buf(1)
} else {
output_buf = output_buf sprintf("%c", st[ix])
}
}
function flush_buf(cr)
{
if (output_buf) {
gsub(/\\/, "\\\\", output_buf)
if (cr)
indent_print("print \"" output_buf "\"")
else
indent_print("printf(\"%s\", \"" output_buf "\")")
output_buf = ""
}
}
function indent_incr()
{
indent = " " indent
}
function indent_decr()
{
indent = substr(indent, 3)
}
function indent_print(line)
{
print indent line
}
function set_last(inst,arg)
{
last_inst = inst
last_arg = arg
}
function reset_last()
{
last_inst = ""
last_arg = 0
}
function out_last( diff)
{
if (last_inst ~ /ix/) {
if (last_arg > 1)
diff = " += " last_arg
else if (last_arg == 1)
diff = "++"
else if (last_arg == 0)
diff = ""
else if (last_arg == -1)
diff = "--"
else if (last_arg < -1)
diff = " -= " (0 - last_arg)
if (diff && (getchar_used || optimize < 2)) indent_print(last_inst diff)
}
last_inst = ""
}
function set_incr_decr(s)
{
if (last_inst == "ix") {
if (last_arg == 1) { gsub(/ix/, "++ix", s); last_inst = "" }
else if (last_arg == -1) { gsub(/ix/, "--ix", s); last_inst = "" }
} else if (last_inst ~ /^st\[/ && s ~ /printf/) {
if (last_arg == 1) { gsub(/st\[ix\]/, "++" last_inst, s); last_inst = "" }
else if (last_arg == -1) { gsub(/st\[ix\]/, "--" last_inst, s); last_inst = "" }
}
return s
}
function output_getchar_funcs()
{
print "function getchar( ch)"
print "{"
print " if (read_buf ~ /^$/) {"
print " if (eof) return -1"
# print " if ((getline read_buf) < 1) { eof = 1; return -1 }"
print " if (getline != 1) { eof = 1; return -1 } ; read_buf = $0" # a2p
print " }"
print " ch = ord(substr(read_buf,1,1))"
print " read_buf = substr(read_buf,2)"
print " return ch"
print "}"
print "function ord(ch, ofs)"
print "{"
print " if (ch == \"\\t\") return 9"
print " if (ch == \"\\n\") return 13"
# print " ofs = index(\" !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\", ch)"
print " if (ch == \"@\") return 64" # a2p
print " ofs = index(\" !\\\"#$%&'()*+,-./0123456789:;<=>?.ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\", ch)" # a2p
print " return ofs ? 31 + ofs : 0"
print "}"
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | + int getContinuousChar(std::istream& is, char c)
+ {
+ int count = 1;
+ char p;
+ while (is >> p) {
+ if (p != c) break;
+ count++;
+ }
+ is.unget();
+ return count;
+ }
+#if 0
case '+': inc(dword [ebp]); break;
case '-': dec(dword [ebp]); break;
case '>': add(ebp, 4); break;
case '<': sub(ebp, 4); break;
#else
case '+':
case '-':
{
int count = getContinuousChar(is, c);
if (count == 1) {
c == '+' ? inc(dword [ebp]) : dec(dword [ebp]);
} else {
add(dword [ebp], (c == '+' ? count : -count));
}
}
break;
case '>':
case '<':
{
int count = getContinuousChar(is, c);
add(ebp, 4 * (c == '>' ? count : -count));
}
break;
#endif
|
Pythonだとこれだけ時間がかかるんですね。 参考になります。 Scalaでもやってみたところ、#3960のコードで 出力したものを、コンパイルせずインタプリタで 実行しても1秒以内に結果が出ました。 Scalaは実はできる子です(笑
素直に変換。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | import std.stdio;
import std.file;
import std.string;
void main(string[] args){
string bfCode = cast(string)read(args[1]);
string[] buf;
uint ptrIndex;
uint maxPtrIndex;
uint indentLevel = 1;
void addLine(string line){
buf ~= repeat("\t", indentLevel) ~ line;
}
foreach(c; bfCode){
switch(c){
case '>':
addLine("ptr++;");
if(++ptrIndex > maxPtrIndex){
maxPtrIndex++;
}
break;
case '<':
addLine("ptr--;");
ptrIndex--;
break;
case '+':
addLine("mem[ptr]++;");
break;
case '-':
addLine("mem[ptr]--;");
break;
case '.':
addLine("putchar(cast(char)mem[ptr]);");
break;
case ',':
addLine("mem[ptr] = cast(ubyte)getchar();");
break;
case '[':
addLine("while(mem[ptr]){");
indentLevel++;
break;
case ']':
indentLevel--;
addLine("}");
break;
default:
}
}
writefln("import std.c.stdio;");
writefln("void main(){");
writefln("\tubyte mem[", maxPtrIndex + 1, "];");
writefln("\tuint ptr;");
writefln(buf.join("\n"));
writefln("}");
}
|
こんなもの dict にするのがいいに決まっている、と思ったら Dan さんに先こされているし。 仕方がないので、インデント情報も dict に突っ込んだやつを。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | from __future__ import with_statement
import sys
def brainfuck_compile(source):
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
actions = {
'>': ('pointer += 1', 0),
'<': ('pointer -= 1', 0),
'+': ('tape[pointer] = tape.get(pointer, 0) + 1', 0),
'-': ('tape[pointer] = tape.get(pointer, 0) - 1', 0),
'.': ('sys.stdout.write(chr(tape.get(pointer, 0)))', 0),
',': ('tape[pointer] = sys.stdin.read(1)', 0),
'[': ('while tape.get(pointer, 0):', 1),
']': ('', -1),
}
generated = StringIO()
print >>generated, 'import sys'
print >>generated, 'tape, pointer = dict(), 0'
indent = 0
for c in source:
if c.isspace(): continue
stmt, indent_diff = actions[c]
print >>generated, '%*s%s' % (indent, '', stmt)
indent += indent_diff
return generated.getvalue()
def iterchar(fp):
for line in fp:
for c in line: yield c
def main(args):
if args:
for arg in args:
with file(arg) as fp:
code = brainfuck_compile(iterchar(fp))
print code
else:
code = brainfuck_compile(iterchar(sys.stdin))
print code
if __name__ == '__main__':
main(sys.argv[1:])
|
うーん、それはちょっと違いますね。 #3960のコードはコードを一つの関数の中にローカルに展開しているのに対して、上のコードは一つ一つの命令がメソッド呼び出しですから。PythonとScalaの性能の違いと言うより、生成されたコードの質の違い思います。
# 要するに僕のコードが生成したのは質が悪いと!orz
毎回bf.fooとメソッド名の解決をしているのをやめるとreal:0m32.481sになり、きちんとインデントするようにしたら(毎回関数呼び出しをするのをやめたら)real:0m15.548sになりました。
それでもScalaには全然追いつかないのか…orz
これ
tape[ptr] = sys.stdin.read(1)
の部分でordしていないのでテープに文字列が書き込まる気が。
コードを張り忘れたので。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | def p_sequence(p):
"sequence : sequence command"
p[0] = p[1] + p[2]
def p_sequence_command(p):
"sequence : command"
p[0] = p[1]
def p_command_PLUS(p):
"command : PLUS"
p[0] = "\nmem[cur] = (mem[cur] + %s) %% 256" % p[1]
def p_command_MINUS(p):
"command : MINUS"
p[0] = "\nmem[cur] = (mem[cur] - %s) %% 256" % p[1]
def p_command_LEFT(p):
"command : LEFT"
p[0] = "\ncur -= %s" % p[1]
def p_command_RIGHT(p):
"command : RIGHT"
p[0] = "\ncur += %s" % p[1]
def p_command_PUT(p):
"command : PUT"
p[0] = "\nsys.stdout.write(chr(mem[cur]))"
def p_command_GET(p):
"command : GET"
p[0] = "\nmem[cur] = ord(sys.stdin.read(1))"
def p_command_RESET(p):
"command : RESET"
p[0] = "\nmem[cur] = 0"
def p_command_loop(p):
"command : loop"
p[0] = p[1]
def p_loop(p):
"loop : WHILE sequence WEND"
p[0] = "\nwhile mem[cur]:%s" % p[2].replace("\n", "\n ")
def p_error(p): pass
# input and parse
data = sys.stdin.read()
lex.lex()
yacc.yacc()
result = yacc.parse(data)
# output
print """
from collections import defaultdict
import sys
mem = defaultdict(int)
cur = 0
"""
print result
|
あ、確かにそうですね。見落としてました。 せっかくなので#3952の素朴なタイプでも手元で時間とって見ましたが、 dppさんの#3952:55秒程度 にしおさんの#3988:2分10秒程度 でした。 あと、メモリとしてdictを使っているけど、 [0]*0xffffみたいにリストで長さ決めうちにすると だいぶマシですね。55 -> 31秒程度まで短縮できました。
正規表現とハッシュの合わせ技で
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | use strict;
use warnings;
sub compile {
my $source = shift;
my $out = '';
my $code = 'sub{no warnings;my(@t, $p, @o);';
my %patterns = (
'>' => sub { sprintf '$p+=%d;', length $_[0] },
'<' => sub { sprintf '$p-=%d;', length $_[0] },
'+' => sub { sprintf '$t[$p]+=%s;', length $_[0] },
'-' => sub { sprintf '$t[$p]-=%d;', length $_[0] },
'.' => sub { sprintf 'push @o,$t[$p];' },
',' => sub { sprintf '$t[$p] = shift;' },
'[' => sub { 'while($t[$p]){' },
']' => sub { '}' },
);
my $re = qr/>+|<+|\++|-+|\.|,|\[|\]/;
my $tmp = $source;
$tmp =~ tr/<>+\-,.[]//cd;
$tmp =~ s/$re/$patterns{substr($&, 0, 1)}->($&)/ge;
$code .= $tmp;
$code .= '@o;}';
}
sub main {
my $source = do { local $/; <> };
my $code = compile($source);
my @out = do { eval $code }->();
print pack('c*', @out);
}
main;
|
引数にBFのプログラムを与えて実行します。 java Sample '>,----------[>,----------]<[++++++++++.<]' BF.javaという名前のファイルが作られるので、さらにjavacでコンパイルします。 上記の例は入力を逆順に出力するプログラム(入力の終了は改行(\n))です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import java.io.*;
import java.util.*;
public class Sample {
static final String NL = System.getProperty("line.separator");
static final String HEADER = "import java.io.*;"+NL+NL+"public class BF {"+NL+
" static final int SIZE = 32768;"+NL+" static byte[] a = new byte[SIZE];"+NL+
" public static void main(String[] args) throws IOException {"+NL+
"\tBufferedReader r = new BufferedReader(new InputStreamReader(System.in));"+NL+
"\tint ptr = 0;";
static final String FOOTER = "\tSystem.out.println();"+NL+" }"+NL+"}";
static HashMap<Character, String> bfCode = new HashMap<Character, String>();
static {
bfCode.put('>', "\tptr++;");
bfCode.put('<', "\tptr--;");
bfCode.put('+', "\ta[ptr]++;");
bfCode.put('-', "\ta[ptr]--;");
bfCode.put('.', "\tSystem.out.write(a[ptr]);");
bfCode.put(',', "\ta[ptr] = (byte)r.read();");
bfCode.put('[', "\twhile (a[ptr] != 0) {");
bfCode.put(']', "\t}");
}
public static void main(String[] args) throws IOException {
PrintWriter w = new PrintWriter("BF.java");
w.println(HEADER);
for (char c : args[0].toCharArray()) {
String code = bfCode.get(c);
if (code != null)
w.println(code);
}
w.println(FOOTER);
w.close();
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #include <iostream>
#include <fstream>
#include <cctype>
using namespace std;
#define REP(n) for (size_t i = 0; i < n; ++i)
void compile(istream& in, ostream& out)
{
size_t indent = 0;
#define OUTPUT(s) REP(indent) out << "\t"; out << s << endl;
OUTPUT("#include <stdio.h>");
OUTPUT("#include <mem.h>");
OUTPUT("");
OUTPUT("int main()");
OUTPUT("{");
++indent;
OUTPUT("unsigned char buf[33000], *p = buf;");
OUTPUT("memset(buf, 0, sizeof(buf));");
OUTPUT("");
char c;
while (in.get(c))
{
if (isspace(c))
{
// pass
}
else if (c == '<')
{
OUTPUT("--p;");
}
else if (c == '>')
{
OUTPUT("++p;");
}
else if (c == '+')
{
OUTPUT("++*p;");
}
else if (c == '-')
{
OUTPUT("--*p;");
}
else if (c == '.')
{
OUTPUT("putchar(*p);");
}
else if (c == ',')
{
OUTPUT("*p = getchar();");
}
else if (c == '[')
{
OUTPUT("while (*p)");
OUTPUT("{");
++indent;
}
else if (c == ']')
{
--indent;
OUTPUT("}");
}
}
--indent;
OUTPUT("}");
#undef OUTPUT
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
cerr << "usage: name" << endl;
return -1;
}
compile(ifstream(argv[1]), cout);
}
|
スタックオーバーフローすると反対側に出ます。
see: 配列の中の一つの値を変更しようとすると全部変更されてしまう!?!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | let parse = function
| '>' -> "stackincr p;"
| '<' -> "stackdecr p;"
| '+' -> "incr code.(!p);"
| '-' -> "decr code.(!p);"
| '.' -> "print_char (char_of_int !(code.(!p)));"
| ',' -> "code.(!p) := read_char ();"
| '[' -> "while 0 <> !(code.(!p)) do"
| ']' -> "done;;"
| _ -> "" ;;
let main ?(stacksize=256) filename =
let file = open_in filename in
Printf.printf "\
let stackincr x = if %d < (incr x; !x) then x:=0;;\n\
let stackdecr x = if 0 > (decr x; !x) then x:=%d;;\n\
let read_char () = int_of_char (read_line () ).[0];;\n\
let code = Array.init %d (fun i -> ref 0);;\n\
let p = ref 0;;\n\n"
(stacksize-1) (stacksize-1) stacksize;
let rec read_loop () =
try
let next = input_line file in
String.iter (fun x -> print_endline (parse x)) next;
read_loop ();
with
End_of_file -> close_in file
in read_loop ();;
if !(Sys.interactive)=false then
match (Array.length Sys.argv) with
| n when n=2 -> main Sys.argv.(1)
| n when 2<n -> main ~stacksize:(int_of_string Sys.argv.(2)) Sys.argv.(1)
| n -> print_endline "usage: ocaml bf2ml.ml [stacksize] filename"
;;
|
ああ、スタックじゃなくてー
リングバッファ?
',' の処理が変じゃありません? echo を試してみましたが、各行頭の一文字しか出力されませんでした。
書き換えてみました。echo はそれらしい動きをしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @@ -4,7 +4,7 @@
| '+' -> "incr code.(!p);"
| '-' -> "decr code.(!p);"
| '.' -> "print_char (char_of_int !(code.(!p)));"
- | ',' -> "code.(!p) := read_char ();"
+ | ',' -> "code.(!p) := input_byte stdin;"
| '[' -> "while 0 <> !(code.(!p)) do"
| ']' -> "done;;"
| _ -> "" ;;
@@ -14,7 +14,6 @@
Printf.printf "\
let stackincr x = if %d < (incr x; !x) then x:=0;;\n\
let stackdecr x = if 0 > (decr x; !x) then x:=%d;;\n\
- let read_char () = int_of_char (read_line () ).[0];;\n\
let code = Array.init %d (fun i -> ref 0);;\n\
let p = ref 0;;\n\n"
(stacksize-1) (stacksize-1) stacksize;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | bf <- function(str){
cat("ptr <- 1", "\n")
cat("buf <- numeric(1)", "\n")
for(c in unlist(strsplit(str, ''))){
switch(c,
'>' = cat("ptr <- ptr+1; if(is.na(buf[ptr])) buf[ptr] <- 0", "\n"),
'<' = cat("ptr <- ptr-1; if(is.na(buf[ptr])) buf[ptr] <- 0", "\n"),
'+' = cat("buf[ptr] <- buf[ptr]+1", "\n"),
'-' = cat("buf[ptr] <- buf[ptr]-1", "\n"),
'.' = cat("cat(rawToChar(as.raw(buf[ptr])))", "\n"),
',' = cat("buf[ptr] <- as.integer(charToRaw(readLines(n=1)))", "\n"),
'[' = cat("while(buf[ptr]){", "\n"),
']' = cat("}", "\n"))
}
}
|
トランスレーターといえばcamlp4ということで、camlp4の3.10版で書いてみました。 LexerはBF用に書いていますが、TokenはOCaml用のものを流用しています。 Parserのコンパイル: ocamlc -c -I +camlp4 -pp camlp4orf dk80.ml Parserを利用したBFファイルのトランスレート結果の確認: camlp4orf dk80.cmo -impl helloworld.bf Parserを利用したBFファイルのコンパイル: ocamlc -o dk80 -pp "camlp4 -printer Camlp4OCamlPrinter dk80.cmo -impl" -impl helloworld.bf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | open Camlp4.PreCast
module MyLexer = struct
module Loc = Loc
module Token = Token
module Error = Token.Error
let mk () loc sc =
let rec next n = match Stream.peek sc with
| None -> Some(EOI, loc)
| Some x -> let _ = Stream.junk sc in (match x with
|'>'|'<'|'+'|'-'|'.'|','|'['|']'-> Some((SYMBOL (Printf.sprintf "%c" x)), loc)
| _ -> next n)
in Stream.from next
end
module BF = MakeGram MyLexer
let expr = BF.Entry.mk "expr"
let repr = BF.Entry.mk "repr"
let term = BF.Entry.mk "term"
EXTEND BF
expr: [[
t = repr; `EOI -> <:str_item<
let p = ref 0 in
let a = Array.create 1024 0 in
let inc a p = a.(!p) := a.(!p) + 1 in
let dec a p = a.(!p) := a.(!p) - 1 in
let prt a p = print_char (char_of_int a.(!p)) in
( $t$ )
>>
]];
repr: [[
t = term -> t
| t = term; u = repr -> <:expr< $t$; $u$ >>
]];
term: [[
">" -> <:expr< incr p >>
| "<" -> <:expr< decr p >>
| "+" -> <:expr< inc a p >>
| "-" -> <:expr< dec a p >>
| "." -> <:expr< prt a p >>
| "," -> <:expr< a.(!p) := input_byte stdin >>
| "["; t = repr; "]" -> <:expr< while a.(!p) <> 0 do ( $t$ ) done >>
]];
END
let myparse ?(directive_handler = fun _ -> None) _loc cs = BF.parse expr _loc cs
let myparse2 ?(directive_handler = fun _ -> None) _loc _ = <:sig_item< >>
let _ = Camlp4.Register.register_parser myparse myparse2;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | using System;
using System.Collections.Generic;
namespace Kinoko
{
/*
BrainF*ck
*/
class Program
{
public static void Main(string[] args)
{
LinkedList<int> Tape = new LinkedList<int>();
Tape.AddLast(0);
LinkedListNode<int> Head = Tape.First;
for(int i=0; i < args[0].Length; i++)
{
String op = args[0].Substring(i,1);
switch(op)
{
case "+":
{
Head.Value++;
break;
}
case "-":
{
Head.Value--;
break;
}
case "<":
{
if(Head == Tape.First)
{
Tape.AddFirst(0);
}
Head=Head.Previous;
break;
}
case ">":
{
if(Head == Tape.Last)
{
Tape.AddLast(0);
}
Head=Head.Next;
break;
}
case "[":
{
if(Head.Value == 0)
{
int nest = 1;
int step = 1;
while(nest > 0)
{
if(args[0].Substring(i+step, 1) == "[")
{
nest++;
}
else if(args[0].Substring(i+step, 1) == "]")
{
nest--;
}
step++;
if(i+step > args[0].Length)
{
break;
}
}
i += step;
}
break;
}
case "]":
{
if(Head.Value != 0)
{
int nest = 1;
int step = 1;
while(nest > 0)
{
if(args[0].Substring(i-step, 1) == "]")
{
nest++;
}
else if(args[0].Substring(i-step, 1) == "[")
{
nest--;
}
step++;
if(i-step < 0)
{
break;
}
}
i -= step;
}
break;
}
case ".":
{
Console.Write("{0}", ((Char)Head.Value));
break;
}
case ",":
{
int input = 0;
Console.Write("input:");
input = Console.Read();
Head.Value = input;
break;
}
default:
{
break;
}
}
}
}
}
}
|
unsafeを使って、 C#でポインタを使ってみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | using System;
using System.IO;
namespace BrainFuck
{
class Program
{
static void Main(string[] args)
{
StreamWriter sw = new StreamWriter("C:\\txt.txt");
sw.WriteLine("using System;");
sw.WriteLine("namespace BrainFuck{");
sw.WriteLine("class Program{");
sw.WriteLine("unsafe static void Main(string[] args){");
sw.WriteLine("fixed (byte* b = new byte[1024]){");
sw.WriteLine("byte* ptr = b;");
foreach (char s in args[0])
{
switch (s)
{
case '>':
sw.WriteLine("ptr++;");
break;
case '<':
sw.WriteLine("ptr--;");
break;
case '+':
sw.WriteLine("(*ptr)++;");
break;
case '-':
sw.WriteLine("(*ptr)--;");
break;
case '.':
sw.WriteLine("Console.Write((char)*ptr);");
break;
case ',':
sw.WriteLine("*ptr = Console.Read();");
break;
case '[':
sw.WriteLine("while(*ptr != 0){");
break;
case ']':
sw.WriteLine("}");
break;
}
}
sw.WriteLine("}}}}");
sw.Close();
}
}
}
|
strictもwarningsも通るけど一文。
Dan the Brainf.cker
1 2 3 4 5 6 7 8 9 10 11 12 13 | #!/usr/local/bin/perl
use strict;
use warnings;
s/(.)/do{no warnings 'syntax';my %c=qw!
< $p--;
> $p++;
+ $d[$p]++;
- $d[$p]--;
. print(chr$d[$p]);
, $d[$p]=getc;
[ while($d[$p]){
] }
!;\%c}->{$1}/egx and print for(<>);
|
例)
% cat hello.bf
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
% make
ruby bfcompile.rb < hello.bf > hello.rb
ruby hello.rb
Hello World!
% cat hello.rb
class String
def until_nz
while self[self[0] + 1] != 0
replace(yield(self))
end
self
end
def refer
self.sub(/\A(.)(.*)\Z/m) do
$1 + $2.sub(/^(.{#{$1[0]}})(.)/m) do
$1 + yield($2)
end
end
end
end
stack = "\000" + "\000" * 256
stack.refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
until_nz {|stack|
stack.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
to_s
}.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| putc(mem) }.
to_s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | puts <<'EOS'
class String
def until_nz
while self[self[0] + 1] != 0
replace(yield(self))
end
self
end
def refer
self.sub(/\A(.)(.*)\Z/m) do
$1 + $2.sub(/^(.{#{$1[0]}})(.)/m) do
$1 + yield($2)
end
end
end
end
stack = "\000" + "\000" * 256
EOS
print 'stack.'
code = {
'>' => [ 'sub(/./m) {|p| (p[0] + 1).chr }.' ],
'<' => [ 'sub(/./m) {|p| (p[0] - 1).chr }.' ],
'+' => [ 'refer {|mem| (mem[0] + 1).chr }.' ],
'-' => [ 'refer {|mem| (mem[0] - 1).chr }.' ],
'.' => [ 'refer {|mem| putc(mem) }.' ],
',' => [ 'refer { STDIN.getc }.' ],
'[' => [ 'until_nz {|stack|', 'stack.' ],
']' => [ 'to_s', '}.' ],
}
while ch = STDIN.getc
next if code[ch.chr].nil?
puts code[ch.chr]
end
puts 'to_s'
|
少しだけ最適化を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | #include <stdio.h>
void putnest(FILE* fout,int n){
while(n--){
fprintf(fout,"\t");
};
}
int main(){
char filename[]="test.bf";
FILE *fin;
FILE *fout;
int cmd=' ',lastcmd;
int count=0;
int nest=1;
fin =fopen(filename,"r");
fout=fopen("bf.c","w");
fprintf(fout,"#include<stdio.h>\n");
fprintf(fout,"\n");
fprintf(fout,"int main(){\n");
fprintf(fout,"\tint buf[256];\n");
fprintf(fout,"\tint *ptr;\n");
fprintf(fout,"\tint i;");
fprintf(fout,"\n");
fprintf(fout,"\tfor(i=0;i<256;i++) buf[i]=0;\n");
fprintf(fout,"\tptr=buf;\n");
fprintf(fout,"\n");
do{
lastcmd=cmd;
cmd=fgetc(fin);
if(lastcmd!=cmd){
switch(lastcmd){
case '+':
case '-':
case '>':
case '<':
putnest(fout,nest);
fprintf(fout,"%s%c",lastcmd&0x10?"ptr":"(*ptr)",lastcmd&0x02?'+':'-');
if(count==1)
fprintf(fout,"%c;\n",lastcmd&0x02?'+':'-');
else
fprintf(fout,"=%d;\n",count);
count=0;
break;
}
}
switch(cmd){
case '+':
case '-':
case '>':
case '<':
count++;
break;
case '[':
putnest(fout,nest);
fprintf(fout,"while(*ptr){\n");
nest++;
break;
case ']':
nest--;
putnest(fout,nest);
fprintf(fout,"}\n");
break;
case '.':
putnest(fout,nest);
fprintf(fout,"putchar(*ptr);\n");
break;
case ',':
putnest(fout,nest);
fprintf(fout,"*ptr=getchar();\n");
break;
}
}while(cmd!=EOF);
fprintf(fout," return 0;\n");
fprintf(fout,"}\n");
fprintf(fout,"\n");
return 0;
}
|
メモリとポインタを用意しておいて,あとは命令を1つずつRubyに置き換えるだけ。他の人と々アプローチだけど,いくらかわかりやすく書いたつもり。
書いてて気がついたけど,BFのループって終了判定が2カ所(前後)にあるんだな。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | print <<"EOBF"
class BF
def initialize
@memory = [0]
@pointer = 0
end
def current
@memory[@pointer]
end
def incr
@memory[@pointer] += 1
end
def decr
@memory[@pointer] -= 1
end
def right
@pointer += 1
unless @memory[@pointer]
@memory << 0
end
end
def left
@pointer -= 1
end
def set(c)
@memory[@pointer] = c
end
def printc
print @memory[@pointer].chr
end
end
bf = BF.new
EOBF
commands = {
">" => "bf.right",
"<" => "bf.left",
"+" => "bf.incr",
"-" => "bf.decr",
"." => "bf.printc",
"," => "c = STDIN.getc; bf.set(c)",
"[" => "begin; break if bf.current.zero?",
"]" => "end until bf.current.zero?"
}
ARGF.each_byte do |c|
cmd = commands[c.chr]
puts cmd if cmd
end
|
1 2 3 4 5 6 7 | using System;
using System.IO;
static class BFCompiler {
public static void Main(String[] args) {
Console.WriteLine(0 == args.Length || !File.Exists(args[0]) ? "usage: bfc [sourcefile]" : "using System;static class BF{static void Main(){byte[]m=new byte[256];int p=0;" + new StreamReader(args[0]).ReadToEnd().Replace("]","}").Replace("[","while(m[p]!=0){").Replace(".","Console.Write((char)m[p]);").Replace(",","m[p]=(byte)Console.Read();").Replace("+","m[p]++;").Replace("-","m[p]--;").Replace(">","p++;").Replace("<","p--;") + "}}");
}
}
|
うーんと、]が条件判断を行わなわずに[に無条件ジャンプしても[が判断して]の後にジャンプするので同じということでしょう。
にしおさんのlex/yacc見てなるほど思ったので、本家lexのお勉強。 下のコードをbf.lとすると % lex bf.l % cc lex.yy.c -ll -o bfc としてコンパイル。 % ./bfc <hello.bf >hello.c % cc hello.c % ./a.out Hello World! のように使います。
一応 ++++++ → *pt = 6 みたいなことするようにしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | %{
#include <stdio.h>
#include <string.h>
%}
%option noyywrap
%%
\++ printf("*pt+=%d;\n", strlen(yytext));
\-+ printf("*pt-=%d;\n", strlen(yytext));
\>+ printf("pt+=%d;\n", strlen(yytext));
\<+ printf("pt-=%d;\n", strlen(yytext));
\, puts("*pt=getchar();");
\. puts("putchar(*pt);");
\[ puts("while(*pt){");
\] puts("}");
. ;
%%
int main(int argc, char **argv)
{
puts("#include <stdio.h>\n"
"int mem[30000];\n"
"int *pt = mem;\n"
"int main(){");
yylex();
puts("return 0;\n}");
return 0;
}
|
無理矢理ですけど健全なマクロで解いてみました。
なお、出力されたコードもbf.scmを参照するのでロードパスを適切に設定しておく必要があります。
;;; bf-test.scm (サンプル)
(use bf)
(bf-compiler "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+." 32)
;;; gosh bf-test.scm | gosh で"Hello, world!"が出力されます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | ;;; bf.scm
(define-module bf
(export-all))
(define-syntax %bf-compiler
(syntax-rules (< > + - |.| |,|)
((_ proc)
proc)
((_ proc < rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (cons (cadr tape) (car tape))
(cddr tape)))
proc)
rest ...))
((_ proc > rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (cdar tape)
(cons (caar tape) (cdr tape))))
proc)
rest ...))
((_ proc + rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (car tape) (cons (modulo (+ (cadr tape) 1) 256)
(cddr tape))))
proc)
rest ...))
((_ proc - rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (car tape) (cons (modulo (- (cadr tape) 1) 256)
(cddr tape))))
proc)
rest ...))
((_ proc |.| rest ...)
(%bf-compiler (compose (lambda (tape)
(write-byte (cadr tape))
(flush)
tape)
proc)
rest ...))
((_ proc |,| rest ...)
(%bf-compiler (compose (lambda (tape)
(let1 c (read-byte)
(cons (car tape)
(cons (if (eof-object? c) 0 c) (cddr tape)))))
proc)
rest ...))
((_ proc [body ...] rest ...)
(%bf-compiler (compose (lambda (tape)
(if (eq? (cadr tape) 0)
tape
(let1 f (%bf-compiler values body ...)
(let loop ((t (f tape)))
(if (eq? (cadr t) 0)
t
(loop (f t)))))))
proc)
rest ...))))
(define (bf-compiler str n)
(write '(use bf))
(write `((%bf-compiler values ,@(with-input-from-string
(regexp-replace-all #/[.,+-<>]/ #`"(,str)" "|\\0|")
read))
',(tape n))))
(define (tape n)
(let1 t (make-list n 0)
(cons t t)))
(provide "bf")
|
あっ、tapeの実装がちょっと変だった。前のやつだと<でポインタが戻りすぎたときに変な挙動になります(修正版ではエラーになるはず)。
まあ、変なプログラムを書かなければさっきのでも問題ないはずです。
1 2 3 | (define (tape n)
(let1 t (make-list n 0)
(cons '() t)))
|
ぐぁ、ポインタの進む向き逆にしてた。コードをあげなおします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | ;;; bf.scm
(define-module bf
(export-all))
(define-syntax %bf-compiler
(syntax-rules (< > + - |.| |,|)
((_ proc)
proc)
((_ proc > rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (cons (cadr tape) (car tape))
(cddr tape)))
proc)
rest ...))
((_ proc < rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (cdar tape)
(cons (caar tape) (cdr tape))))
proc)
rest ...))
((_ proc + rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (car tape) (cons (modulo (+ (cadr tape) 1) 256)
(cddr tape))))
proc)
rest ...))
((_ proc - rest ...)
(%bf-compiler (compose (lambda (tape)
(cons (car tape) (cons (modulo (- (cadr tape) 1) 256)
(cddr tape))))
proc)
rest ...))
((_ proc |.| rest ...)
(%bf-compiler (compose (lambda (tape)
(write-byte (cadr tape))
(flush)
tape)
proc)
rest ...))
((_ proc |,| rest ...)
(%bf-compiler (compose (lambda (tape)
(let1 c (read-byte)
(cons (car tape)
(cons (if (eof-object? c) 0 c) (cddr tape)))))
proc)
rest ...))
((_ proc [body ...] rest ...)
(%bf-compiler (compose (lambda (tape)
(if (eq? (cadr tape) 0)
tape
(let1 f (%bf-compiler values body ...)
(let loop ((t (f tape)))
(if (eq? (cadr t) 0)
t
(loop (f t)))))))
proc)
rest ...))))
(define (bf-compiler str n)
(write '(use bf))
(write `((%bf-compiler values ,@(with-input-from-string
(regexp-replace-all #/[.,+-<>]/ #`"(,str)" "|\\0|")
read))
',(tape n))))
(define (tape n)
(let1 t (make-list n 0)
(cons '() t)))
(provide "bf")
|
バグ発見。ランタイムテンプレート コメント内のファイル名 bfrts.templ → bfrts.tmpl main の定義の右辺 execute → run
高階関数を使ってみたい年頃なので高階関数使った版findpc を書いてみました。 findpc はループがネストしているとき '[' が来るごとに何度も呼ばれるのが ちょっともったいない気もします。 それと hClose oh しないとバッファが flush されませんでした。
1 2 3 4 5 6 7 8 9 10 11 | -- import Maybe
-- import Control.Monad
-- -- where pc = findpc [] cs
-- where pc = findpc cs
findpc cs = fromMaybe (error "syntax error")
$ msum $ snd $ mapAccumL f 0 cs where
f 0 (p,']') = (0, Just (p+1))
f s (_,c) = (s+d, Nothing) where
d = case c of; ']' -> (-1); '[' -> 1; _ -> 0
|
なでしこへ変換。1.50047以降用。出力されるコードはもはや日本語でもなんでもない(^ ^;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | !変数宣言が必要
FNとは文字列
BFCODEとは文字列
NAKOとは文字列=`mとは配列
pとは整数=0
//コード開始
`
INDとは文字列=""
TABとは整数=0
FN="bf"の""でファイル選択
もし(FN="")ならば、おわり
BFCODE=FNを開く
BFCODEの改行を""に置換して文字列分解
反復
対象で条件分岐
">"ならば
NAKO=NAKO&IND&`p=p+1`&改行
"<"ならば
NAKO=NAKO&IND&`p=p-1`&改行
"+"ならば
NAKO=NAKO&IND&`m[p]=m[p]+1`&改行
"-"ならば
NAKO=NAKO&IND&`m[p]=m[p]-1`&改行
"."ならば
NAKO=NAKO&IND&`継続表示(CHR(m[p]))`&改行
","ならば
NAKO=NAKO&IND&`m[p]=INT("${HEXエンコード(文字抜出(入力(空),1,1))}")`&改行
"["ならば
NAKO=NAKO&IND&`(m[p]<>0)の間`&改行
TAB=TAB+1
IND=リフレイン(タブ,TAB)
"]"ならば
TAB=TAB-1
IND=リフレイン(タブ,TAB)
「nako」のファイル名抽出(FN)&「.nako」で保存ファイル選択
NAKOをそれに保存
おわり
|
素朴な実装。 一応、連続したインクリメント・デクリメントは一つにまとめる程度の最適化はしてます。 サンプルコードは弾さんのページからコピペしました。 % cat hello.bf ++++++++++[>+++++++>++++++++++>+++>+<<<<-] >++.>+.+++++++..+++.>++.<<+++++++++++++++. >.+++.------.--------.>+.>. % ./bf.byte hello.bf % cat hello.ml (* runtime *) let p = ref 0 let buf_size = 256 let make_buf () = Array.make buf_size 0 let buf = ref (make_buf ()) let extend () = buf := Array.append !buf (make_buf ()) let pincr n = p := !p + n; if Array.length !buf >= !p then extend () let pdecr n = p := !p - n; if !p < 0 then failwith "invalid pointer address!" type vtype = INCR | DECR let vset v n = !buf.(!p) <- !buf.(!p) + (match v with INCR -> n | DECR -> -n) let output () = print_char (char_of_int !buf.(!p)) let input () = !buf.(!p) <- int_of_char (input_char stdin) (* end of runtime *) let () = (* begin of code *) vset INCR 10; while !buf.(!p) <> 0 do pincr 1; vset INCR 7; pincr 1; vset INCR 10; pincr 1; vset INCR 3; pincr 1; vset INCR 1; pdecr 4; vset DECR 1; done; pincr 1; vset INCR 2; output (); pincr 1; vset INCR 1; output (); vset INCR 7; output (); vset INCR 3; output (); pincr 1; vset INCR 2; output (); pdecr 2; vset INCR 15; output (); pincr 1; output (); vset INCR 3; output (); vset DECR 6; output (); vset DECR 8; output (); pincr 1; vset INCR 1; output (); pincr 1; output (); (* end of code *) ()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | (* lexer.mll *)
{
exception Eof
type instruction =
| PINCR of int | PDECR of int
| VINCR of int | VDECR of int
| OUTPUT | INPUT | FJUMP | BJUMP
}
rule token = parse
| ('>' +) as s { PINCR (String.length s) }
| ('<' +) as s { PDECR (String.length s) }
| ('+' +) as s { VINCR (String.length s) }
| ('-' +) as s { VDECR (String.length s) }
| ('.' +) { OUTPUT }
| (',' +) { INPUT }
| ('[' +) { FJUMP }
| (']' +) { BJUMP }
| ['\n' '\r'] { token lexbuf }
| eof { raise Eof }
(* bf.ml *)
let runtime = "\
(* runtime *)
let p = ref 0
let buf_size = 256
let make_buf () = Array.make buf_size 0
let buf = ref (make_buf ())
let extend () = buf := Array.append !buf (make_buf ())
let pincr n = p := !p + n; if Array.length !buf >= !p then extend ()
let pdecr n = p := !p - n; if !p < 0 then failwith \"invalid pointer address!\"
type vtype = INCR | DECR
let vset v n = !buf.(!p) <- !buf.(!p) + (match v with INCR -> n | DECR -> -n)
let output () = print_char (char_of_int !buf.(!p))
let input () = !buf.(!p) <- int_of_char (input_char stdin)
(* end of runtime *)
"
let code_begin = "\
let () =
(* begin of code *)
"
let code_end = "\
(* end of code *)
()
"
open Lexer
let code_of_instruction token =
let to_s = string_of_int in
match token with
| PINCR i -> "pincr " ^ (to_s i) ^ ";\n"
| PDECR i -> "pdecr " ^ (to_s i) ^ ";\n"
| VINCR i -> "vset INCR " ^ (to_s i) ^ ";\n"
| VDECR i -> "vset DECR " ^ (to_s i) ^ ";\n"
| OUTPUT -> "output ();\n"
| INPUT -> "input ();\n"
| FJUMP -> "while !buf.(!p) <> 0 do\n"
| BJUMP -> "done;\n"
let translate str =
let buf = Buffer.create ((String.length runtime) * 2) in
let add = Buffer.add_string buf in
let () = add runtime; add code_begin in
let lexbuf = Lexing.from_string str in
try
while true do
add (code_of_instruction (Lexer.token lexbuf))
done;
failwith "unreached"
with Lexer.Eof ->
add code_end;
Buffer.contents buf
let main () =
match Sys.argv with
| [|_; fname |] -> begin
let out_file_name =
try (Filename.chop_extension fname) ^ ".ml"
with _ -> fname ^ ".ml"
in
let in_ch = open_in fname in
let buf = Buffer.create 10 in
try
while true do
Buffer.add_string buf (input_line in_ch)
done
with End_of_file ->
close_in in_ch;
let code = translate (Buffer.contents buf) in
let out_ch = open_out out_file_name in
output_string out_ch code;
close_out out_ch
end
| _ ->
print_endline ("usage: " ^ Sys.argv.(0) ^ " BF_file")
let () = if not !Sys.interactive then main ()
|
1 2 3 4 | 名前が付いてたんですかっっ。
でも上のは、スタックオーバーフローじゃなくて単に
Out of rangeなんだろうかとふらふら考えてた跡です。
すみません。
|
Pervasivesなんて聞き慣れないモジュール名だったので
リファレンスも見逃してました。基本だったのに……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | begingroupundo;
gofiletop;
$code['>'] = "#ptr=#ptr+1;";
$code['<'] = "#ptr=#ptr-1;";
$code['+'] = "#buf[#ptr]=#buf[#ptr]+1;";
$code['-'] = "#buf[#ptr]=#buf[#ptr]-1;";
$code['.'] = "insert char(#buf[#ptr]);";
$code[','] = "call get;";
$code['['] = "while(#buf[#ptr]){";
$code[']'] = "}";
#isUsedGetchar = false;
while( code != eof ) {
if( code == ',' ) #isUsedGetchar = true;
insert $code[code];
delete;
}
if( #isUsedGetchar ) {
insert "endmacro;get:if($get==\"\"&&#c<=0)$get=input(\"入力\");#c=ascii($get);if($get==\"\")#c=-1;$get=rightstr($get,strlen($get)-strlen(char(#c)));#buf[#ptr]=#c;return;";
}
endgroupundo;
|
Mac OS X (PowerPC 32bit) アセンブリで。コマンドラインでBFのソースを書いて、標準出力にアセンブリを出力します。当方ヘタレなもんで、Mac OS X PowerPCアセンブリでのファイルの扱いがよくわかりません。
# BFコンパイラのコンパイル % gcc -o bfc-osxppc bfc-osxppc.s % ./bfc-osxppc "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+." > hello-bf.s # 出力されたソースのコンパイルと実行 % gcc -o hello-bf hello-bf.s % ./hello-bf Hello World!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | ;; % gcc -o bfc-osxppc bfc-osxppc.s
;;
;; Usage: ./bfc-osxppc "source..." > output
;;
;; ex)
;; % ./bfc-osxppc "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+." > hello-bf.s
;; % gcc -o hello-bf hello-bf.s
;; % ./hello-bf
;; Hello World!
.machine ppc
.globl _main
.macro PUSH
stwu r3, -4(r1)
stwu r4, -4(r1)
stwu r5, -4(r1)
stwu r6, -4(r1)
stwu r7, -4(r1)
stwu r8, -4(r1)
stwu r9, -4(r1)
stwu r10, -4(r1)
stwu r11, -4(r1)
stwu r12, -4(r1)
stwu r13, -4(r1)
mflr r20
stwu r20, -4(r1)
.endmacro
.macro POP
lwz r20, 0(r1)
mtlr r20
lwzu r13, 4(r1)
lwzu r12, 4(r1)
lwzu r11, 4(r1)
lwzu r10, 4(r1)
lwzu r9, 4(r1)
lwzu r8, 4(r1)
lwzu r7, 4(r1)
lwzu r6, 4(r1)
lwzu r5, 4(r1)
lwzu r4, 4(r1)
lwzu r3, 4(r1)
addi r1, r1, 4
.endmacro
.macro PRINT
PUSH
li r3, 1
lis r4, hi16($0)
addi r4, r4, lo16($0)
lis r5, hi16($0_len)
addi r5, r5, lo16($0_len)
li r0, 4
sc
POP
.endmacro
.macro PRINT_DIGIT
PUSH
mr r3, $0
bl print_digit
POP
.endmacro
;; r7: ソースコード
;; r8: 読み込んだ文字
;; r9: 配列のサイズ
;; r10: ラベルの数
_main:
lwz r7, 4(r4) ; argv[1]
li r8, 0
li r9, 10
li r10, 0
;; ヘッダを出力
print_header:
PRINT bf_header
compile:
lbz r8, 0(r7)
cmpli cr7, r8, 0
beq cr7, finish_compile
addi r7, r7, 1
cmpli cr7, r8, 62 ; '>'
beq cr7, compile_inc
cmpli cr7, r8, 60 ; '<'
beq cr7, compile_dec
cmpli cr7, r8, 43 ; '+'
beq cr7, compile_plus
cmpli cr7, r8, 45 ; '-'
beq cr7, compile_minus
cmpli cr7, r8, 46 ; '.'
beq cr7, compile_output
cmpli cr7, r8, 44 ; ','
beq cr7, compile_input
cmpli cr7, r8, 91 ; '['
beq cr7, compile_forward
cmpli cr7, r8, 93 ; ']'
beq cr7, compile_backward
;; 他の文字は無視
b compile
compile_inc:
addi r9, r9, 1
PRINT bf_inc
b compile
compile_dec:
subi r9, r9, 1
PRINT bf_dec
b compile
compile_plus:
PRINT bf_plus
b compile
compile_minus:
PRINT bf_minus
b compile
compile_output:
PRINT bf_output
b compile
compile_input:
PRINT bf_input
b compile
compile_forward:
addi r10, r10, 1
stwu r10, -4(r1)
PRINT bf_forward_cmp
PRINT bf_end_label
PRINT_DIGIT r10
PRINT lf
PRINT bf_begin_label
PRINT_DIGIT r10
PRINT bf_label_term
b compile
compile_backward:
lwz r2, 0(r1)
addi r1, r1, 4
PRINT bf_backward_cmp
PRINT bf_begin_label
PRINT_DIGIT r2
PRINT lf
PRINT bf_end_label
PRINT_DIGIT r2
PRINT bf_label_term
b compile
finish_compile:
PRINT lf
PRINT bf_exit
PRINT bf_data
PRINT bf_array_decl
print_array:
PRINT bf_array_element
cmpli cr7, r9, 0
subi r9, r9, 1
bgt cr7, print_array
PRINT bf_array_decl_end
exit:
PRINT lf
;; sys_exit()
li r3, 0
li r0, 1 ; sys_exit
sc
;; 正の整数を10進数で出力
;; r3: 出力する整数
print_digit:
mr r10, r3
li r11, 25000 ; fig, 桁
mulli r11, r11, 4
li r14, 10 ; 桁の除算用
li r15, 0 ; 残りの数字をすべて表示するか
;; 上の桁から順に出力する
print_each_digit:
divw r12, r10, r11 ; d = i / fig
mullw r13, r11, r12 ; rem = i - fig * d
sub r10, r10, r13
cmpli cr7, r15, 0
cmpli cr6, r12, 0 ; d == 0
cmpli cr5, r11, 1 ; fig > 1
crand 2, 30, 26 ; cr0[eq] = cr7[eq] && cr6[eq]
crand 2, 2, 21 ; cr0[eq] = cr0[eq] && cr5[gt]
divw r11, r11, r14 ; fig /= 10
beq cr0, print_each_digit
;; 出力
li r15, 1
addi r12, r12, 48 ; ASCIIコードにする
li r3, 1 ; 標準出力
lis r4, hi16(temp)
addi r4, r4, lo16(temp)
li r5, 1
li r0, 4 ; sys_write
stb r12, 0(r4)
PUSH
sc
POP
cmpli cr7, r11, 0 ; fig == 0 なら終了
beqlr cr7
b print_each_digit
;; 定数
.data
.align 4
temp:
.asciz " "
.align 4
lf:
.asciz "\n"
.align 4
lf_len = 1
.align 4
bf_header:
.asciz ";; This file is automatically generated. Do not edit.\n\n .machine ppc\n .globl _main\n\n .macro PUSH\n stwu r7, -4(r1)\n stwu r8, -4(r1)\n stwu r9, -4(r1)\n .endmacro\n\n .macro POP\n lwz r9, 0(r1)\n lwzu r8, 4(r1)\n lwzu r7, 4(r1)\n addi r1, r1, 4\n .endmacro\n\n .macro OUTPUT\n PUSH\n li r3, 1\n lis r4, hi16(temp)\n addi r4, r4, lo16(temp)\n stb r8, 0(r4)\n li r5, 1\n li r0, 4\n sc\n POP\n .endmacro\n\n\n_main:\n lis r7, hi16(array)\n addi r7, r7, lo16(array)\n li r8, 0\n\n"
bf_header_len = . - bf_header - 1
.align 4
bf_exit:
.asciz "exit:\n li r8, 10\n OUTPUT\n li r3, 0\n li r0, 1\n sc\n\n"
bf_exit_len = . - bf_exit - 1
.align 4
bf_data:
.asciz "\n\n .data\n .align 4\n\ntemp:\n .asciz \" \"\n .align 4\n\n"
bf_data_len = . - bf_data - 1
.align 4
bf_array_decl:
.asciz "array:\n .long "
bf_array_decl_len = . - bf_array_decl - 1
.align 4
bf_array_element:
.asciz "0, "
bf_array_element_len = . - bf_array_element - 1
.align 4
bf_array_decl_end:
.asciz "0\n .align 4\n"
bf_array_decl_end_len = . - bf_array_decl_end - 1
.align 4
bf_inc:
.asciz " addi r7, r7, 4\n"
bf_inc_len = . - bf_inc - 1
.align 4
bf_dec:
.asciz " subi r7, r7, 4\n"
bf_dec_len = . - bf_dec - 1
.align 4
bf_plus:
.asciz " lwz r8, 0(r7)\n addi r8, r8, 1\n stw r8, 0(r7)\n"
bf_plus_len = . - bf_plus - 1
.align 4
bf_minus:
.asciz " lwz r8, 0(r7)\n subi r8, r8, 1\n stw r8, 0(r7)\n"
bf_minus_len = . - bf_minus - 1
.align 4
bf_output:
.asciz " lwz r8, 0(r7)\n OUTPUT\n"
bf_output_len = . - bf_output - 1
.align 4
bf_input:
.asciz " INPUT\n"
bf_input_len = . - bf_input - 1
.align 4
bf_forward_cmp:
.asciz " lwz r8, 0(r7)\n cmpi cr7, r8, 0\n beq cr7, "
bf_forward_cmp_len = . - bf_forward_cmp - 1
.align 4
bf_begin_label:
.asciz "begin"
bf_begin_label_len = . - bf_begin_label - 1
.align 4
bf_end_label:
.asciz "end"
bf_end_label_len = . - bf_end_label - 1
.align 4
bf_label_term:
.asciz ":\n"
bf_label_term_len = . - bf_label_term - 1
.align 4
bf_backward_cmp:
.asciz " lwz r8, 0(r7)\n cmpi cr7, r8, 0\n bne cr7, "
bf_backward_cmp_len = . - bf_backward_cmp - 1
.align 4
|
昔七行スレに投下したのを投稿してみる。 要gcc、出力はa.outで固定です。 $ gcc bfc.c -o bfc #コンパイラのコンパイル $ ./bfc hoge.bf #コンパイル $ ./a.out #実行
1 2 3 4 5 6 7 | #include <stdio.h>
int system(),i;int main(int c,char**v){FILE*f=fopen(*++v,"r"),*g=fopen("!.c","w"
);char s[99],*o[]={"putchar(*p);","*p=getchar();","while(*p){","}","++p;","--p;"
,"++*p;","--*p;"};fputs("#include<stdio.h>\nint main(void){static int b[30000],"
"*p=b;",g);for(;f&&(c=fgetc(f))-EOF;)for(i=8;0<i--;)fputs(".,[]><+-"[i]-c?"":o[i
],g);fputs("return 0;}",g);fclose(f);fclose(g);sprintf(s,"gcc !.c -o%s",*++v);
return system(s);}
|
MetaOCaml 使ってみました。コード生成部が読みにくい……
出力されるコードはこんな風になります:
# compile "++++++++[>++++++++<-]>+.";;
- : ('a, state -> unit) code =
.<fun s_1 ->
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + 8) in
let _ =
while ((s_1.array).(s_1.ptr) <> 0) do
let _ = s_1.ptr <- ((s_1.ptr + 1) mod s_1.array_size) in
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + 8) in
let _ = s_1.ptr <- ((s_1.ptr + (-1)) mod s_1.array_size) in
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + (-1)) in ()
done in
let _ = s_1.ptr <- ((s_1.ptr + 1) mod s_1.array_size) in
let _ = (s_1.array).(s_1.ptr) <- ((s_1.array).(s_1.ptr) + 1) in
let _ =
(output_char stdout
(((* cross-stage persistent value (as id: Char.chr) *))
(s_1.array).(s_1.ptr))) in
()>.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | type inst = Inc of int | Shift of int | Input | Output | Loop of inst list
let count_while c s =
let rec loop m =
match Stream.peek s with
| Some c' when c = c' -> Stream.junk s; loop (m+1)
| _ -> m
in loop 1
let rec parse1 s =
match Stream.next s with
| '+' -> Inc (count_while '+' s)
| '-' -> Inc (- (count_while '-' s))
| '>' -> Shift (count_while '>' s)
| '<' -> Shift (- (count_while '<' s))
| '.' -> Output
| ',' -> Input
| '[' -> Loop (parseloop s [])
| _ -> parse1 s
and parse s acc =
try let i = parse1 s in parse s (i :: acc)
with Stream.Failure -> List.rev acc
and parseloop s acc =
if Stream.peek s = Some ']' then (Stream.junk s; List.rev acc)
else parseloop s (parse1 s :: acc)
type state = { mutable ptr : int; array : int array; array_size : int; }
let rec trans1 i s =
match i with
| Inc n -> .< (.~s).array.((.~s).ptr) <- (.~s).array.((.~s).ptr) + n >.
| Shift n -> .< (.~s).ptr <- ((.~s).ptr + n) mod (.~s).array_size >.
| Output -> .< output_char stdout (Char.chr (.~s).array.((.~s).ptr)) >.
| Input -> .< (.~s).array.((.~s).ptr) <- Char.code (input_char stdin) >.
| Loop is ->
.< while (.~s).array.((.~s).ptr) <> 0 do .~(trans is s) done >.
and trans insts s =
let f i code = .< let _ = .~(trans1 i s) in .~code >.
in List.fold_right f insts .<()>.
let make_state n = { ptr = 0; array = Array.create n 0; array_size = n; }
let compile str =
.<fun s -> .~(trans (parse (Stream.of_string str) []) .<s>.)>.
let execute str = (.! compile str) (make_state 100)
|
VBAを使用せず、Excelの普通(?)の関数のみでBrainfuckインタプリタを作りました。
メモリセル(16個だけ)、プログラムの長さ(1024文字まで)、実行ステップ数(1024ステップまで)に制限があります。 あと、Excelで表示できない文字は出力されないようにしました。
1 2 3 | ソースを貼り付けることができないので、
Excelファイルをここにアップロードしておきました。
http://unkun.ikaduchi.com/uploadFiles/BF.xls
|
たとえば以下のようにBFコードが生成されます。 インタプリタではなくて、コンパイル後のGroovyでのインストラクション表現をBFコード文字列と同じにしたということです。まあ内部DSL、BF DSLみたいなもんだ(厳密には違うけど)。
"++++++++++[>++++++++++<-]>++++.+++++++.--------.--.".run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | println """
String.metaClass.define {
jump { ip, direc, target, curr ->
if (code[ip] == curr) {
ip = jump(ip+direc+direc, direc, target, curr)
}
if (code[ip] == target) {
return ip+direc
}
jump(ip+direc, direc, target, curr)
}
run {
code = new String(delegate)
for (int ip=0; ip>=0 && ip<delegate.size(); ip=delegate[ip].intern().exec(ip))
;
System.out.flush()
}
}
'>'.metaClass.exec={ ptr++; it+1 }
'<'.metaClass.exec={ ptr--; it+1 }
'+'.metaClass.exec={ data[ptr]++; it+1 }
'-'.metaClass.exec={ data[ptr]--; it+1 }
'.'.metaClass.exec={ System.out.write(data[ptr]); it+1 }
','.metaClass.exec={ System.in.read(data, ptr, 1); it+1 }
'['.metaClass.exec={ (data[ptr]==0 ? jump(it+1, +1, ']', delegate) : it+1) }
']'.metaClass.exec={ (data[ptr]!=0 ? jump(it-1, -1, '[', delegate) : it)+1 }
data = new byte[30000]
ptr = 0
"${args[0]}".run()
"""
|
たとえば以下のようにBFコードが生成されます。 インタプリタではなくて、コンパイル後のGroovyでのインストラクション表現をBFコード文字列と同じにしたということです。まあ内部DSL、BF DSLみたいなもんだ(厳密には違うけど)。
"++++++++++[>++++++++++<-]>++++.+++++++.--------.--.".run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | println """
String.metaClass.define {
jump { ip, direc, target, curr ->
if (code[ip] == curr) {
ip = jump(ip+direc+direc, direc, target, curr)
}
if (code[ip] == target) {
return ip+direc
}
jump(ip+direc, direc, target, curr)
}
run {
code = new String(delegate)
for (int ip=0; ip>=0 && ip<delegate.size(); ip=delegate[ip].intern().exec(ip))
;
System.out.flush()
}
}
'>'.metaClass.exec={ ptr++; it+1 }
'<'.metaClass.exec={ ptr--; it+1 }
'+'.metaClass.exec={ data[ptr]++; it+1 }
'-'.metaClass.exec={ data[ptr]--; it+1 }
'.'.metaClass.exec={ System.out.write(data[ptr]); it+1 }
','.metaClass.exec={ System.in.read(data, ptr, 1); it+1 }
'['.metaClass.exec={ (data[ptr]==0 ? jump(it+1, +1, ']', delegate) : it+1) }
']'.metaClass.exec={ (data[ptr]!=0 ? jump(it-1, -1, '[', delegate) : it)+1 }
data = new byte[30000]
ptr = 0
"${args[0]}".run()
"""
|
- <a href=http://www.silka.org/>buy valium online</a>
- The increased and prolonged sedation is identified to occur with cimetidine, ketoconazole, fluvoxamine, fluoxetine and omeprazole.
<a href=http://www.silka.org/>buy valium online</a>
In patients with myasthenia gravis who are prescribed with Valium, care should be taken on account of preexisting muscle weakness. http://www.silka.org/ - valium pill
No prescription is needed!
If Valium is to be combined with other centrally-acting agents, for example, antipsychotics, anxiolytics/sedatives, antidepressants, hypnotics, anticonvulsants, narcotic analgesics, anesthetics and sedative antihistamines, it should be borne in mind that their effects may potentiate or be potentiated by the action of Valium.
Hi, <a href=http://nicheblogssquad.com/>Play Online Casino</a>
These websites are absolutely legitimate and authorized by government.
- <a href=http://nicheblogssquad.com/>Play Casino For Fun</a>
- Online gaming is taking the industry by storm and people don't even require leaving their home to have some fun with gambling.
- http://nicheblogssquad.com/ - Best Online Casinos
- Some websites are freely accessible, while you'll require putting down a certain deposit in others.
На финансовом рынке страны Всеукраинский Акционерный Банк (<a href=http://www.express-release.com/organization/1417>VAB Банк</a>) работает с 1992 года и, согласно данным НБУ, входит в группу крупных финансовых учреждений. С октября 2006 года – в составе универсальной финансовой группы VAB Group. Региональная сеть Банка по состоянию на 1.03.2009 г. представлена 22 филиалами и 151 отделениями в Украине, а также представительством в г. Будапешт (Венгрия). В числе акционеров <a href=http://www.express-release.com/organization/1417>VAB Банк</a> – крупная международная компания TBIF Financial Services BV (Амстердам, Нидерланды). По состоянию на 1 января 2009 года объем чистых активов VAB Банка составил 7,7 млрд грн, регулятивный капитал Банка – 925 млн грн. <a href=http://www.express-release.com/organization/1417>VAB Банк</a> <a href=http://www.express-release.com/organization/1417>VAB Банк</a> http://www.express-release.com/organization/1417 - VAB Банк
see: VAB Банк
Biden War On Drugs <a href=http://greenheartmedicaluniversity.com/>order acomplia online</a>
Almost half of the volunteers who took part in the study showed improvement in HDL-cholesterol level beyond the expected increase resulting from weight loss.
- <a href=http://greenheartmedicaluniversity.com/>buy acomplia on line</a>
- In test periods, Acomplia is found to associate a few side effects among which vomiting tendency and nausea was most common.
- http://greenheartmedicaluniversity.com/ - buy acomplia
- It was initially launched in UK and then gradually launched in Denmark, Ireland, Germany, Finland and Norway in the last 6 months of that year.
see: Bidil Medication
Better Medicine Canada fartonast <a href=http://www.jo3design.com/>Vardenafil Online</a>
If a patient has an allergy to the drug content of Levitra, this drug is not recommended for him.
- <a href=http://www.jo3design.com/>Buy Levitra On Line</a>
- Levitra loosens the muscles around the penile area of the male body allowing blood to flow into the penis at increased rates.
- http://www.jo3design.com/ - Levitr





dankogai
#3886()
Rating0/2=0.00
「どう書く?」でまだ出ていないのが不思議なお題。それがBF処理系。 ここでは、BFで書かれたソースを、同じ言語に変換するコンパイラーを募集します。
私自身、すでにPerlとJavaScriptに関しては http://blog.livedoor.jp/dankogai/archives/50545151.html でやっているのですが、他の言語バージョンも是非見たいので。
Dan the Brainf.ucker
see: Brainfuck - Wikipedia
1 reply [ reply ]