Boris Nagaev · Home page | About | Contact | Github | Code | FBB files

Brainfuck to C translator written in Lua

Lua code translating Brainfuck to C:

local brainfuckToC_map = {
    ['>'] = '++ptr;',
    ['<'] = '--ptr;',
    ['+'] = '++*ptr;',
    ['-'] = '--*ptr;',
    ['.'] = 'putchar(*ptr);',
    [','] = '*ptr=getchar();',
    ['['] = 'while (*ptr) {',
    [']'] = '}',
}

local function brainfuckToC(bf_code)
    local c_code = bf_code:gsub('.', function(op)
        return brainfuckToC_map[op] or ''
    end)
    return c_code
end

local PROLOGUE = [[
#include <stdio.h>
int main() {
    #define TAPE_SIZE 30000
    char array[TAPE_SIZE] = {0};
    char *ptr=array;
]]

local EPILOGUE = [[
    return 0;
}
]]

print(PROLOGUE)
for line in io.lines() do
    print(brainfuckToC(line))
end
print(EPILOGUE)

This script reads Brainfuck code from stdin and writes corresponding C code to stdout.

Let us compare the performance of code generated by the script with Brainfuck interpreter beef and Brainfsck JIT compiler from DynASM tutorial.

Download a mandelbrot set fractal viewer in Brainfuck written by Erik Bosman.

Brainfuck to C translator

Translate Brainfuck code to C using the translator:

$ lua brainfuck-to-c.lua < mandelbrot.bf > mandelbrot.c

Compile generated C code:

$ gcc mandelbrot.c -o mandelbrot.exe

Run the program:

$ time ./mandelbrot.exe
<mandelbrot set fractal here>
real    0m23.183s
user    0m23.141s
sys     0m0.004s

Add option -O3 to enable more optimizations:

$ time gcc -O3 mandelbrot.c -o mandelbrot.exe

real    0m4.796s
user    0m4.728s
sys     0m0.048s

$ time ./mandelbrot.exe
<mandelbrot set fractal here>
real    0m1.599s
user    0m1.596s
sys     0m0.000s

Brainfuck interpreter beef

$ time beef mandelbrot.bf
<mandelbrot set fractal here>
real    0m28.981s
user    0m28.886s
sys     0m0.044s

Brainfuck JIT compiler

The Brainfuck JIT compiler can be found in the DynASM tutorial.

Build the Brainfuck JIT compiler:

$ git clone https://github.com/corsix/dynasm-doc.git
$ cd dynasm-doc
$ git submodule update --init
$ gcc -o minilua luajit-2.0/src/host/minilua.c
$ ./minilua luajit-2.0/dynasm/dynasm.lua -o bf_dynasm_c.c -D X64 bf_dynasm.c
$ gcc bf_dynasm_c.c -o bf_dynasm_c.exe

Run the program:

$ time ./bf_dynasm_c.exe mandelbrot.bf
<mandelbrot set fractal here>
real    0m2.750s
user    0m2.740s
sys     0m0.004s

Conclusions

The JIT compiler’s performance of Brainfuck code execution is comparable to the performance of C code generated from Brainfuck code with -O3 optimizations enabled. The time of execution of gcc -O3 (4.7 seconds) added to the time of execution of compiled C code (1.6 seconds) exceeds the time of execution of JIT compiler (2.7 seconds).

Obvious advantage of JIT compiler is that it doesn’t require C compiler to execute Brainfuck code. Same JIT compiler binary can execute any Brainfuck code while C compiler is needed each time to compile a C code translated from a Brainfuck code using the translator.

DynASM project is great!

Time of execution was measured on Intel(R) Core(TM) i5-3317U CPU @ 1.70GHz.