Golf un interprete viola


13

Golf un interprete viola

Purple è un esolang progettato con due scopi principali:

  • Per essere una minimizzazione della melanzana , dal momento che non ci sono abbastanza lingue auto-modificanti in una sola istruzione in giro.
  • Ammettere la possibilità di interpreti di golf terrificantemente piccoli . Il mio primo passaggio a un interprete Python 2 ragionevolmente completo è di soli 702 byte, e sono sicuro che un golfista più esperto potrebbe radersi un po 'da quello.

Il tuo obiettivo è scrivere un interprete per questa lingua.

Informazioni su Viola:

Un programma Purple è una sequenza di caratteri posizionati in una matrice di memoria infinita e indirizzabile in modo tale che il primo carattere del programma sia posizionato all'indirizzo zero. Il resto dell'array (sia prima che dopo la memorizzazione del programma Purple) viene inizializzato su zero.

Esistono tre registri in viola, chiamati a e b e i , ognuno dei quali può contenere un numero intero con segno ed è inizializzato su zero. i è anche il puntatore di istruzione, e sempre punti all'istruzione viola attualmente in esecuzione.

Ad ogni ciclo, l'interprete leggerà una sequenza di tre caratteri contigui a partire dalla posizione di memoria indicata dal puntatore dell'istruzione e tenterà di eseguire questa sequenza come l'istruzione Purple. Successivamente, il puntatore dell'istruzione viene sempre incrementato di 3.

Sintatticamente, l'istruzione Purple è composta da tre caratteri (o codifiche degli stessi) di seguito, come " xyz ".

Il primo carattere x può essere uno dei seguenti:

abABio

Questi simboli hanno il seguente significato:

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.

Gli altri due byte y e z possono essere uno dei seguenti:

abABio1

Ognuno di questi simboli ha il seguente significato:

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.

Dopo aver recuperato l'istruzione, l'interprete viola valuterà ye quindi z , sottrarrà il risultato di z dal risultato di y , quindi eseguirà l'azione indicata da x sulla differenza.

Se la sequenza di tre caratteri (o loro codifiche) non è un'istruzione Purple valida, l'interprete si ferma immediatamente senza dare alcun errore.

Il tuo interprete deve:

  • Sii un programma completo, non una funzione.
  • Non emettere mai su stderr, a meno che non venga letto EOF .
  • Comportarsi in modo identico all'implementazione di riferimento su tutti gli input ben formati che non implicano numeri molto grandi, compresi i programmi di test indicati di seguito. (Beh, identicamente all'altezza dei tempi - può funzionare più lentamente, ma non troppo!)

Puoi fornire il programma all'interprete in qualsiasi forma desideri: leggilo da un file, incorporalo nel programma come una stringa o leggilo da stdin.

Casi test:

Il programma

ooo

quando eseguito con input

z!

dovrebbe cedere

Y

Il programma

bbboobiii

quando eseguito con input

It's a cat program.

(o qualsiasi altro input) dovrebbe cedere

It's a cat program.

(o qualunque input ricevuto) e ricominciare da capo e fare di nuovo la stessa cosa .


Il programma

Aoab11bi1bABoAaiba

quando eseguito con input

0

dovrebbe cedere

0

e poi fermarsi, ma quando eseguito con input

1

dovrebbe continuare a produrre

1

per sempre.


Il programma

b1bbb1oAbabaa1ab1Ab1Bi1b

dovrebbe cedere

b1bbb1oAbabaa1ab1Ab1Bi1b

Il programma

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

dovrebbe cedere

Hello, World!

punteggio:

Questo è , quindi vince la fonte più breve in byte, potenzialmente modificata dal seguente bonus.

Bonus:

  • -10% se l'interprete legge un nome file da stdin o da un argomento della riga di comando e carica il programma dal file.

1
Qual è la dimensione delle celle di memoria? byte, caratteri (unicode?), interi (arbitrari) di grandi dimensioni? Sembra che tu stia usando "carattere" e "byte" con lo stesso significato.
Paŭlo Ebermann,

@ PaŭloEbermann, credo che sia specifico per l'implementazione; per esempio, devo usare uint32per i personaggi e MAXINT per gli ints
cat

2
@sysreq È davvero un blocco? L'implementazione potrebbe semplicemente avere due nastri, uno per gli indici negativi e uno per gli indici positivi. (Sì, ci vorrà un po 'più di codice, ma non molto, credo.)
Paŭlo Ebermann,

1
@sysreq fondamentalmente, un auto-interprete viola sarebbe un programma che legge un programma viola da stdin e quindi fa qualunque cosa farebbe quel programma. Il primo programma Purple (l'interprete) può essere eseguito in qualunque interprete desideri. Un programma che sovrascrive completamente gli indirizzi di memoria più bassi con l'input, quindi si elimina da solo prima di saltare in qualche modo al codice di lettura si qualificherebbe (anche se non penso che ciò sia effettivamente possibile).
Quintopia,

2
Mi avvicinai così tanto ad avere un'autonomia capace di autointerpretazione, ma ero troppo tardi.
cat

Risposte:


7

Pyth, 148 128 121 byte (o 124 * .9 = 111.6, vedi in basso)

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

Suite di test

Codice fornito sulla prima riga di STDIN, immesso nel programma Purple sul resto di STDIN. Per usare il codice con newline, usa la versione alternativa in fondo.

Ragionevolmente golf. Eccolo con interruzioni di riga e rientro per chiarezza:

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

Fondamentalmente, un #loop esegue l'esecuzione e si interrompe tramite l'interruzione dell'errore.

ae bsono combinati in una singola variabile, J. Zè il puntatore alle istruzioni. kviene immesso nel programma Purple. Hè il nastro, rappresentato come un dizionario. bè il risultato attuale. Yè il primo byte corrente dell'istruzione.

Lettura dal file:

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

Assegna il nome del file come prima riga di STDIN. Prova:

$ 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!

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

Spiegazione

Le risposte JavaScript sono sempre strane quando STDINe STDOUTsono richieste ...

Il primo prompt è l'input per la stringa di programma. Ogni prompt risultante da oun'istruzione leggerà solo il primo carattere.

evalè usato per sostituire una frase comune che salva alcuni byte. Ungolfed e senza il evalprogramma si presenta così:

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

2
Il secondo può c="charCodeAt"essere sostituito con just c?
Dendrobium,

L'accesso agli array con indici negativi funziona in JavaScript?
nimi

@Dendrobium Wow, non so come mi sia perso quell'haha! Grazie.
user81655

2
@nimi Funziona. Gli array stessi non supportano gli indici negativi, ma questo sfrutta il fatto che si comportano anche come oggetti. array[-1] = 1è lo stesso di array = { "-1": 1 }. Entrambi sono accessibili con array[-1].
user81655

@ user81655: Ah bello, non lo sapevo.
nimi,

3

Ceylon, 827 792 671 byte

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

Si comporta in modo leggermente diverso rispetto all'implementazione di riferimento quando il programma tenta di leggere l'input su EOF: l'implementazione di riferimento si arresta in modo anomalo con un TypeError, che è troppo costoso da riprodurre qui (e probabilmente anche un bug), quindi restituirà -1 invece ( ripetutamente, se necessario).

(Quando si tenta di scrivere questo -1 su stdout, l'interprete finirà con un OverflowError. Simile succederà se viene emesso un intero al di fuori dell'intervallo Unicode.)

L'interprete prende il programma come primo argomento della riga di comando (assicurati di citarlo per la tua shell quando contiene spazi bianchi o altre cose interessanti).

In Ceylon possiamo solo leggere facilmente l'input in linea (suppongo che questo cambierà in una delle prossime versioni), quindi quando oviene usato per leggere, sto leggendo un'intera riga e bufferizzo le parti per usi futuri. Immagino che funzioni in modo simile nell'implementazione Python quando è collegato a un terminale.


Quando si tenta di eseguire un comando (parte) che non è uno dei caratteri validi, nothingviene generato un errore AssertionError, che viene quindi catturato nel blocco catch attorno al loop principale.

Penso che questo dovrebbe essere preferibilmente un tipo di eccezione personalizzato (poiché AssertionError potrebbe verificarsi anche in altri posti se ho un bug), ma ciò richiederebbe molto più spazio, consumando la maggior parte dei miglioramenti che ho apportato dalla prima versione.

Alcuni trucchi utilizzati per il golf:

  • Le versioni precedenti usavano un ceylon.collection.HashMap - invece ora usiamo una mappa immutabile come creata dalla mapfunzione e ne creiamo una nuova ogni volta Ao Bviene usata come x .
  • Uso alias-imports per tutti gli identificatori di ceylon.language che vengono utilizzati più di una volta (inclusa l' variableannotazione, che è ora l).
  • Mi sono sbarazzato della classe E(per ambiente) e del smetodo (step) - tutto ora accade all'interno della runfunzione.
  • Invece di usare .integerper ottenere il punto di codice di un personaggio, .hashdà lo stesso risultato. Quindi string*.hashè lo stesso di string.map(Character.integer)(fornisce un iterabile dei punti di codice da una stringa).
  • Quando un tipo viene importato alias, is I ...è più breve di exists ....
  • Quando converti qualcosa (ad es. x) In una stringa, "``t``"è più breve di t.string(o, quello che ho usato per un personaggio,String{t} ).
  • le funzioni usate solo una volta possono essere spesso incorporate.

Ecco la versione formattata (e commentata):

// 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
    }
}

Ho riutilizzato parte di quel codice per un "interprete parallelo" che cercava di trovare tutti i programmi di arresto. (Ce ne sono molti.) (Lì ho usato una versione non I / O di Purple, dato che I / O occupa molto spazio e non è usato in quel compito.)
Paŭlo Ebermann il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.