Golf un intérprete morado

13

Golf un intérprete morado

El púrpura es un esolang que está diseñado con dos propósitos principales:

  • Para ser una minimización de Aubergine , ya que simplemente no hay suficientes lenguajes de una sola instrucción auto modificables.
  • Para admitir la posibilidad de intérpretes de golf terriblemente pequeños . Mi primer pase en un intérprete de Python 2 razonablemente completo es de solo 702 bytes, y estoy seguro de que un golfista más experimentado podría afeitarse un poco.

Su objetivo es escribir un intérprete para este idioma.

Información sobre púrpura:

Un programa púrpura es una secuencia de caracteres colocados en una matriz de memoria infinita y direccionable, de modo que el primer carácter del programa se coloca en la dirección cero. El resto de la matriz (tanto antes como después de donde se almacena el programa Purple) se inicializa a cero.

Hay tres registros en púrpura, llamados a y b y i , cada uno de los cuales puede contener un número entero firmado y se inicializa a cero. i también es el puntero de instrucciones, y siempre apunta a la instrucción Purple que se está ejecutando actualmente.

Cada ciclo, el intérprete leerá una secuencia de tres caracteres contiguos a partir de la ubicación de memoria indicada por el puntero de la instrucción e intentará ejecutar esta secuencia como la instrucción Púrpura. Posteriormente, el puntero de instrucciones siempre se incrementa en 3.

Sintácticamente, la instrucción Púrpura consta de tres caracteres (o codificaciones de los mismos) en una fila, como " xyz ".

El primer carácter x puede ser cualquiera de los siguientes:

abABio

Estos símbolos tienen el siguiente significado:

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.

Los otros dos bytes y y z pueden ser cualquiera de los siguientes:

abABio1

Cada uno de estos símbolos tiene el siguiente significado:

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.

Después de buscar la instrucción, el intérprete de Purple evaluará y y luego z , restará el resultado de z del resultado de y , y luego realizará la acción indicada por x en la diferencia.

Si la secuencia de tres caracteres (o sus codificaciones) no es una instrucción púrpura válida, el intérprete se detiene inmediatamente sin dar ningún error.

Su intérprete debe:

  • Sea un programa completo, no una función.
  • Nunca envíe a stderr, a menos que se lea EOF .
  • Comportarse de manera idéntica a la implementación de referencia en todas las entradas bien formadas que no involucren números muy grandes, incluidos los programas de prueba que se proporcionan a continuación. (Bueno, idénticamente hasta el momento, ¡puede correr más lento, pero no demasiado!)

Puede proporcionar el programa al intérprete en cualquier forma que desee: leerlo desde un archivo, incrustarlo en el programa como una cadena o leerlo desde stdin.

Casos de prueba:

El programa

ooo

cuando se ejecuta con entrada

z!

debería rendir

Y

El programa

bbboobiii

cuando se ejecuta con entrada

It's a cat program.

(o cualquier otro insumo) debería producir

It's a cat program.

(o cualquier entrada que haya recibido) y luego comience de nuevo y haga lo mismo nuevamente .


El programa

Aoab11bi1bABoAaiba

cuando se ejecuta con entrada

0

debería rendir

0

y luego detener, pero cuando se ejecuta con entrada

1

debería continuar produciendo

1

Siempre.


El programa

b1bbb1oAbabaa1ab1Ab1Bi1b

debería rendir

b1bbb1oAbabaa1ab1Ab1Bi1b

El programa

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

debería rendir

Hello, World!

Puntuación:

Este es el , por lo que gana la fuente más corta en bytes, modificada potencialmente por el siguiente bono.

Prima:

  • -10% si su intérprete lee un nombre de archivo de stdin o de un argumento de línea de comando y carga el programa desde el archivo.
quintapia
fuente
1
¿Cuál es el tamaño de las celdas de memoria? bytes, caracteres (¿unicode?), enteros grandes (arbitrarios)? Parece que está usando "carácter" y "byte" con el mismo significado.
Paŭlo Ebermann
@ PaŭloEbermann, supongo que es específico de la implementación; por ejemplo, necesito usar uint32para los caracteres y MAXINT para las entradas
gato del
2
@sysreq ¿Es eso realmente un bloqueador? Su implementación podría tener simplemente dos cintas, una para índices negativos y otra para índices positivos. (Sí, esto requerirá un poco más de código, pero creo que no tanto).
Paŭlo Ebermann,
1
@sysreq básicamente, un autointerpretador Purple sería un programa que lee un programa Purple de stdin y luego hace lo que ese programa haría. El primer programa Purple (el intérprete) puede ejecutarse en el intérprete que desee. Un programa que sobrescribe completamente las direcciones de memoria más bajas con la entrada, luego se elimina a sí mismo antes de que salte de alguna manera al código de lectura (aunque no creo que esto sea realmente posible).
quintopia
2
Estuve tan cerca de tener un tiempo de ejecución capaz de autointerpretarse, pero llegué demasiado tarde.
gato

Respuestas:

7

Pyth, 148 128 121 bytes (o 124 * .9 = 111.6, ver abajo)

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

Banco de pruebas

Código dado en la primera línea de STDIN, entrada al programa Purple en el resto de STDIN. Para usar código con líneas nuevas, use una versión alternativa en la parte inferior.

Razonablemente golfizado. Aquí está con saltos de línea y sangría para mayor claridad:

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

Básicamente, un #bucle realiza la ejecución y se detiene a través de un corte de error.

ay bse combinan en una sola variable, J. Zes el puntero de instrucciones. kes entrada al programa Purple. Hes la cinta, representada como un diccionario. bes el resultado actual Yes el primer byte actual de la instrucción.

Lectura del archivo:

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

Indique el nombre del archivo como primera línea de STDIN. Prueba de funcionamiento:

$ 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
fuente
5

JavaScript (ES6), 292 bytes

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

Explicación

Las respuestas de JavaScript son siempre extrañas cuando STDINy STDOUTse requieren ...

El primer aviso es la entrada para la cadena del programa. Cada solicitud que resulte de una oinstrucción leerá solo el primer carácter.

evalse usa para reemplazar una frase común que ahorra algunos bytes. Sin golf y sin el evalprograma se ve así:

// 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
usuario81655
fuente
2
¿Se c="charCodeAt"puede reemplazar el segundo por solo c?
Dendrobium
¿El acceso a matrices con índices negativos funciona en JavaScript?
nimi
@Dendrobium Wow, no sé cómo me perdí eso jaja! Gracias.
user81655
2
@nimi Funciona. Las matrices en sí mismas no admiten índices negativos, pero esto aprovecha el hecho de que también se comportan como objetos. array[-1] = 1es el mismo que array = { "-1": 1 }. Se puede acceder a ambos con array[-1].
user81655
@ user81655: Ah, bien, no lo sabía.
nimi
3

Ceilán, 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){}}

Se comporta de manera un poco diferente a la implementación de referencia cuando el programa intenta leer la entrada en EOF: la implementación de referencia se bloquea con un TypeError, que es demasiado costoso para reproducir aquí (y también es probable que sea un error), por lo que esto devolverá -1 en su lugar ( repetidamente, si es necesario).

(Sin embargo, cuando intente escribir este -1 en stdout, el intérprete terminará con un OverflowError. Sucederá algo similar si sale un número entero fuera del rango Unicode).

El intérprete toma el programa como su primer argumento de línea de comando (asegúrese de citarlo para su shell cuando contenga espacios en blanco u otras cosas interesantes).

En Ceilán solo podemos leer fácilmente la entrada en línea (supongo que esto cambiará en una de las próximas versiones), así que cuando o se usa para leer, estoy leyendo una línea completa y almacenando las partes para usos futuros. Supongo que funciona de manera similar en la implementación de Python cuando está conectado a una terminal.


Al intentar ejecutar un comando (parte) que no es uno de los caracteres válidos, el nothing provocará que se arroje un AssertionError, que luego atraparemos en el bloque catch alrededor del bucle principal.

Creo que esto debería ser preferiblemente un tipo de excepción personalizado (ya que AssertionError también podría ocurrir en otros lugares si tengo un error), pero eso tomaría mucho más espacio, consumiendo la mayoría de las mejoras que hice desde la primera versión.

Algunos trucos utilizados para jugar al golf:

  • Las versiones anteriores usaban ceylon.collection.HashMap; en cambio, ahora usamos un mapa inmutable tal como lo creó la mapfunción, y creamos uno nuevo cada vez Ao Bse usa como x .
  • Utilizo alias-imports para todos los identificadores de ceylon.language que se usan más de una vez (incluida la variableanotación, que es ahora l).
  • Me deshice de la clase E(para el entorno) y el smétodo (paso): ahora todo sucede dentro de la runfunción.
  • En lugar de usar .integerpara obtener el punto de código de un personaje, .hashda el mismo resultado. Por string*.hashlo tanto, es lo mismo questring.map(Character.integer) (da un iterable de los puntos de código de una cadena).
  • Cuando un tipo se importa alias, is I ... es más corto que exists ....
  • Al convertir algo (p x. Ej. ) En una cadena, "``t``"es más corto quet.string (o, lo que usé para un personaje String{t}).
  • Las funciones utilizadas solo una vez a menudo pueden estar en línea.

Aquí está la versión formateada (y comentada):

// 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
fuente
Reutilicé parte de ese código para un "intérprete paralelo" que intentaba encontrar todos los programas de detención. (Hay muchos de ellos.) (Allí utilicé una versión no púrpura de E / S, ya que la E / S ocupa mucho espacio y no se usa en esa tarea).
Paŭlo Ebermann,