Schreiben Sie einen Brainfuck-Übersetzer


18

Schreiben Sie in einer beliebigen Programmier- oder Skriptsprache x ein Programm, das einen gültigen Brainfuck-Quellcode aus stdin entnimmt und in stdout ausgibt, den Quellcode eines Programms, das in der Sprache x geschrieben ist und genau dasselbe ausgibt wie das Brainfuck-Programm.

Ihr Programm muss für jedes gültige Brainfuck-Programm funktionieren, einschließlich der leeren Datei.

Ihre Punktzahl entspricht der Byteanzahl Ihres Quellcodes plus der Byteanzahl Ihrer Ausgabe bei folgender Eingabe:

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

Beispielsweise ist für eine Eingabe von [-]die Ausgabe von *p=0;viel günstiger alswhile(*p) *p--;

Wenn Sie Nicht-ASCII-Zeichen verwenden, muss die Byteanzahl mit der UTF-8-Codierung berechnet werden.

Die niedrigste Punktzahl gewinnt. Kreative Lösungen, die versuchen, den Output zu minimieren, sollen jedoch durch Upvotes gefördert werden.


11
Vielleicht möchten Sie eine Klausel hinzufügen, dass die Zielsprache nicht auch Brainfuck ist;)
Josh

@Josh gut, wenn es jemand geschafft hat, ein kurzes Brainfuck-Programm zu schreiben, das unnötige, nutzlose Codes entfernt, warum lassen Sie es nicht zu?
user12205

2
Ganz einfach, weil die triviale Lösung, die Quelle unverändert auszugeben, für Brainfuck ohnehin eine sehr niedrige Punktzahl haben wird. Ich werde überrascht sein, ob eine andere Sprache das schlagen kann.
Tim Seguine

@ Tim Seguine Ich könnte die Frage ändern, aber wäre das unfair gegenüber denen, die bereits eine Antwort gegeben haben? Und wenn ich die Frage ändere, denke ich darüber nach, die Berechnung der Punktzahl zu ändern. byte count of source + (byte count of output)^2Würde dies die Leute dazu ermutigen, sich mehr auf die Vereinfachung der Ausgabe zu konzentrieren?
user12205

Eine solche Frage zu ändern, nachdem sie bereits beantwortet wurde, ist in der Regel verpönt. Ich habe nur einen Grund genannt, warum ich denke, dass Josh Recht hatte. Es ist gut, solche Dinge zuerst in der Sandbox zu veröffentlichen, damit Sie potenzielle Probleme herausfinden können, während Sie fair zu allen sind.
Tim Seguine

Antworten:


12

Perl - 177 (Quelle) + 172 (Ausgabe) = 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'

Zählen Sie den Shebang als 2 Byte, eines für jede Option. Zunächst wird jeder der acht Befehle in den Bereich übersetzt p-w, während gleichzeitig alle anderen Zeichen entfernt werden. Diese Zeichenfolge wird dann lauflängencodiert und mit einem minimalen Decoder / Interpreter ausgegeben. Ein paar Dinge sind weg optimiert: Der String macht ><offensichtlich nichts, und eine for-Schleife, die direkt nach der anderen folgt, kann vollständig entfernt werden, da sie niemals eingegeben wird.

Ausgabe für das Testprogramm:

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

Ein Probelauf:

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

Perl - 232 (Quelle) + 21 (Ausgabe) = 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\"")

Diese basiert auf der Beobachtung von FIQ , dass die Ausgabe statisch ist und daher auf eine einzige printAnweisung reduziert werden kann, wenn das ursprüngliche Programm keine Eingabeanweisung enthält . Wenn Ihnen dieser gefällt, geben Sie seiner Antwort eine +1.

Was wir also tun können, ist stdout, eine Pipe an eine Variable zu senden, evalden Code , den wir ausgegeben hätten, und das Ergebnis in a zu verpacken print.

... das wird aber nicht immer funktionieren. Wann immer der zu übersetzende Code zu einer Endlosschleife geführt hätte (zB +[.]), kann dies printaus offensichtlichen Gründen nicht auf eine einzige Anweisung reduziert werden. Stattdessen starten wir den evalin-a-child-Prozess mit einer kurzen Zeitüberschreitung. Wenn die Ausführung nicht innerhalb dieser Zeit abgeschlossen ist, geben wir das übersetzte Programm wie zuvor aus.

Strukturiert und kommentiert:

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\""
  }
}

Ausgabe für Beispielprogramm:

print"Hello\ world\!"

Ausgabe für ,[.]:

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

Ausgabe für +[.](nach 9 Sekunden):

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

1
Das ist großartig! Gehirn schmerzt :)
Timwi

Ich denke wv.*?(?=w)ist falsch. Ich denke, es wird nur den Code bis zum nächsten entfernen ], aber Sie brauchen ihn, um den passenden zu finden ]. Sie müssen sich um die Verschachtelung kümmern ...
Timwi

@Timwi Durch Ignorieren der verschachtelten Fälle behoben wv[^v]*(?=w), was erheblich kürzer ist als die Alternative.
Primo

14

Brainfuck, 5 + 540 = 545 Bytes

5 Byte Code, 540 Byte von der Ausgabe der angegebenen Testdatei (vorausgesetzt, ich habe die Anzahl richtig aus meiner Einfügung des Codes ermittelt).

,[.,]

Angenommen, EOF ist 0.


@primo, da es nicht zurückgesetzt wird, bevor ein Interpreter gelesen wird, der den Wert bei EOF nicht ändert, macht dieses Programm zu einer Endlosschleife für alle Eingaben, die größer als 0 Byte sind.
Sylwester

Ich frage mich, mit welcher Software dieses Zeug ausgeführt wird. xD
Teun Pronk

@TeunPronk Es gibt einen Brainfuck-Interpreter namens bfi ( github.com/susam/bfi ). Kompilieren und installieren Sie es einfach und führen Sie es folgendermaßen aus: bfi input.bfWo input.bfist die zu interpretierende Brainfuck-Datei?
Braden Best

5

PHP, 553 + 27 = 580 Bytes

(553 Bytes mit allen Leerzeichen, dh Zeilenumbrüchen und Leerzeichen, entfernt)

Ich mag es nicht, PHP zu spielen, daher kann dieser Ansatz stark optimiert werden. Ich wollte meine Herangehensweise an die Lösung hauptsächlich in etwas zeigen, das nicht BF ist.

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

Die Fehlerberichterstattung muss ausgeschaltet sein, sonst hasst Sie PHP. Verwendung: Wirf dies als Seite auf und starte es mit script.php? C = CODE (wenn das resultierende Skript Eingaben erfordert, starte es als out.php? I = INPUT). Denken Sie daran, die Eingabe zu verlassen!

Dies bedeutet im Grunde Folgendes: Wenn das BF-Skript "," enthält, bettet es sich als resultierendes Skript mit einem angehängten $ b = 1 ein. oben. Wenn es NICHT "," enthält, wird es auf "echo '<BF output>'" optimiert. Praktischerweise benötigt das Testskript im OP KEINE Eingabe. Die zusätzlichen Schrägstriche () sind nur zum Entkommen von 'und \ da.


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

Ausgabe:

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

Originalcode:

#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 << "}";
}

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"))')

Ausgabe:

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

1

io

659 + 553 = 1212

Dinge wie File standardInput readBufferOfLength(1)wirklich töten die Byteanzahl, aber ich kann es nicht umgehen. Ich habe keine Optimierungen für wiederholte Symbole oder fehlende Eingaben im BF-Programm vorgenommen, werde aber weiterhin daran arbeiten und auch an einer, die die Metaprogrammierungsfunktionen von io nutzt.

"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))

Testen

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

Ausbeuten

Hello world!
659  +  553  = 1212

1

Brainfuck , 109 + 407 = 516

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

Probieren Sie es online!

Es werden nur Nicht-BF-Operationen entfernt und andere Optimierungen nicht berücksichtigt.


0

Lua - 328 + 2256 = 2584

(Oh, mir ist nur klar geworden, dass Sie auch die Länge des Ergebnisses addieren müssen, schlechte Punktzahl, wie es aussieht)

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]")))

Entnommen aus dieser Antwort von mir.


0

Lua - 319 + 21 = 340

Dies ist höchstwahrscheinlich der kürzeste Code von allen, aber er akzeptiert keine Eingaben, ist also ein bisschen betrügerisch. Ich habe eine Idee für eine andere Version mit Input, siehe Ende dieses Kommentars.

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

Diese Version soll beweisen, dass lua mehr kann als 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;"}))()')

Beide Versionen fügen 30000 Datenbytes hinzu. Meine zweite Version basiert auf Eingabe / Ausgabe: alles nach einem '.' oder ',' wird entfernt. Meine zweite Version erlaubt keine Endlosschleifen ([.,], [] Usw.)

Meine Idee ist es:

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

Aus Ihrer Eingabe mit einem zusätzlichen ', +.'

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.