Golf ein lila Dolmetscher

13

Golf ein lila Dolmetscher

Lila ist ein Esolang, der zwei Hauptzwecken dient:

  • Eine Minimierung der Aubergine , da es nicht genug selbstmodifizierende Einzelanweisungssprachen gibt.
  • Die Möglichkeit erschreckend kleiner Golfdolmetscher zuzugeben. Mein erster Durchgang bei einem einigermaßen voll ausgestatteten Python 2-Interpreter ist nur 702 Bytes, und ich bin sicher, ein erfahrener Golfer könnte sich einiges davon abschneiden.

Ihr Ziel ist es, einen Dolmetscher für diese Sprache zu schreiben.

Informationen zu Lila:

Ein Purple-Programm ist eine Folge von Zeichen, die in einem unendlichen, adressierbaren Speicherfeld so angeordnet sind, dass das erste Zeichen des Programms an der Adresse Null steht. Der Rest des Arrays (vor und nach dem Speichern des Purple-Programms) wird auf Null initialisiert.

In Purple gibt es drei Register mit den Bezeichnungen a und b und i , von denen jedes eine Ganzzahl mit Vorzeichen enthalten kann und auf Null initialisiert ist. ich ist auch der Befehlszeiger und zeigt immer auf den gerade ausgeführten Purple-Befehl.

In jedem Zyklus liest der Interpreter eine Folge von drei aufeinander folgenden Zeichen, beginnend an der durch den Befehlszeiger angegebenen Speicherstelle, und versucht, diese Folge als den Purple-Befehl auszuführen. Danach wird der Anweisungszeiger immer um 3 erhöht.

Syntaktisch besteht der Purple-Befehl aus drei Zeichen (oder Codierungen davon) in einer Reihe, wie " xyz ".

Das erste Zeichen x kann eines der folgenden sein:

abABio

Diese Symbole haben folgende Bedeutung:

a - Place the result in register a.
b - Place the result in register b.
A - Place the result in the location in memory referred to by register a.
B - Place the result in the location in memory referred to by register b.
i - Set the instruction pointer to the result.
o - Output the result to stdout.

Die anderen zwei Bytes y und z können eines der folgenden sein:

abABio1

Jedes dieser Symbole hat folgende Bedeutung:

a - Return the contents of register a.
b - Return the contents of register b.
A - Return the contents of the memory array at the address stored in register a.
B - Return the contents of the memory array at the address stored in register b.
i - Return the contents of register i (the instruction pointer).
o - Return the value of a single character read from stdin.
1 - Return the literal numeric value 1.

Nach dem Abrufen der Anweisung wertet der Purple-Interpreter y und dann z aus , subtrahiert das Ergebnis von z vom Ergebnis von y und führt dann die durch x angegebene Aktion für die Differenz aus.

Wenn die Folge von drei Zeichen (oder deren Codierungen) keine gültige Purple-Anweisung ist, wird der Interpreter sofort angehalten, ohne dass Fehler auftreten.

Ihr Dolmetscher muss:

  • Sei ein komplettes Programm, keine Funktion.
  • Niemals nach stderr ausgeben, es sei denn, EOF wird gelesen .
  • Verhalten Sie sich bei allen wohlgeformten Eingaben, die keine sehr großen Zahlen enthalten, einschließlich der unten angegebenen Testprogramme, identisch zur Referenzimplementierung. (Naja, genau wie beim Timing - es kann langsamer laufen, aber nicht zu viel!)

Sie können das Programm dem Interpreter in einer beliebigen Form zur Verfügung stellen: Lesen Sie es aus einer Datei, binden Sie es als Zeichenfolge in das Programm ein oder lesen Sie es aus stdin.

Testfälle:

Das Programm

ooo

wenn mit Eingabe ausgeführt

z!

sollte nachgeben

Y

Das Programm

bbboobiii

wenn mit Eingabe ausgeführt

It's a cat program.

(oder eine andere Eingabe) sollte ergeben

It's a cat program.

(oder was auch immer Eingang es erhielt) und dann von vorne anfangen und das gleiche noch einmal tun .


Das Programm

Aoab11bi1bABoAaiba

wenn mit Eingabe ausgeführt

0

sollte nachgeben

0

und dann halt, aber bei lauf mit eingabe

1

sollte weiterhin ausgeben

1

für immer.


Das Programm

b1bbb1oAbabaa1ab1Ab1Bi1b

sollte nachgeben

b1bbb1oAbabaa1ab1Ab1Bi1b

Das Programm

aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG

sollte nachgeben

Hello, World!

Wertung:

Dies ist , also gewinnt die kürzeste Quelle in Bytes, die möglicherweise durch den folgenden Bonus modifiziert wird.

Bonus:

  • -10%, wenn Ihr Interpreter einen Dateinamen entweder von stdin oder von einem Befehlszeilenargument liest und das Programm aus der Datei lädt.
Quintopie
quelle
1
Wie groß sind die Speicherzellen? Bytes, Zeichen (Unicode?), (beliebige) große Ganzzahlen? Es sieht so aus, als ob Sie "Zeichen" und "Byte" mit derselben Bedeutung verwenden.
Paŭlo Ebermann
@ PaŭloEbermann Ich vermute, das ist implementierungsspezifisch. Ich muss zum Beispiel uint32für Zeichen und MAXINT für ints
cat verwenden
2
@sysreq Ist das wirklich ein Blocker? Ihre Implementierung könnte einfach zwei Bänder enthalten, eines für negative und eines für positive Indizes. (Ja, das wird ein bisschen mehr Code
Paŭlo Ebermann
1
@sysreq Im Grunde genommen wäre ein Purple-Selbstinterpreter ein Programm, das ein Purple-Programm aus stdin liest und dann alles tut, was dieses Programm tun würde. Das erste Purple-Programm (der Interpreter) kann in einem beliebigen Interpreter ausgeführt werden. Ein Programm, das die niedrigsten Speicheradressen mit der Eingabe vollständig überschreibt, löscht sich dann von selbst, bevor ein Sprung zum gelesenen Code in Frage kommt (obwohl ich dies eigentlich nicht für möglich halte).
Quintopia
2
Ich war so nah dran, eine Laufzeit zu haben, die zur Selbstinterpretation fähig ist, aber ich war zu spät.
Katze

Antworten:

7

Pyth, 148 128 121 Bytes (oder 124 * .9 = 111,6, siehe unten)

J,00=kjb.z .eXHkCbz#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

Testsuite

Code in der ersten Zeile von STDIN, Eingabe in das Purple-Programm für den Rest von STDIN. Um Code mit Zeilenumbrüchen zu verwenden, verwenden Sie die alternative Version unten.

Vernünftig golfen. Hier ist es mit Zeilenumbrüchen und Einrückung für Klarheit:

J,00
=kjb.z
 .eXHkCbz
#
  =b-Fm
    ?=zx"oabABi1"C@H+Zd
      @
        s[0Jm.x@Hk0JZ1H)
        z
      Ch~tk
    S2
   ?hKx"abAB"=YC@HZ
    ?PK
      XH@JKb
      XJKb
  ?qY\i=Zb
  ?qY\opCb
  vN
  =+Z3

Grundsätzlich führt eine #Schleife die Ausführung und das Anhalten über eine Fehlerunterbrechung aus.

aund bwerden zu einer einzigen Variablen zusammengefasst J. Zist der Befehlszeiger. kwird in das Purple-Programm eingegeben. Hist das Band, dargestellt als Wörterbuch.bist das aktuelle Ergebnis. Yist das aktuelle erste Byte der Anweisung.

Lesen aus der Datei:

J,00=kjb.z .eXHkCbjb'z#=b-Fm?q\o=zC@H+ZdCh~tk@s[Jm.x@Hk0JZ1H)x"abABi1"zS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

Geben Sie den Dateinamen als erste Zeile von STDIN an. Testlauf:

$ cat purple-final.pyth 
J,00=kjb.z .eXHkCbjb'z#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3
$ cat purple-code.txt 
aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG
$ pyth purple-final.pyth <<< 'purple-code.txt' 
Hello, World!
isaacg
quelle
5

JavaScript (ES6), 292 Byte

eval(`a=b=i=d=0;v=n=>(x=m[i+n])==97?a_98?b_65?m[a]_66?m[b]_105?i_111?p()[c]()_49?1:d=1;for(m=[...(p=prompt)()].map(b=>b[c="charCodeAt"]());!d;i+=3)(y=v(1),d)||(z=v(2),d)?1:(x=m[r=y-z,i])==97?a=r_98?b=r_65?m[a]=r_66?m[b]=r_105?i=r-3_111?alert(String.fromCharCode(r)):d=1`.replace(/_/g,":x=="))

Erläuterung

JavaScript-Antworten sind immer komisch, wenn STDINundSTDOUT werden benötigt ...

Die erste Eingabeaufforderung ist die Eingabe für die Programmzeichenfolge. Jede Eingabeaufforderung, die sich aus einer oAnweisung ergibt , liest nur das erste Zeichen.

evalwird verwendet, um einen allgemeinen Ausdruck zu ersetzen, der einige Bytes spart. Ungolfed und ohne das evalsieht das Programm so aus:

// Initialisation
a=b=i=                            // initialise registers to 0
  d=0;                            // d is set to true when the program should die

// Gets the result of Y or Z
v=n=>                             // n = offset from i
  (x=m[i+n])==97?a:               // x = value of instruction
  x==98?b:
  x==65?m[a]:
  x==66?m[b]:
  x==105?i:
  x==111?p()[c]():
  x==49?1:
  d=1;                            // if it was none of the valid values, die

// Execution loop
for(
  m=                              // m = memory array
    [...(p=prompt)()]             // receive the program
    .map(b=>b[c="charCodeAt"]()); // initialise m to the ASCII values of the program
  !d;                             // finish if an error occured
  i+=3                            // increment i
)
  (y=v(1),d)||                    // get the value of Y and check for errors
  (z=v(2),d)?1:                   // get the value of Z and check for errors

    // Get the result of X
    (x=m[r=y-z,i])==97?a=r:       // r = result of y - z
    x==98?b=r:
    x==65?m[a]=r:
    x==66?m[b]=r:
    x==105?i=r-3:
    x==111?alert(String.fromCharCode(r)):
    d=1
user81655
quelle
2
Kann die zweite c="charCodeAt"mit nur ersetzt werden c?
Dendrobium
Funktioniert der Array-Zugriff mit negativen Indizes in JavaScript?
nimi
@ Dendrobium Wow, ich weiß nicht, wie ich das verpasst habe haha! Vielen Dank.
user81655
2
@ nimi Es funktioniert. Arrays selbst unterstützen keine negativen Indizes, dies nutzt jedoch die Tatsache, dass sie sich auch als Objekte verhalten. array[-1] = 1ist das gleiche wie array = { "-1": 1 }. Beides erreichen Sie mit array[-1].
user81655
@ user81655: Ah schön, wusste das nicht.
Nimi
3

Ceylon, 827 792 671 Bytes

import ceylon.language{l=variable,I=Integer,x=nothing,p=process,m=map}shared void run(){try{if(exists d=p.arguments[0]){l value t=m{*d*.hash.indexed};l I a=0;l I b=0;l I i=0;I g(I j)=>t[j]else 0;l{I*}c=[];I o{if(c==[]){if(exists e=p.readLine()){c=e*.hash.chain{10};}else{c={-1}.cycled;}}assert(is I r=c.first);c=c.rest;return r;}value f=m{97->{a},98->{b},65->{g(a)},66->{g(b)},105->{i},111->{o},49->{1}};value s=m{97->((I v)=>a=v),98->((I v)=>b=v),65->((I v)=>t=m{a->v,*t}),66->((I v)=>t=m{b->v,*t}),105->((I v)=>i=v),111->((I v)=>p.write("``v.character``"))};I h(I v)=>f[v]?.first else x;while(0<1){(s[g(i)]else x)(h(g(i+1))-h(g(i+2)));i+=3;}}}catch(AssertionError e){}}

Es verhält sich ein bisschen anders als die Referenzimplementierung, wenn das Programm versucht, Eingaben bei EOF zu lesen. Die Referenzimplementierung stürzt mit einem TypeError ab, der zu teuer ist, um hier reproduziert zu werden (und wahrscheinlich auch mit einem Fehler), sodass stattdessen -1 zurückgegeben wird ( wiederholt, falls erforderlich).

(Wenn versucht wird, -1 nach stdout zu schreiben, wird der Interpreter jedoch mit einem OverflowError beendet. Ähnliches passiert, wenn eine Ganzzahl außerhalb des Unicode-Bereichs ausgegeben wird.)

Der Interpreter verwendet das Programm als erstes Befehlszeilenargument (stellen Sie sicher, dass Sie es für Ihre Shell in Anführungszeichen setzen, wenn es Leerzeichen oder andere interessante Dinge enthält).

In Ceylon können wir Eingaben nur leicht zeilenweise lesen (ich denke, dies wird sich in einer der nächsten Versionen ändern). Wenn oalso zum Lesen verwendet wird, lese ich eine ganze Zeile und puffere die Teile für zukünftige Zwecke. Ich denke, es funktioniert ähnlich in der Python-Implementierung, wenn Sie mit einem Terminal verbunden sind.


Wenn Sie versuchen, einen Befehl (Teil) auszuführen, der nicht zu den gültigen Zeichen gehört, wird der Befehl nothing wird ein AssertionError ausgelöst, den wir dann im catch-Block um die Hauptschleife abfangen.

Ich denke, dies sollte vorzugsweise ein benutzerdefinierter Ausnahmetyp sein (da AssertionError auch an anderen Stellen auftreten kann, wenn ich einen Fehler habe), aber das würde viel mehr Platz in Anspruch nehmen und die meisten Verbesserungen, die ich gegenüber der ersten Version gemacht habe, verschlingen.

Einige Tricks zum Golfen:

  • In den vorherigen Versionen wurde eine ceylon.collection.HashMap verwendet. Stattdessen verwenden wir jetzt eine unveränderliche Karte, wie sie von der mapFunktion erstellt wurde, und erstellen jedes Mal eine neue Aoder sie Bwird als x verwendet .
  • Ich verwende Alias-Importe für alle Bezeichner aus ceylon.language, die mehrmals verwendet werden (einschließlich der variableAnmerkung, die jetzt ist l).
  • Ich habe die Klasse E(für die Umgebung) und die s(Schritt) -Methode entfernt - alles passiert jetzt innerhalb der runFunktion.
  • Anstatt .integerzum Abrufen des Codepunkts eines Zeichens zu verwenden, erhalten Sie .hashdasselbe Ergebnis. So string*.hashist das gleiche wiestring.map(Character.integer) (gibt einen Iterationswert der Codepunkte aus einer Zeichenfolge an).
  • Wenn ein Typ per Alias ​​importiert wird, is I ...ist er kürzer als exists ....
  • Wenn Sie etwas (z. B. x) in eine Zeichenfolge konvertieren , "``t``"ist es kürzer als t.string(oder was ich für ein Zeichen verwendet habe String{t}).
  • Funktionen, die nur einmal verwendet werden, können häufig inline gesetzt werden.

Hier ist die formatierte (und kommentierte) Version:

// Purple – a self-modifying, "one-instruction" language.
//
// Question:  http://codegolf.stackexchange.com/q/65411/2338
// My answer: http://codegolf.stackexchange.com/a/65492/2338

import ceylon.language {
    l=variable,
    I=Integer,
    x=nothing,
    p=process,
    m=map
}

shared void run() {
    try {
        // Reading code from file certainly takes more than 73 characters,
        // this isn't worth the 10% bonus.
        if (exists d = p.arguments[0]) {

            // The memory tape, as a Map<Integer, Integer>.
            // We can't modify the map itself, but we
            // can replace it by a new map when update is needed.
            l value t = m {
                // It is initialized with the code converted to Integers.
                // We use `.hash` instead of `.integer` because it is shorter.
                *d*.hash.indexed };

            // three registers
            l I a = 0;
            l I b = 0;
            l I i = 0;

            // get value from memory
            I g(I j) =>
                    t[j] else 0;

            // cached input which is still to be read
            l {I*} c = [];

            // get value from stdin.
            // we can only comfortably access stdin by line, so we read a whole line
            // and cache the rest for later.
            I o {
                if (c == []) {
                    if (exists e = p.readLine()) {
                        c = e*.hash.chain { 10 }; // convert string into ints, append \n
                    } else {
                        // EOF – return just -1 from now on.
                        c = { -1 }.cycled;
                    }
                }
                assert (is I r = c.first);
                c = c.rest;
                return r;
            }


            // Map of "functions" for fetching values.
            // We wrap the values in iterable constructors for lazy evaluation
            //  – this is shorter than using (() => ...).
            // The keys are the (Unicode/ASCII) code points of the mapped
            // source code characters.
            value f = m {
                // a
                97 -> { a },
                // b
                98 -> { b },
                // A
                65 -> { g(a) },
                // B
                66 -> { g(b) },
                // i
                105 -> { i },
                // o
                111 -> { o },
                // 1
                49 -> { 1 }
            };

            // Map of functions for "storing" results.
            // The values are void functions taking an Integer,
            // the keys are the ASCII/Unicode code points of the corresponding
            // source code characters.
            value s = m {
                // a
                97 -> ((I v) => a = v),
                // b
                98 -> ((I v) => b = v),
                // Modification of the memory works by replacing the map with a new one.
                // This is certainly not runtime-efficient, but shorter than importing
                // ceylon.collections.HashMap.
                // A
                65 -> ((I v) => t = m { a->v, *t }),
                // B
                66 -> ((I v) => t = m { b->v, *t }),
                // i
                105 -> ((I v) => i = v),
                // o – output as a character.
                111 -> ((I v) => p.write("``v.character``"))
            };

            // accessor function for the f map
            I h(I v) =>
                    f[v]?.first else x;

            // the main loop, can only be left by exception
            while (0 < 1) {
                (s[g(i)] else x)(h(g(i + 1)) - h(g(i + 2)));
                i += 3;
            }
        }
    } catch (AssertionError e) {
        // abort silently
    }
}
Paŭlo Ebermann
quelle
Ich habe einen Teil dieses Codes für einen "Parallelinterpreter" wiederverwendet, der versucht, alle anhaltenen Programme zu finden. (Es gibt viele von ihnen.) (Dort habe ich eine Nicht-E / A-Version von Purple verwendet, da E / A viel Platz
beansprucht