Écrire un ~ interprète ATH

12

Le célèbre webcomic Homestuck utilise un langage de programmation appelé ~ATHà détruire les univers. Bien que ce défi de golf de code ne soit pas d'écrire un programme pour anéantir notre existence, nous allons détruire des entités plus apprivoisées (bien que moins intéressantes): les variables

~ATH(prononcé "jusqu'à la mort", remarquez comment ~athest "tilde ath") fonctionne en créant une variable appelée THIS, en exécutant une commande avec EXECUTEet en terminant le programme avec THIS.DIE(). Une page wiki pour l'utilisation de la langue dans Homestuck peut être trouvée ici . L'objectif de ce défi sera de créer un ~ATHinterprète.

Pour relever le défi, je vais créer des détails ~ATHqui n'existent pas vraiment mais les rendre (quelque peu) utiles.

  • Le langage ne fonctionnera qu'avec des entiers, qui sont déclarés avec import <variable name>;. La variable sera automatiquement définie sur une valeur de 0. Une seule variable à la fois peut être importée.
  • Une variable xpeut être copiée en écriture bifurcate x[y,z];, ce qui supprimera la variable xet la remplacera par des variables identiques yet z. Notez qu'il ne peut pas créer une variable avec le même nom que celui supprimé. Essentiellement, une variable est renommée, puis une copie de la variable avec un nom différent est créée. Cela semble être une caractéristique stupide, mais la bêtise est très profondément ancrée dans Homestuck.
  • La syntaxe pour écrire un programme qui exécute du code xest ~ATH(x){EXECUTE(<code>)}. Si vous voulez exécuter du code sur deux variables simultanément, le code devient imbriqué, comme ceci: ~ATH(x){~ATH(y){EXECUTE(<code>)}}. Toutes les commandes de <code>seront exécutées sur xet y.
  • Passons maintenant aux commandes. +incrémente les variables pertinentes de 1 et les -diminue de 1. Et ... c'est tout.
  • La dernière caractéristique ~ATHest qu'il tue tout ce avec quoi il fonctionne. Les variables sont imprimées au format <name>=<value>(suivi d'une nouvelle ligne) à la commande [<name>].DIE();. Ensuite, le programme imprime le mot DIE <name>et une nouvelle ligne un nombre de fois égal à la valeur absolue de la valeur de la variable. Lorsque des variables sont supprimées simultanément avec [<name1>,<name2>].DIE();(vous pouvez supprimer autant de variables que vous le souhaitez, tant qu'elles existent), la DIE()commande est exécutée de manière séquentielle sur les variables.

Exemples de programmes

Programme 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

Production:

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

Programme 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

Production:

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

C'est du golf de code, donc les règles standard s'appliquent. Le code le plus court en octets gagne.

Arcturus
la source
2
Jusqu'à la mort. Je vois ce que tu as fait là.
Digital Trauma
3
@DigitalTrauma Je dois donner le crédit à Andrew Hussie (le gars qui écrit Homestuck) pour avoir trouvé le nom.
Arcturus
1
@sysreq ~ATHutilise comme virgules fins de ligne pour le import, bifurcateet les DIEcommandes. REPL et les fichiers sont corrects. La sensibilité à la casse est requise à la fois en entrée et en sortie (j'essaie de faire correspondre le ~ATHplus possible le réel ).
Arcturus
1
@sysreq je devais changer quelques choses si la langue serait effectivement faire quelque chose dans la vie réelle, je Pecs sont très bien décrits.
Arcturus
2
Je suis honnêtement surpris que cette question n'ait pas obtenu plus de réponses, et encore plus surpris qu'il n'y ait pas une horde de sorciers Perl armés de baguettes magiques regexy
chat

Réponses:

3

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

D'accord, je ne bat aucun record ici, mais il s'agit du plus petit Python qui ira dans la mesure où la lecture d'une entrée à la fois à partir d'un fichier plutôt que séquentiellement au fil du temps. J'essaierai de mettre cela à niveau plus tard dans une autre langue (et un interprète, pas seulement un analyseur). Jusque-là, profitez de la monstruosité horriblement horrible.

Remarque : ouvre un fichier appelé tdans le répertoire de travail. Pour lui faire ouvrir un argument de ligne de commande, ajoutez import sysen haut du fichier et passez 't'àsys.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
chat
la source
2

Python 2, 447 475 463 443 octets

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

Il s'avère que le zip et l'encodage du programme base64 économise toujours des octets sur la version normale. À titre de comparaison, voici la normale:

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])

Fondamentalement, la solution "baguettes magiques regexy" qui était souhaitée. Lit dans le programme entier depuis stdin comme une seule chaîne, remplace les expressions ~ ATH par des expressions Python qui font la sémantique décrite et exécute () la chaîne résultante.

Pour voir ce qu'il fait, regardez le programme python dans lequel le deuxième programme de test fourni est traduit:

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])                                 

C'est une bonne chose que 00 == 0: P

Évidemment, quelques octets pourraient être économisés en exploitant l'ambiguïté dans les règles. Par exemple, on ne dit pas ce qui devrait arriver si quelqu'un essaie DIE()une variable qui n'a pas été importéditée ou qui a déjà été bifurcated. Ma conjecture basée sur la description était qu'il devrait y avoir une erreur. Si aucune erreur n'est requise, je pourrais supprimer la deldéclaration.

EDIT: correction d'un bug que les cas de test fournis ne testaient pas. À savoir, tel qu'il était, chaque ~ATHbloc remettait la variable à zéro avant de l'incrémenter. Cela m'a coûté 28 octets pour résoudre ce problème. Si quelqu'un voit une meilleure façon de remplacer les ~ATHblocs, j'aimerais le savoir.

EDIT 2: 12 octets enregistrés en déroulant la boucle d'expression régulière, en les transformant en sous-ensembles et en laissant la compression prendre en charge la répétition.

EDIT 3: économisé 20 octets supplémentaires en remplaçant la forboucle interne par une multiplication de chaînes.

quintopie
la source
Hé, enfin les baguettes magiques regexy! Je ne pourrai pas battre ça mais bravo!
chat
Mon implémentation ignore complètement les choses qui ne sont pas explicitement couvertes par les règles, ce qui signifie que vous pouvez ne pas lancer d'erreur et ignorer ces cas également.
chat
vous pourriez économiser quelques octets en faisant import sys,replutôt queimport sys;import re
cat
1
la mise en évidence de la syntaxe le rend beaucoup plus facile à lire
cat
1
@cat désolé d'avoir oublié de vous répondre il y a si longtemps. vous l'exécutez à partir de la ligne de commande et vous dirigez l'entrée depuis un fichier:python ~ath.py < program.~ath
quintopie