¡Escribe un intérprete de pollo!

8

¡Tienes que escribir un intérprete para un lenguaje genial llamado Chicken !

Debería leer un programa Chicken de un archivo, entrada estándar, argumentos de programa o función, o lo que sea más conveniente para su idioma, así como la entrada al programa.

Debe imprimir o devolver el resultado de interpretar el programa de acuerdo con las especificaciones del lenguaje Chicken.

Más descripción sobre el idioma .


Descripción general del programa de pollo

Chicken opera en una sola pila, que compone todo su modelo de memoria. A medida que se ejecutan las instrucciones, el programa empujará y extraerá valores de la pila, pero también hay instrucciones que permiten que el programa modifique otras partes de la pila a voluntad.

Hay tres segmentos en la pila:

  1. Los registros, en los índices 0 y 1. El índice 0 es una referencia a la pila misma, y ​​el índice 1 es una referencia a la entrada del usuario. Principalmente utilizado para la instrucción 6 (ver más abajo).
  2. El código cargado: para cada línea de código hay una celda en este segmento que contiene el número de "gallinas" en la línea. Esto se rellena con un 0 (código de operación para terminar el programa) al final.
  3. La pila de programas real, donde los valores se empujan / reventan a medida que se ejecuta el programa. Tenga en cuenta que los segmentos no están aislados, lo que significa que es posible crear código auto modificable o ejecutar código desde este segmento del espacio de la pila.

El pollo ISA

El conjunto de instrucciones de Chicken se basa en la cantidad de veces que aparece la palabra "pollo" en cada línea del programa. Una línea vacía termina el programa e imprime el valor más alto en la pila.

El conjunto de instrucciones Chicken, por número de "pollo" por línea:

  1. Empuje la cadena literal "pollo" a la pila
  2. Agregue los dos valores superiores de la pila como números naturales y empuje el resultado.
  3. Resta los dos valores superiores como números naturales y empuja el resultado.
  4. Multiplique los dos valores superiores como números naturales y presione el resultado.
  5. Compare dos valores superiores para la igualdad, presione 1 si son iguales y 0 en caso contrario.
  6. Mire la siguiente instrucción para determinar desde qué fuente cargar: 0 cargas de la pila, 1 cargas de la entrada del usuario. Parte superior de los puntos de pila para direccionar / indexar para cargar desde la fuente dada; cargar ese valor y empujarlo a la pila. Como se trata de una instrucción de doble ancho, el puntero de instrucción omite la instrucción utilizada para determinar la fuente.
  7. Parte superior de los puntos de la pila a la dirección / índice para almacenar. El valor a continuación aparecerá y se almacenará en la pila en el índice dado.
  8. La parte superior de la pila es un desplazamiento relativo para saltar. Si el valor debajo de eso es verdadero, entonces el programa salta por el desplazamiento.
  9. Interpreta la parte superior de la pila como ascii y empuja el personaje correspondiente.
  10. (10 + N) Inserta el número literal n-10 en la pila.

Ejemplo

Suponga que el programa es:

chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
(an empty line)

(Un programa de gato. Tenga en cuenta que la línea vacía es necesaria debido a que la línea anterior tiene 6 "pollo").

Aporte proporcionado al programa Chicken

Chicken

Salida

Chicken

La implementación de referencia de Chicken.js .


Detección de errores

El intérprete debe dejar un error y finalizar cuando cualquier palabra que no sea "pollo" esté presente en la fuente.


¡Buena suerte!

Tinta de valor
fuente
3
Debe copiar las especificaciones de idioma en la pregunta. Las preguntas no deben depender de enlaces externos.
mbomb007
Bueno, ¿por qué no haces eso tú también?
1
Es tu pregunta Tú determinas las especificaciones.
mbomb007
55
La entrada del archivo restringe este desafío a idiomas específicos. Por ejemplo, hace que sea imposible producir una respuesta de pollo, lo que estoy seguro de que estará de acuerdo es decepcionante.
Aaron

Respuestas:

1

Rubí, 335 bytes

Toma el nombre del archivo de entrada como un argumento de línea de comando y toma la entrada del usuario (para la instrucción # 6) de STDIN.

Debido a que la "verdad" de Ruby (todo excepto falsey nil) es diferente de la "verdad" de Javascript (Ruby verdad más 0, cadenas vacías, etc.), puede haber algunos casos extremos donde los programas que funcionan bien en un intérprete JS fallan en este debido a la instrucción # 8, como si ""está en la pila. Sin embargo, he arreglado el caso más grande, que es falso 0.

Funciona con el programa de prueba y el programa Hello World en el sitio web de Chicken.

+(/^(#{c='chicken'}|\s)*$/m=~f=$<.read+"

")
s=[0,STDIN.read]+f.lines.map{|l|l.split.size};s[0]=s;i=1
s<<(s[i]<10?[->{c},->{x,y=s.pop 2;x+y},->{x,y=s.pop 2;x-y},->{s.pop*s.pop},->{s.pop==s.pop},->{s[s[i+=1]][s.pop]},->{s[s.pop]=s.pop;s.pop},->{l,k,j=s.pop 3;i+=j if k&&k!=0;l},->{s.pop.chr}][s[i]-1][]:s[i]-10)while s[i+=1]>0
$><<s.pop

Explicación

El intérprete comienza inmediatamente ejecutando una coincidencia de expresiones regulares /^(chicken|\s)*$/mcontra todo el archivo ( $<.read), lo que garantiza que el archivo no contenga más que un chickenespacio en blanco. En Ruby, este operador devuelve el índice de la coincidencia, o nilsi no se encontró.

Aquí se usan dos trucos para guardar bytes: en lugar de coincidir directamente chicken, el operador de sustitución de cadenas #{}se usa en su lugar para asignar también esa cadena a una variable para más adelante (guarda 1 byte) y al almacenar el contenido del archivo en una variable para procesar , agrega dos líneas nuevas para permitir que la linesfunción luego agregue naturalmente un extra 0al final del conjunto de instrucciones. (Se necesitan dos debido a la nueva línea final ignorada, que es necesaria para el programa Chicken).

El error utilizado es NoMethodError: undefined method '+@' for nil:NilClass, que se realiza envolviendo la coincidencia de expresiones regulares en parens y colocando un +frente. Si el archivo coincide con el patrón, obtienes +0, que se evalúa 0y continúa normalmente.

A continuación, se ensambla la pila. La lista inicial debe crearse antes de que se pueda asignar la autorreferencia a la pila, de modo que se use un marcador de posición y luego se reemplace. El puntero de instrucción se establece en 1lugar de 2porque los operadores posteriores al incremento no existen en Ruby.

Finalmente, usa el truco lambda de @BassdropCumberwubwubwub para determinar qué empujar en la pila a continuación. Si una operación no introduce nada en la pila, el intérprete simplemente muestra un valor adicional para que la pila permanezca igual. (Esto ahorra bytes al agregar una operación de inserción en cada lambda).

Código sin golf:

f = $<.read + "\n\n"
+(/^(chicken|\s)*$/m =~ f)
s = [0, STDIN.read] + f.lines.map{|l|l.split.size}
s[0] = s
i = 1

while s[i += 1] > 0
    if s[i] < 10
        s.push [
            ->{'chicken'},
            ->{
                x,y = s.pop 2
                x+y
                },
            ->{
                x,y = s.pop 2
                x-y
                },
            ->{s.pop*s.pop},
            ->{s.pop==s.pop},
            ->{s[s[i+=1]][s.pop]},
            ->{s[s.pop]=s.pop;s.pop},
            ->{
                l,k,j=s.pop 3
                i+=j if k&&k!=0
                l
                },
            ->{s.pop.chr}
        ][s[i] - 1][]
    else
        s.push(s[i] - 10)
    end
end

print s.pop
Tinta de valor
fuente
En realidad, no creo que pueda acortar esto. (+1)
4

Javascript ES6, 398 bytes

Con mucho, el golf más largo que he hecho, estoy seguro de que esto se puede mejorar, pero mi cerebro no reconoce nada más que chickenen este momento.

(a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

Editaré la explicación cuando mi cerebro comience a funcionar nuevamente. Aquí hay una versión ungolfed por ahora.
Emite un valor falsey (0) para todo lo que no eschicken

(a,b)=>{
    for(c='chicken',s=[j=0,b,...A=a.split`
    `.map(m=>m.split(c).length-1)],i=A.length+2; // loop init
    j<A.length; // loop condition
    ( // everything else
        [
            _=>s[++i]=c,
            _=>s[--i]=s[i]+s[i+1],
            _=>s[--i]=s[i]-s[i+1],
            _=>s[--i]=s[i]*s[i+1],
            _=>s[--i]=s[i]==s[i+1],
            _=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],
            _=>s[s[i--]]=s[i--],
            _=>j+=s[--i]?s[--i+2]:0,
            _=>s[i]=String.fromCharCode(s[i])
        ][s[j+2]-1]
        ||(_=>s[++i]=s[j+1]-10)
    )(j++)
);
return /[^chicken \n]\w/g.test(a)?0:s[i]}

Pruébalo aquí

f=
  (a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

i.innerHTML = f(`chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
`, 'Hello world!')
<pre id=i>

Bassdrop Cumberwubwubwub
fuente
Bueno, déjame esperar algunas respuestas más y descubrir quién es el ganador.
Esto falla la "Detección de errores". Puede hacerlo agregando if(!/^(chicken\s?)+$/.test(a))throw'There are any words except "chicken".';justo al comienzo de su intérprete.
Ismael Miguel
@Matthew, ¿qué piensas sobre eso? Hay ciertos idiomas que no tienen un tipo de error, que generalmente pueden generar un valor falsey. Es un poco vago en el OP, así que supuse que estaba bien.
Bassdrop Cumberwubwubwub
Puede generar el error, diciéndoles que algo está mal.
1
@BassdropCumberwubwubwub Lo que significaba el OP es, por ejemplo, lanzar una excepción o generar algo stderro salir del programa con un código distinto de cero. Algo que muestra que algo no está bien. En Javascript, puede lanzar una excepción, devolver un objeto Error, mostrar una alerta, escribir en la consola usando console.erro()o algo similar.
Ismael Miguel