Write a brainfuck translator
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.
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
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 \.