Escreva um intérprete ~ ATH

12

O popular webestic Homestuck utiliza uma linguagem de programação chamada ~ATHpara destruir universos. Embora esse desafio do código de golfe não seja escrever um programa para aniquilar nossa existência, estaremos destruindo algumas entidades mais dóceis (embora menos interessantes): variáveis

~ATH(pronunciado "até a morte", observe como o ~ath"til ath") funciona criando uma variável chamada THIS, executando um comando EXECUTEe finalizando o programa THIS.DIE(). Uma página wiki para o uso do idioma no Homestuck pode ser encontrada aqui . O objetivo deste desafio será criar um ~ATHintérprete.

Para o bem do desafio, vou criar alguns detalhes ~ATHque realmente não existem, mas torná-lo (um pouco) útil.

  • O idioma funcionará apenas com números inteiros, que são declarados com import <variable name>;. A variável será automaticamente definida como um valor 0. Somente uma variável por vez pode ser importada.
  • Uma variável xpode ser copiada escrevendo bifurcate x[y,z];, que excluirá a variável xe a substituirá por variáveis ​​idênticas ye z. Observe que ela não pode criar uma variável com o mesmo nome que a excluída. Essencialmente, uma variável é renomeada e, em seguida, uma cópia da variável com um nome diferente é criada. Esta parece ser uma característica estúpida, mas a estupidez é muito profundamente enraizada na Homestuck.
  • A sintaxe para escrever um programa que executa o código xé ~ATH(x){EXECUTE(<code>)}. Se você quiser executar o código de duas variáveis simultaneamente, o código fica aninhada, como este: ~ATH(x){~ATH(y){EXECUTE(<code>)}}. Todos os comandos <code>serão executados em ambos xe y.
  • Agora vamos para os comandos. +incrementa a (s) variável (s) relevante (s) em 1 e a -diminui em 1. E ... é isso.
  • O recurso final ~ATHé que ele mata o que quer que funcione. As variáveis ​​são impressas no formato <name>=<value>(seguido por uma nova linha) no comando [<name>].DIE();. Posteriormente, o programa imprime a palavra DIE <name>e uma nova linha várias vezes igual ao valor absoluto do valor da variável. Quando variáveis ​​são eliminadas simultaneamente com [<name1>,<name2>].DIE();(você pode ter quantas variáveis ​​quiser, contanto que elas existam), o DIE()comando é executado nas variáveis ​​sequencialmente.

Programas de exemplo

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

Resultado:

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

Resultado:

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

Isso é código de golfe, portanto, regras padrão se aplicam. O código mais curto em bytes vence.

Arcturus
fonte
2
Até a morte. Eu vi o que você fez lá.
Digital Trauma
3
@DigitalTrauma Eu tenho que passar o crédito para Andrew Hussie (o cara que escreve Homestuck) por ter inventado o nome.
Arcturus
1
@sysreq ~ATHusa ponto e vírgula como as quebras de linha para o import, bifurcatee DIEcomandos. O REPL e os arquivos estão bem. A distinção entre maiúsculas e minúsculas é necessária na entrada e na saída (estou tentando corresponder ao ~ATHmáximo possível).
Arcturus
1
@ sysreq Eu tive que mudar algumas coisas para que o idioma realmente fizesse algo na vida real, os pecs que descrevi estão bem.
quer
2
Sinceramente estou surpreendido esta questão não tenha chegado mais respostas, e ainda mais surpreso que não há horda de Perl assistentes armados com bastões regexy de mágica
cat

Respostas:

3

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

Tudo bem, eu não estou quebrando nenhum registro aqui, mas esse é o menor Python, pois lemos a entrada de uma só vez de um arquivo e não sequencialmente ao longo do tempo. Tentarei resolver isso posteriormente em outro idioma (e um intérprete, não apenas um analisador). Até então, aproveite a monstruosidade repugnantemente horrível.

Nota : abre um arquivo chamado tno diretório de trabalho. Para abrir um argumento de linha de comando, adicione import sysna parte superior do arquivo e mude 't'parasys.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
fonte
2

Python 2, 447 475 463 443 bytes

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

Acontece que compactar e codificar o programa base64 ainda salva bytes sobre a versão normal. Para comparação, aqui está o 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])

Basicamente, a solução "regexy wands of magic" que era desejada. Lê em stdin o programa inteiro como uma única sequência, substitui ~ expressões ATH por expressões Python que executam a semântica descrita e exec () é a sequência resultante.

Para ver o que está fazendo, observe o programa python ao qual o segundo programa de teste fornecido é traduzido:

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

É bom que 00 == 0: P

Obviamente, alguns bytes podem ser salvos explorando a ambiguidade nas regras. Por exemplo, não é dito o que deve acontecer no caso de alguém tentar DIE()uma variável que não foi importeditada ou que já foi bifurcated. Meu palpite, com base na descrição, era que deveria haver um erro. Se nenhum erro for necessário, eu poderia remover a delinstrução.

EDIT: Corrigido um bug que os casos de teste fornecidos não testavam. Ou seja, do jeito que era, todo ~ATHbloco redefinia a variável para zero antes de incrementá-la. Custou-me 28 bytes para corrigir isso. Se alguém vê uma maneira melhor de substituir ~ATHblocos, eu adoraria saber.

EDIT 2: Salva 12 bytes desenrolando o loop regex, tornando-os todos subns e deixando a compactação cuidar da repetição.

EDIT 3: economizou mais 20 bytes substituindo o forloop interno por uma multiplicação de strings.

quintopia
fonte
Ei, finalmente as varinhas regexy da mágica! Não vou conseguir superar isso, mas bem feito!
gato
Minha implementação ignora completamente as coisas não explicitamente cobertas pelas regras, o que significa que não há problema em você não gerar um erro e apenas ignorar esses casos.
gato
você pode salvar alguns bytes fazendo em import sys,revez deimport sys;import re
cat
1
o realce da sintaxe facilita muito a leitura
cat
1
@cat desculpe, eu esqueci de responder a você há tanto tempo. você o executa na linha de comando e canaliza a entrada em um arquivo:python ~ath.py < program.~ath
quintopia