challenge BFコンパイラー

「どう書く?」でまだ出ていないのが不思議なお題。それがBF処理系。 ここでは、BFで書かれたソースを、同じ言語に変換するコンパイラーを募集します。

私自身、すでにPerlとJavaScriptに関しては http://blog.livedoor.jp/dankogai/archives/50545151.html でやっているのですが、他の言語バージョンも是非見たいので。

Dan the Brainf.ucker

以下のように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 - C++

C++→Cの変化球. Xbyakを使ってオブジェクトコードを出力. ./bf hello.bf なら即実行 ./bf hello.bf 1 > a.c でx86 32bit用Cコードを生成します.これでも32bit x86ならMac/Win/Linuxで動作します. gccの場合は-fno-operator-namesをつけてコンパイルしてください.
  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;
}

にしおさんに倣って,若干最適化コードを生成するようにしました.素数計算のだと3割ぐらい出力コード長が短くなるようです.
 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


	
 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);
}

Index

Feed

Other

Link

Pathtraq

loading...