Escribir un intérprete ~ ATH

12

El popular webcomic Homestuck utiliza un lenguaje de programación llamado ~ATHpara destruir universos. Si bien este desafío de código de golf no es escribir un programa para aniquilar nuestra existencia, estaremos destruyendo algunas entidades más mansas (aunque menos interesantes): las variables

~ATH(pronunciado "hasta la muerte", observe cómo funciona ~ath"tilde ath") creando una variable llamada THIS, ejecutando un comando con EXECUTEy terminando el programa con THIS.DIE(). Aquí se puede encontrar una página wiki para el uso del idioma en Homestuck . El objetivo de este desafío será crear un ~ATHintérprete.

En aras del desafío, voy a crear algunos detalles ~ATHque realmente no existen, pero lo haré (algo) útil.

  • El lenguaje solo funcionará con enteros, que se declaran con import <variable name>;. La variable se establecerá automáticamente en un valor de 0. Solo se puede importar una variable a la vez.
  • Se xpuede copiar una variable escribiendo bifurcate x[y,z];, lo que eliminará la variable xy la reemplazará con variables idénticas yy z. Tenga en cuenta que no puede crear una variable con el mismo nombre que la eliminada. Esencialmente, se cambia el nombre de una variable, luego se crea una copia de la variable con un nombre diferente. Esto parece como una característica estúpida, pero la estupidez está muy profundamente arraigado en Homestuck.
  • La sintaxis para escribir un programa que ejecuta código xes ~ATH(x){EXECUTE(<code>)}. Si desea ejecutar un código de dos variables al mismo tiempo, se convierte en el código anidado, como esto: ~ATH(x){~ATH(y){EXECUTE(<code>)}}. Todos los comandos <code>se ejecutarán en ambos xy y.
  • Ahora pasemos a los comandos. +incrementa las variables relevantes en 1 y las -disminuye en 1. Y ... eso es todo.
  • La característica final de ~ATHes que mata todo lo que funciona. Las variables se imprimen en el formato <name>=<value>(seguido de una nueva línea) en el comando [<name>].DIE();. Posteriormente, el programa imprime la palabra DIE <name>y una nueva línea varias veces igual al valor absoluto del valor de la variable. Cuando las variables se eliminan simultáneamente con [<name1>,<name2>].DIE();(puede tener tantas variables eliminadas como desee, siempre que existan), el DIE()comando se ejecuta secuencialmente en las variables.

Programas de ejemplo

Programa 1:

import sollux;                  //calls variable "sollux"
import eridan;                  //calls variable "eridan"
~ATH(sollux){EXECUTE(--)}       //sets the value of "sollux" to -2
~ATH(eridan){EXECUTE(+++++)}    //sets the value of "eridan" to 5
[sollux].DIE();                 //kills "sollux", prints "DIE sollux" twice
~ATH(eridan){EXECUTE(+)}        //sets the value of "eridan" to 6
[eridan].DIE();                 //kills "eridan", prints "DIE eridan" 6 times

Salida:

sollux=-2
DIE sollux
DIE sollux
eridan=6
DIE eridan
DIE eridan
DIE eridan
DIE eridan
DIE eridan
DIE eridan

Programa 2:

import THIS;                    //calls variable "THIS"
~ATH(THIS){EXECUTE(++++)}       //sets the value of "THIS" to 4
bifurcate THIS[THIS1,THIS2];    //deletes "THIS", creates variables "THIS1" and "THIS2" both equal to 4
~ATH(THIS1){EXECUTE(++)}        //sets the value of "THIS1" to 6
[THIS1,THIS2].DIE();            //kills "THIS1" and "THIS2", prints "DIE THIS1" 6 times then "DIE THIS2" 4 times

import THAT;                                         //calls variable "THAT"
bifurcate THAT[THESE,THOSE];                         //deletes "THAT", creates variables "THESE" and "THOSE"
~ATH(THESE){~ATH(THOSE){EXECUTE(+++)}EXECUTE(++)}    //sets the value of "THESE" and "THOSE" to 3, then sets the value of "THESE" to 5
[THESE,THOSE].DIE();                                 //kills "THESE" and "THOSE", prints "DIE THESE" 5 times then "DIE THOSE" 3 times

Salida:

THIS1=6
DIE THIS1
DIE THIS1
DIE THIS1
DIE THIS1
DIE THIS1
DIE THIS1
THIS2=4
DIE THIS2
DIE THIS2
DIE THIS2
DIE THIS2
THESE=5
DIE THESE
DIE THESE
DIE THESE
DIE THESE
DIE THESE
THOSE=3
DIE THOSE
DIE THOSE
DIE THOSE

Este es el código de golf, por lo que se aplican reglas estándar. El código más corto en bytes gana.

Arcturus
fuente
2
Hasta la muerte. Veo lo que hiciste alli.
Trauma digital
3
@ DigitalTrauma Tengo que pasarle el crédito a Andrew Hussie (el tipo que escribe Homestuck) por haber inventado el nombre.
Arcturus
1
@sysreq ~ATHutiliza un punto y coma como el fin de línea para el import, bifurcatey DIElos comandos. Tanto REPL como archivos están bien. Se requiere mayúsculas y minúsculas tanto en la entrada como en la salida (estoy tratando de igualar la real ~ATHtanto como sea posible).
Arcturus
1
@sysreq Tuve que cambiar algunas cosas para que el lenguaje realmente hiciera algo en la vida real, los pecs que describí están bien.
Arcturus
2
Estoy sinceramente sorprendido de que esta pregunta no haya recibido más respuestas, y aún más sorprendido de que no haya una horda de magos Perl armados con varitas mágicas de expresión
gato

Respuestas:

3

Python 2.7.6, 1244 1308 1265 1253 1073 1072 1071 1065 1064 1063 bytes

Muy bien, no estoy batiendo ningún récord aquí, pero se trata de que el Python más pequeño irá en la medida en que lea la entrada de una vez desde un archivo en lugar de hacerlo secuencialmente con el tiempo. Trataré de aumentar esto más adelante en un idioma diferente (y un intérprete, no solo un analizador). Hasta entonces, disfruta de la monstruosidad asquerosamente horrible.

Nota : abre un archivo llamado ten el directorio de trabajo. Para que abra un argumento de línea de comando, agregue import sysal principio del archivo y cambie 't'asys.argv[1]

n=s='\n';m=',';X='[';Y=']';c=';';A='~ATH';D='import';b,g,k=[],[],[];r=range;l=len;f=open('t','r').read().split(n)
def d(j,u):
 p=[]
 for e in j:
  if e!=u:p.append(e)
 return''.join(p)
for h in r(l(f)):f[h]=f[h].split('//')[0].split()
while[]in f:f.remove([])
for h in r(l(f)):
 i=f[h]
 if i[0]==D and l(i)==2and i[1][l(i[1])-1]==c and d(i[1],c)not in b:g.append(0);b.append(d(i[1],c))
 elif i[0].startswith(A):
  i=i[0].split('){')
  for e in r(l(i)):
   if i[e].startswith(A):
    i[e]=i[e].split('(')
    if i[0][1]in b:g[b.index(i[0][1])]+=(i[1].count('+')-i[1].count('-'))
 elif i[0].startswith('bifurcate')and l(i)==2and i[1][l(i[1])-1]==c:
  i=i[1].split(X)
  if i[0] in b:
   z=d(d(i[1],c),Y).split(m)
   for e in r(l(z)):g.append(g[b.index(i[0])]);b.append(z[e])
   g.remove(g[b.index(i[0])]);b.remove(i[0])
 elif i[0].startswith(X)and i[0].endswith('.DIE();')and l(i)==1:
  z=d(i[0],X).split(Y)[0].split(m)
  for e in r(l(z)):
   k.append((z[e],g[b.index(z[e])]))
for e in r(l(k)):k0=k[e][0];k1=k[e][1];s+=k0+'='+str(k1)+n+('DIE '+k0+n)*abs(k1)
print s
gato
fuente
2

Python 2, 447 475 463 443 bytes

exec("eNp1UUtrAjEQvu+vCEshiYnrxl7KbqOUVmjvCoUkxUdiG7BRkpW2iP3tTVwrReppMsx8r4l936x9A8JXoN5kmu/2WeCxK0KjrSu8mWmEs0Ad96YI27lDPu/1is7wKqcQ0kBLenM+ty0nilu4zqnPtYCSQcXL2P2LmNvl1i9mjWlBUhwKbRt14uhHjlSvjzVy1tqswO/7AjsSpKtwIpGvt2zALqyNnkf3k/FIolb2ACjlpe2jR6lk8fAUQbKNulx7YIF1IDkqwmZlGwQpxNXGW9cASyCHZKqFVVOCoJQOEhjxABKLO7N5QGmET5qOs/Qfoqq6TGUfb3ZlgKvOnOxTwJKpDq6HSLzsVfK1k7g1iB7Hd9/JWh3T9wclkYwTlY4odP0nnvk0C3RUwj95/ZUq".decode('base64').decode('zip'))

Resulta que comprimir y codificar el programa base64 todavía guarda bytes sobre la versión normal. A modo de comparación, aquí está el normal:

import sys,re
d={}
s=sys.stdin.read()
s,n=re.subn(r"//.*?$",'',s,0,8)
s,n=re.subn(r"import (.*?);",r"d['\1']=0;",s,0,8)
s,n=re.subn(r"bifurcate (.*?)\[(.*?),(.*?)\];",r"d['\2']=d['\3']=d['\1'];del d['\1'];",s,0,8)
s,n=re.subn(r"([+-])",r"\g<1>1",s,0,8)
s,n=re.subn(r"EXECUTE\((.*?)\)",r"0\1",s,0,8)
s,n=re.subn(r"\[(.*?)\]\.DIE\(\);",r"for i in '\1'.split(','):print i+'='+`d[i]`+('\\n'+'DIE '+i)*abs(d[i])",s,0,8)
n=1
s=s[::-1]
while n:s,n=re.subn(r"\}([+-01]*);?([^}]*?)\{\)(.*?)\(HTA~",r";\g<2>0+\1=+]'\3'[d;\1",s,0,8)
exec(s[::-1])

Básicamente, la solución de "varitas mágicas regexy" que se deseaba. Lee en todo el programa desde stdin como una sola cadena, reemplaza las expresiones ~ ATH con expresiones de Python que hacen la semántica descrita, y exec () s la cadena resultante.

Para ver lo que está haciendo, mire el programa de Python al que se traduce el segundo programa de prueba proporcionado:

d['THIS']=0;                    
0+1+1+1+1;d['THIS']+=0+1+1+1+1+0;       
d['THIS1']=d['THIS2']=d['THIS'];del d['THIS'];    
0+1+1;d['THIS1']+=0+1+1+0;        
for i in 'THIS1,THIS2'.split(','):print i+'='+`d[i]`+('\n'+'DIE '+i)*abs(d[i])            

d['THAT']=0;                                         
d['THESE']=d['THOSE']=d['THAT'];del d['THAT'];                         
0+1+1;d['THESE']+=0+1+1+00+1+1+1;d['THOSE']+=0+1+1+1+0;    
for i in 'THESE,THOSE'.split(','):print i+'='+`d[i]`+('\n'+'DIE '+i)*abs(d[i])                                 

Es bueno que 00 == 0: P

Obviamente, se podrían guardar algunos bytes explotando la ambigüedad en las reglas. Por ejemplo, no se dice qué debería suceder en el caso de que alguien intente con DIE()una variable que no ha sido importeditada, o que ya ha sido bifurcated. Mi suposición basada en la descripción fue que debería haber un error. Si no se requiere ningún error, podría eliminar la deldeclaración.

EDITAR: se corrigió un error que los casos de prueba proporcionados no probaron. A saber, tal como era, cada ~ATHbloque restablecía la variable a cero antes de incrementarla. Me costó 28 bytes arreglar eso. Si alguien ve una mejor manera de reemplazar ~ATHbloques, me encantaría saberlo.

EDIT 2: ahorró 12 bytes desenrollando el ciclo regex, haciéndolos todos subns y dejando que la compresión se encargue de la repetición.

EDIT 3: ahorró 20 bytes más al reemplazar el forbucle interno con una multiplicación de cadena.

quintapia
fuente
¡Hey, finalmente las varitas mágicas de la regexia! ¡No podré superar esto, pero bien hecho!
gato
Mi implementación ignora por completo las cosas que no están explícitamente cubiertas por las reglas, lo que significa que está bien que no arroje un error y simplemente ignore esos casos también.
gato
puede guardar algunos bytes haciendo en import sys,relugar deimport sys;import re
cat
1
el resaltado de sintaxis hace que esto sea mucho más fácil de leer
cat
1
@cat lo siento, olvidé responderte hace mucho tiempo. lo ejecuta desde la línea de comando y canaliza la entrada desde un archivo:python ~ath.py < program.~ath
quintopia