Write a brainfuck translator

18

4

In any programming or scripting language x, write a program that takes a valid brainfuck sourcecode from stdin and output, to stdout, the sourcecode of a program, written in language x, that would output the exact same thing as the brainfuck program would do.

Your program must work for any valid brainfuck program, including the empty file.

Your score would be equal to the byte count of your sourcecode, plus the byte count of your output given the following input:

+++++ [-]
+++++ +++++ [
    > +++++ ++
    > ++ +++ ++++ +
    > +++
    <<< -
]
> ++ . H
> + . e
++ +++ ++. l
. l
+++ . o
> ++ . space
< +++++ +++ . w
----- --- . o
+++ . r
---- - - . l
----- --- . d
> + . exclamation mark
------lol; useless code :-)--------------------------[.............................................][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]<-<<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><

For example, for an input of [-], the output of *p=0; is much more favourable than while(*p) *p--;

If you happen to use non-ASCII characters, the byte count must be calculated using the UTF-8 encoding.

Lowest score wins. However, creative solutions that attempt to minimise the output shall be encouraged by upvotes.

user12205

Posted 2014-02-03T01:23:31.080

Reputation: 8 752

11You might want to add a clause that the target language not also be brainfuck ;) – Josh – 2014-02-03T01:58:47.257

@Josh well, if someone managed to write a short brainfuck program that removes unnecessary useless codes, why not let them do it? – user12205 – 2014-02-03T11:11:39.320

2Well quite simply because the trivial solution of outputting the source unchanged is going to have a really low score anyway for brainfuck. I will be surprised if another language can beat that. – Tim Seguine – 2014-02-03T11:19:52.750

@Tim Seguine I could change the question, but would that be unfair to those who have already provided an answer? And if I change the question, I'm thinking about changing the score calculation, making it byte count of source + (byte count of output)^2, would that encourage people to focus more on simplifying the output? – user12205 – 2014-02-03T11:25:05.963

Generally changing a question like that after it has already been answered is frowned upon. I was just pointing out a reason why I think Josh was right. It's good to post stuff like this in the sandbox first, so you can work out potential problems while being fair to everyone. – Tim Seguine – 2014-02-03T11:33:18.693

@Tim Seguine I'm hoping someone can come up with some clever way to minimise the output so as to win this. I mean, the given input essentially is just print "Hello world!", if someone can reduce the output to, say, 200 bytes then even if their program is 300 bytes long they may still beat the current brainfuck solution – user12205 – 2014-02-03T11:37:00.077

What about variants of brainfuck like pbrain? Technically not doing any translation at all is perfectly valid, yet at the same time there exists the opportunity to optimise. – Pharap – 2014-02-03T12:16:06.430

There's a pretty easy way to make it to less than 540+5 bytes (not in BF though) in a lot of languages, including PHP and JS. I'll investigate this further today, but given the example that you have to count as part of the bytecount, I can find a trivial solution. – FIQ – 2014-02-03T13:41:46.743

@FIQ looking forward to it :) – user12205 – 2014-02-03T14:43:49.200

@ace posted it, it's worse than my BF program currently, but that's because the script itself isn't very golfed down at all, the actual resulting script is minimal (for the test case anyways ;) ). – FIQ – 2014-02-03T18:49:25.963

@FIQ nice thinking, I like it – user12205 – 2014-02-03T18:55:33.523

Your scoring criterion is not precisely the source byte-count, so -- according to the blurb for the code-golf tag -- the code-golf tag should not be used, and the code-challenge tag should be used instead. – r.e.s. – 2014-02-04T02:07:03.007

Answers

12

Perl - 177 (source) + 172 (output) = 349

#!perl -p0
y/-+><.,[]
-~/p-w/d;s/(.)\K\1+|rs|wv[^v]*(?=w)/$+&&length$&/ge;$_="eval'r$_'=~".'s/.(\d*)/(qw(--$ ++$ -- ++ print+chr$ $$i=ord+getc; while($$i){ })[$&&v63].q($i;))x($++1)/ger'

Counting the shebang as 2 bytes, one for each option. First, each of the eight commands is translated onto the range p-w, while at the same time removing all other characters. This string is then run-length encoded and output with a minimal decoder/interpreter. A few things are optimized away: the string >< obviously does nothing, and a for loop that follows directly after another may be removed entirely, as it will never be entered.

Output for the test program:

eval'rq4vpwq9vrq6rq9rq2s2pwrq1trqtq6t1q2trq1tsq7tp7tq2tp5tp7trqtp32vt44wsps1'=~s/.(\d*)/(qw(--$ ++$ -- ++ print+chr$ $$i=ord+getc; while($$i){ })[$&&v63].q($i;))x($++1)/ger

A sample run:

$ perl brainfusk.pl < in.bf | perl
Hello world!

Perl - 232 (source) + 21 (output) = 253

#!perl -p0
y/-+><.,[]
-~/0-7/d;$_="eval'2$_'=~".'s/./qw(--$ ++$ -- ++ print+chr$ $$i=ord+getc; while($$i){ })[$&].q($i;)/ger';
/5/||fork?(wait,$?||exit):($SIG{ALRM}=sub{exit 1},alarm 9,$S=select(open 1,'>',\$o),eval,print$S "print\"\Q$o\E\"")

This one is based on FIQ's observation that if the original program doesn't contain an input statement, the output will be static, and therefore can be reduced to a single print statement. If you like this one, be sure to give his answer a +1.

So what we can do is pipe stdout to a variable, eval the code we would have output, and wrap the result in a print.

...that won't always work, though. Whenever the code to be translated would have resulted in an infinite loop, (e.g. +[.]), this cannot be reduced to a single print statement, for obvious reasons. So instead, we launch the eval in a child process with a short timeout, and if it doesn't finish executing within that time we output the translated program as before.

Structured and commented:

if(!/5/) { # no `,` in program

  if(fork) { # parent process

    # wait for child
    wait;
    # no child error, terminate without output
    $?||exit

  } else { # child process

    # alarm handler, exit with error
    $SIG{ALRM}=sub{exit 1};
    # set an alarm in 9 seconds
    alarm 9;
    # redirect STDOUT to variable $o
    $S=select open 1,'>',\$o;
    # execute translated code
    eval;
    # wrap the result in a print statement
    print$S "print\"\Q$o\E\""
  }
}

Output for sample program:

print"Hello\ world\!"

Output for ,[.]:

eval'25647'=~s/./qw(--$ ++$ -- ++ print+chr$ $$i=ord+getc; while($$i){ })[$&].q($i;)/ger

Output for +[.] (after 9 seconds):

eval'21647'=~s/./qw(--$ ++$ -- ++ print+chr$ $$i=ord+getc; while($$i){ })[$&].q($i;)/ger

primo

Posted 2014-02-03T01:23:31.080

Reputation: 30 891

1This is amazing! Brain hurts :) – Timwi – 2014-02-03T20:25:47.513

I think wv.*?(?=w) is wrong. I think it will only remove code up to the next ], but you need it to find the matching ]; you need to take care of nesting... – Timwi – 2014-02-03T20:35:54.377

@Timwi Fixed, by ignoring the nested cases wv[^v]*(?=w), which is significantly shorter than the alternative. – primo – 2014-02-04T00:20:00.087

14

Brainfuck, 5 + 540 = 545 bytes

5 bytes of code, 540 from output of given test file (assuming got the count right from my paste of that code).

,[.,]

Assuming EOF is 0.

FIQ

Posted 2014-02-03T01:23:31.080

Reputation: 519

@primo since it doesn't reset before reading a interpreter that does not chaneg value at EOF will make this program an endless loop for all input larger than 0 bytes. – Sylwester – 2014-02-03T16:12:06.717

I cant help to wonder, what software is used to run this stuff? xD – Teun Pronk – 2014-02-05T14:31:07.433

@TeunPronk There's a brainfuck interpreter called bfi (https://github.com/susam/bfi). Just compile and install it, and run it like so: bfi input.bf where input.bf is the brainfuck file to be interpreted.

– Braden Best – 2014-02-10T01:17:15.463

5

PHP, 553 + 27 = 580 bytes

(553 bytes with all whitespaces, i.e. newlines and spaces, removed)

I suck badly at golfing PHP, so this approach can be heavily optimized. I mostly wanted to show my approach to the solution in something not BF.

<?php
echo "<?php ";
$x = 'if (!$b) $c = $_GET[c];
$x=$y=$n[0]=$p=0;$o[0]=1;$d="";
while($a=$c[$x++]){
    if($o[$p]){
        if($a=="-")$m[$y]--;
        if($a=="+")$m[$y]++;
        $m[$y]=$m[$y]%256;
        if($a=="<")$y--;
        if($a==">")$y++;
        if($a=="."){
            $e=chr($m[$y]);
            if ($b) echo $e;
            else $d.=addslashes($e);
        }
        if($a==",")$m[$y]=($b=$_GET[i])?ord($b):0;
    }if($a=="["){
        $p++;
        $n[$p]=$x-1;
        $o[$p]=$o[$p-1]?$m[$y]:0;
    }
    if($a=="]"){
        if($o[$p])$x=$n[$p];
        $p--;
        if($p=-1)$p=0;
    }
}
if (!$b) echo "echo \'$d\';";';
if (strstr($_GET['c'],",")) {
    $x = '$b=1;'.$x;
    echo '$c="'.addslashes($_GET[c]).'";'.$x;
    return;
}
eval($x);

Error reporting must be off, otherwise PHP will hate you. Usage: throw this up as a page, and run it with script.php?c=CODE (if the resulting script requires input, you run it as out.php?i=INPUT). Remember to url escape the input!

What this does is basically this - if the BF script contains ",", it pretty much embeds itself as the resulting script with an attached $b=1; at the top. If it does NOT contain ",", it optimizes it down to "echo '<BF output>'". Conveniently, the test script in the OP does NOT require any input. The addslashes() is just there for escaping ' and \.

FIQ

Posted 2014-02-03T01:23:31.080

Reputation: 519

4

C++, 695 + 510 = 1205 bytes

Code:

#include<iostream>
#include<utility>
#include<vector>
#define D "\n#define "
using namespace std;using S=string;int main(){vector<pair<S,S>>m={{"--------","(*p)-=8;"},{"<>",""},{"[]","F;"},{"+","A;"},{"-","B;"},{">","C;"},{"<","D;"},{"[","F{"},{"]","}"},{".","E;"},{",","std::cin>>*p;"}};S s;char c;while(cin>>c)if(S("+-><[].,").find(c)<8)s+=c;for(int i=0;i<s.length();i++)if(s.substr(i,4)=="[][]")s=s.replace(i--,4,"[]");cout<<"#include<iostream>" D"A ++*p" D"B --*p" D"C p++" D"D p--" D"E std::cout<<*p" D"F while(*p)\nint main(){char*p=new char[1<<19]();";while(s.size())for(auto p:m)if(s.substr(0,p.first.length())==p.first){s=s.substr(p.first.length());cout<<p.second;break;}cout<<"}";}

Output:

#include<iostream>
#define A ++*p
#define B --*p
#define C p++
#define D p--
#define E std::cout<<*p
#define F while(*p)
int main(){char*p=new char[1<<19]();A;A;A;A;A;F{B;}A;A;A;A;A;A;A;A;A;A;F{C;A;A;A;A;A;A;A;C;A;A;A;A;A;A;A;A;A;A;C;A;A;A;D;D;D;B;}C;A;A;E;C;A;E;A;A;A;A;A;A;A;E;E;A;A;A;E;C;A;A;E;D;A;A;A;A;A;A;A;A;E;(*p)-=8;E;A;A;A;E;B;B;B;B;B;B;E;(*p)-=8;E;C;A;E;(*p)-=8;(*p)-=8;(*p)-=8;(*p)-=8;B;F{E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;}F;D;B;D;D;}

Original code:

#include <iostream>
#include <utility>
#include <vector>
using namespace std;
int main() {
    vector<pair<string, string>> m={
    {"--------","(*p)-=8;"},
    {"<>",""},
    {"[]","F;"},
    {"+","A;"},
    {"-","B;"},
    {">","C;"},
    {"<","D;"},
    {"[","F{"},
    {"]","}"},
    {".","E;"},
    {",","std::cin>>*p;"}};
    string s;
    char c;
    while (cin >> c)
        if (string("+-><[].,").find(c) < 8)
            s += c;
    for(int i = 0; i < s.length(); i++)
        if(s.substr(i, 4) == "[][]")
            s = s.replace(i--, 4, "[]");
    cout << "#include<iostream>\n"
            "#define A ++*p\n"
            "#define B --*p\n"
            "#define C p++\n"
            "#define D p--\n"
            "#define E std::cout<<*p\n"
            "#define F while(*p)\n"
            "int main(){char*p=new char[1<<19]();";
    while (s.size())
        for (auto p : m)
            if (s.substr(0, p.first.length()) == p.first) {
                s = s.substr(p.first.length());
                cout << p.second;
                break;
            }
    cout << "}";
}

johnchen902

Posted 2014-02-03T01:23:31.080

Reputation: 1 177

2

Python - 514 + 352 = 866

Code:

import sys,zlib,base64
s,i="import sys\na,i=[0]*300000,0\n",0
for c in sys.stdin.read():
 if c in"+-><,.[]":
  s+=" "*i+{'+':"a[i]+=1\n",'-':"a[i]-=1\n",'>':"i+=1\n",'<':"i-=1\n",',':"a[i]=(lambda x:0if x==''else ord(x))(sys.stdin.read(1))\n",".":"sys.stdout.write(chr(a[i]))\n","[":"while a[i]!=0:\n","]":"pass\n"}[c]
  i+={'[':1,']':-1}.get(c,0)
print('import zlib,base64\nexec(zlib.decompress(base64.b64decode("'+base64.b64encode(zlib.compress(bytes(s,"utf8"),9)).decode("utf8")+'")).decode("utf8"))')

Output:

import zlib,base64
exec(zlib.decompress(base64.b64decode("eNrLzC3ILypRKK4s5krUybSNNojVMjYAAR0DrsTozFhtW0OCdHlGZk6qAoinaGtgxQVm6QLFFQoSi4uJNoVc2zJBggowWTIZVDGEEvMzddFJ1FDMxBYUwFjTKy5JyS8t0SsvyixJ1UjOKNIASWpqomrAp5DceMBnJjn2Ee0ZojToUiGlEfIFzA5yaGqHELXtp5XfMukVwMOFRi/u8IXZqOSo5KjkqOSIlAQ3k9BLy1HBUcFRwVFBOgpmIrfeMhGE9ihrpLEAudg3NA==")).decode("utf8"))

johnchen902

Posted 2014-02-03T01:23:31.080

Reputation: 1 177

1

Brainfuck, 109 + 407 = 516

>+[>+++++++[-<------>]<-[-[-[-[--------------[--[>+++++[-<------>]<+[--[[-]<[-]>]]]]]]]]<[.[-]]>>,[-<+<+>>]<]

Try it online!

It only removes non BF ops and does not look at other optimizations.

Sylwester

Posted 2014-02-03T01:23:31.080

Reputation: 3 678

1

io

659 + 553 = 1212

Things like File standardInput readBufferOfLength(1) really kill the byte count but I can't get around it. I didn't do optimizations for repeated symbols or lack of input in the BF program, but will continue to work on it, also working on one making use of io's metaprogramming capabilities.

"v :=Vector clone setSize(30000)
p :=0
z :=getSlot(\"method\")
j :=z(p=p+1)
k :=z(p=p-1)
a :=z(v at(p))
l :=z(v atPut(p,a+1))
m :=z(v atPut(p,a-1))
n :=z(a asCharacter print)
u :=getSlot(\"while\")
o :=z(v atPut(p,File standardInput readBufferOfLength(1)))"println
z :=getSlot("method")
g :=z(a,b,if(a,a,b))
v :=z(e,f,if((x :=s)==e,nil,f .. g(w(x),"")))
s :=z(File standardInput readBufferOfLength(1))
w :=z(c,c switch(">",v("<","j"),"<","k","+","l","-","m",".","n",",","o","[",v("]","u(a>0,"),"]",")"))
while((c :=s)!=nil,if((t :=w(c))!=nil,t println))

Testing

cat test.bf | io bftrans.io > out.io && io out.io && echo && echo  $(cat out.io | wc -c) " + " $(cat bftrans.io | wc -c) " = "$(($(cat bftrans.io | wc -c) + $(cat out.io | wc -c)))

Yields

Hello world!
659  +  553  = 1212

Jordon Biondo

Posted 2014-02-03T01:23:31.080

Reputation: 1 030

0

Lua - 328 + 2256 = 2584

(Oh I just realized you need to add the length of the result too, poor score, it looks like)

print((("l,m,p=loadstring,{0},1 z,y,x,w,v,u=l'io.write(string.char(@))',l'@=io.read(1):byte()',l'p=p-1',l'p=p+1 @=@or 0',l'@=(@+1)%256',l'@=(@-1)%256'"..io.read"*a":gsub("[^.,<>[%]+-]",""):gsub(".",{["."]="z()",[","]="y()",["<"]="x()",[">"]="w()",["["]="while @~=0 do ",["]"]="end ",["+"]="v()",["-"]="u()"})):gsub("@","m[p]")))

Taken from this answer of mine.

mniip

Posted 2014-02-03T01:23:31.080

Reputation: 9 396

0

Lua - 319 + 21 = 340

This is most likely the shortest code of all, but it doesn't accept input, so it is kinda cheaty. I got a idea for another version with input, see the end of this comment.

loadstring("o=\"\";d={"..string.rep("0,",30000).."}p=1;"..io.read():gsub("[^%+%-<>%.,%[%]]+",""):gsub(".",{["+"]="d[p]=d[p]+1;",["-"]="d[p]=d[p]-1;",[">"]="p=p+1;",["<"]="p=p-1;",["."]="o=o..string.char(d[p])",[","]="d[p]=io.read()",["["]="while d[p]~=0 do ",["]"]="end;"}))()print("print("..string.format("%q",o)..")")

Lua - 376 + 366 = 742

This version is to prove that lua can do better than 2584 :D

print('loadstring("d={"..string.rep("0,",30000).."}p=1;"..('..string.format("%q",io.read():gsub("[^%+%-<>%.,%[%]]+",""):gsub("%[[^%+%-<>%,%[%]]*%]",""):match("(.*[.,]).-"))..'):gsub(".",{["+"]="d[p]=d[p]+1;",["-"]="d[p]=d[p]-1;",[">"]="p=p+1;",["<"]="p=p-1;",["."]="io.write(string.char(d[p]))",[","]="d[p]=string.byte(io.read())",["["]="while d[p]~=0 do ",["]"]="end;"}))()')

Both versions add in 30000 bytes of data. My second version is based on input/output: everything after a '.' or ',' will be removed. My second version does not allow infinite loops ( [.,], [], etc. )

My Idea is to get:

print("Hello world!"..string.char(string.byte(io.read())+1)

From your input, with a extra ',+.'

YoYoYonnY

Posted 2014-02-03T01:23:31.080

Reputation: 1 173