Imprimir, Incrementar, Disminuir, Alias ​​- Interpretar Prindeal

30

Prindeal (pronunciado prin-dee-AL ) es una nueva esotérico lenguaje de programación que sólo tiene cuatro comandos: pr int , en Crement , de Crement y cols NIC . A pesar de su minimalismo, se pueden realizar operaciones matemáticas complejas en Prindeal combinando hábilmente los cuatro comandos.

Su tarea en este desafío de código de golf es escribir el programa más corto que pueda ejecutar el código de Prindeal.

La especificación es larga, pero he tratado de hacerlo lo más claro posible y creo que si te esfuerzas por aprender Prindeal, ¡te parecerá bastante elegante!


Intrepreting Prindeal

Preprocesamiento

Antes de poder interpretar un programa de Prindeal, es necesario eliminar estas cosas en este orden:

  1. Todo lo que está después de una #señal al final de la línea está encendido, más el #propio. (Estos son comentarios)
  2. Espacio en blanco al final de cualquier línea.
  3. Líneas completamente vacías.

Por ejemplo, el programa Prindeal

p cat #The next line has 7 trailing spaces.
p dog       

#p mouse

sería preprocesado en

p cat
p dog

De aquí en adelante asumiremos que este paso de preprocesamiento se ha realizado.

Variables

Rápidamente necesitamos definir variables antes de mostrar cómo se usan.

Las variables (y las referencias a las variables) son lo que se pasa a los argumentos de los comandos de Prindeal. Las variables son siempre globales , por lo que las modificaciones a una variable, sin importar dónde ocurran, se reflejan en todas partes.

Cada variable contiene un entero de precisión arbitraria no negativo (0, 1, 2, 3, ...). Las variables no necesitan ser preinicializadas: siempre comienzan con el valor 0 la primera vez que se usan o se solicitan.

Un nombre de variable puede ser cualquier cadena no alfanumérica de caracteres alfanuméricos y subrayados que no comience con un dígito, [a-zA-Z_][0-9a-zA-Z_]*en la expresión regular . Son sensibles a mayúsculas y minúsculas spiny_lumpsuck3ry Spiny_lumpsuck3rson variables diferentes.

Ejecución

Prindeal es un lenguaje de programación imperativo . Cuando se ejecuta un programa Prindeal, sus declaraciones se ejecutan de arriba a abajo en orden y luego el programa finaliza.

Cada línea no sangrada en un programa Prindeal es una declaración que involucra la ejecución de un solo comando que puede o no tomar argumentos.

Las líneas sangradas solo ocurren después de los comandos de alias . Específicamente, se producen exactamente tres líneas con sangría con espacios individuales después de cada comando de alias y se consideran parte de él. Entonces, las declaraciones de alias son realmente cuatro líneas de largo. (Podrían ser una línea, cuatro es simplemente más legible).

No alias Declaraciones

Con la excepción del alias , cada declaración en un programa Prindeal tiene la forma:

[command name] [argument 1] [argument 2] [argument 3] ...

Puede haber un número arbitrario de argumentos (incluido ninguno). Cada argumento es siempre una variable o (como veremos al discutir el alias ) una referencia a una variable .

Una vez finalizada la ejecución, cada instrucción se marca como una falla o éxito, dependiendo de si se encontraron errores o no. (Esto solo es realmente importante cuando usamos el alias ).

La impresión , el incremento y la disminución incorporados son declaraciones con el formulario anterior. Esto es lo que hacen:

  1. print tiene nombre de comando py toma un argumento. Imprime el nombre de la variable pasada y su valor (en decimal) separados por "=", luego una nueva línea. Siempre se marca como un éxito .

    Por ejemplo, el programa Prindeal

    p _MyVariable_321
    p screaming_hairy_armadillo
    

    saldría

    _MyVariable_321 = 0
    screaming_hairy_armadillo = 0
    

    porque todas las variables comienzan en 0. (Se requieren los espacios antes y después del signo igual).

  2. incremento tiene nombre de comando iy toma un argumento. Incrementa el valor de la variable que se pasa en 1. Siempre se marca como un éxito .

    Por ejemplo, el programa

    i alpaca
    p alpaca
    i alpaca
    p alpaca
    

    saldría

    alpaca = 1
    alpaca = 2
    

    Tenga en cuenta cómo alpacase incrementó de 0 a 1 a pesar de que nunca antes se había accedido.

  3. decrement tiene nombre de comando dy toma un argumento. Si la variable que se pasa no es cero, su valor se reduce en 1 y la instrucción se marca como correcta . Si la variable que se pasa es 0, no se hace nada y la declaración se marca como un error .

    Por ejemplo, el programa

    i malamute
    p malamute
    d malamute    #success
    p malamute
    d malamute    #failure
    p malamute
    d akita       #failure
    p akita
    

    saldría

    malamute = 1
    malamute = 0
    malamute = 0
    akita = 0
    

    Tenga en cuenta que la disminución de una variable con valor 0 es la única forma de producir un error .

La declaración de alias y los comandos con alias

El comando alias tiene una sintaxis especial y es el más poderoso porque puede usarse para definir nuevos comandos. El nombre del comando alias es ay una declaración de alias tiene la forma:

a [name of new command]
 [statement A]
 [statement B]
 [statement C]

Donde cada uno [statement X]representa cualquier declaración sin alias , es decir, algo con la forma [command name] [argument 1] [argument 2] [argument 3] ....

El nombre del comando con alias [name of new command]puede ser cualquier cadena no alfanumérica de caracteres alfanuméricos y subrayados que no comience con un dígito, [a-zA-Z_][0-9a-zA-Z_]*en expresiones regulares.

(Este es el mismo conjunto de nombres que las variables, pero los comandos con alias y las variables son cosas diferentes que se usan en diferentes lugares . Una variable podría denominarse igual que un comando sin consecuencias negativas).

Cuando se ejecuta una declaración de alias , se agrega un nuevo comando junto con los cuatro p i d acomandos originales . El nuevo comando se puede usar como [command name]declaraciones in y se puede invocar con argumentos como cualquier otro comando sin alias .

Cuando se ejecuta una declaración con un nombre de comando con alias, se ejecutan exactamente dos declaraciones más de su declaración de alias original :

  • [statement A] siempre se ejecuta
  • [statement B]se ejecuta si [statement A]fue un éxito
  • [statement C]se ejecuta si [statement A]fue un error

Las declaraciones A, B y C siempre se ejecutan perezosamente , es decir, se evalúan sobre la marcha en el momento en que se ejecutan.

Cuando finaliza la ejecución, el comando con alias se marca con el mismo indicador de éxito o error que la instrucción B o C, cualquiera que se haya ejecutado . (las declaraciones de alias no necesitan ser marcadas ya que no pueden ocurrir dentro de ellas mismas).

Alias ​​Ejemplo 1

Digamos que queremos un nuevo comando que incremente la variable frogdos veces. Esta declaración de alias lo logra:

a increment_frog_twice
 i frog
 i frog
 d frog

La instrucción A ( i frog) siempre se ejecuta y siempre se marca como un éxito, por lo que la instrucción B ( i frog) también se ejecuta siempre y, por lo tanto, la variable frog se incrementa en 2. El increment_frog_twicecomando siempre se marca como un éxito porque la instrucción B siempre se ejecuta y B siempre es un el éxito . La instrucción C ( d frog) nunca se ejecuta.

Entonces la salida a

a increment_frog_twice
 i frog
 i frog
 d frog
p frog
increment_frog_twice
p frog

sería

frog = 0
frog = 2

Podemos generalizar este ejemplo para que cualquier variable pueda incrementarse dos veces dando un argumento al comando con alias.

Dentro de una declaración de alias , los enteros positivos 1, 2, 3, etc. representan los argumentos primero, segundo, tercero, etc. pasados ​​al comando con alias. (Estos argumentos pueden ser variables simples o referencias a variables en sí mismas). Estos números solo pueden aparecer dentro de los argumentos de la declaración A, B y C en una declaración de alias . No tiene sentido que aparezcan en otro lugar.

Alias ​​Ejemplo 2

Esto generaliza el último ejemplo: cualquier variable pasada increment_twicese incrementará en 2 porque 1es una referencia al primer argumento pasado:

a increment_twice
 i 1
 i 1
 d 1 #never reached
p toad
increment_twice toad
p toad

El resultado de este programa sería

toad = 0
toad = 2

Podríamos alias otro comando que tome dos argumentos y los invoque increment_twicea ambos:

a increment_twice
 i 1
 i 1
 d 1 #never reached
a increment_both_twice
 increment_twice 1
 increment_twice 2
 d 1 #never reached
increment_both_twice platypus duck
p platypus
p duck

La salida aquí sería

platypus = 2
duck = 2

Es importante darse cuenta de que los comandos con alias pueden ser recursivos, ya que aquí es donde radica su verdadero poder. Por ejemplo, podemos hacer un comando que establezca cualquier variable pasada a 0:

Alias ​​Ejemplo 3

El set_to_zerocomando toma un argumento y establece su variable en 0 y se marca como un éxito cuando se hace:

a set_to_zero
 d 1
 set_to_zero 1
 i _dummy_
i oryx
i oryx
i oryx
p oryx
set_to_zero oryx
p oryx

El resultado de este programa sería

oryx = 3
oryx = 0

Lo que sucede es que cuando set_to_zero oryxse ejecuta, d 1disminuye con éxito oryxde 3 a 2, luego set_to_zero 1se llama, que es lo mismo que llamar de set_to_zero oryxnuevo. Por lo tanto, el proceso se repite hasta que se d 1produce un error , deteniendo la recursividad e incrementando la _dummy_variable para que se produzca un éxito .


Reto

Escriba un programa que pueda ejecutar el código de Prindeal exactamente como se describe anteriormente. Tome el código de Prindeal mediante stdin, la línea de comando o como un archivo de texto. Imprima la salida del programa Prindeal en stdout o la alternativa más cercana a su idioma.

Alternativamente, puede escribir una función que tome el código como una cadena e imprima o devuelva la cadena de salida.

Además, puede suponer que:

  • El código de entrada de Prindeal solo contendrá nuevas líneas y ASCII imprimible y (opcionalmente) que termina con una línea vacía.
  • El código de entrada será válido Prindeal, bien formado y sintácticamente correcto.
  • Ejecutar el código no producirá ningún bucle infinito ni referencias inválidas a comandos que no han sido definidos o argumentos que no han sido dados.
  • Los nombres de los comandos p, i, d, y anunca serán más de alias. (Es posible que no se asuma que las variables no tendrán estos nombres.)

Además, no importa si los valores de sus variables no son enteros de precisión arbitraria, ya que solo se probarán números menores de 1000. También está bien si su idioma tiene límites de recursión (como Python ) que los programas Prindeal más complejos pueden encontrar mientras el programa de prueba a continuación funcione.

Programa de prueba

Aquí hay un gran programa Prindeal que desarrolla las operaciones de suma, multiplicación y exponenciación mediante el uso de variables ficticias (comenzando _por convención) y muchos alias auxiliares:

#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##

#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f

#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C

(Si está jugando con este código, tenga en cuenta que muchos de los comandos fallarán si la misma variable se da varias veces como argumento. Esto se puede solucionar fácilmente pero el código resultante es más largo).

Su intérprete Prindeal debería poder producir la salida exacta:

A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729

Tanteo

El código más corto en bytes gana. Tiebreaker va a la presentación anterior.

Brownie Bonus: escribe un programa genial en Prindeal. Implementé la suma y la multiplicación, ¿puedes hacer resta o división?

Pasatiempos de Calvin
fuente
¡Oh hombre, creo que por una vez dejaré el Pyth solo y sacaré un Lisp! Una pregunta: las funciones y variables viven en espacios de nombres completamente diferentes, ¿verdad? Entonces puedo incrementar p, y luego p p, ¿cuál imprimiría 1, verdad?
orlp
@orlp Correcto. (Hay algunas notas sobre eso allí).
Calvin's Hobbies
2
No puedo ser el único que piensa PRNDL cuando veo el nombre del idioma.
Downgoat
¿Hay un número máximo de argumentos que se pasarán a un comando con alias?
Zach Gates
@ZachGates Nope
Aficiones de Calvin

Respuestas:

9

Pyth, 162 136 bytes

JfTmchcd\#).zKHW<ZlJI!e=T@J~+Z1=@Tk)=k0 .x=J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0,=Y.x@H=eT0?qN\pps[Td\=dYb)?xGN?qN\iXHThY?YXTH_1=k1XKT:JZ=+Z3

Demostración.

Golfed fuera 26 caracteres por inlining variables, y se cambia de Iy Eflujo de control basado a ?y .xflujo de control basado.

Por primera vez, me quedé sin variables en Pyth. Todas las variables en Pyth ( bdkGHNTYy JK) estaban en uso, y quería usarlas bcomo una nueva línea. Afortunadamente, pude usar Npara significar dos cosas completamente diferentes en diferentes partes del programa, y ​​aún así funciona.

Sin golf (ejecutar con -m):

JfTmchcd\#).z
KH
W<ZlJ
  I!e=T@J~+Z1
    =@Tk)
  =k0
     .x
      =J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0
      ,
        =Y.x@H=eT0
        ?qN\p
          ps[Td\=dYb)
          ?xGN
            ?qN\i
              XHThY
              ?Y
                XTH_1
                =k1
            XKT:JZ=+Z3
isaacg
fuente
3
Me encanta como yo todavía no puedo decir lo que hace que incluso con ungolfed ...
Jerry Jeremías
Bueno, eso concluye que Pyth no está completo de Turing ...
Erik the Outgolfer
8

Python 2, 600 584 397 373 bytes

Esta es mi propia solución de referencia de golf. Cualquiera puede mejorarlo o seguir su lógica en su propia respuesta, siempre y cuando se otorgue la atribución.

Lo bueno de esto es que no se hace recursión, por lo que nunca tendrá problemas con el límite de recursión de Python. Por ejemplo, el programa Spup Countup Prindeal puede ejecutarse indefinidamente.

p=filter(len,[l.split('#')[0].split()for l in input().split('\n')]);m={};v={};i=0
while i<len(p):
 s=p[i]
 if'('in`s`:s=s[f]
 n,f=s[0],0
 if n in m:a,b,c=([s[int(g)]if g.isdigit()else g for g in t]for t in m[n]);p=[a,(b,c)]+p[i+1:];i=0;continue
 s=s[1]
 q=v.get(s,0)
 if'd'>n:m[s]=p[i+1:i+4];i+=3
 elif'i'<n:print s,'=',q
 elif'd'<n:v[s]=q+1
 elif q:v[s]-=1
 else:f=1
 i+=1

Es un programa que toma la cadena de programa citada con líneas nuevas escapadas, por ejemplo
'p _MyVariable_321\np screaming_hairy_armadillo'.

Tomé varias pistas de golf de las respuestas de Sp y Pietu . Gracias chicos :)

Pasatiempos de Calvin
fuente
6

Python 3, 345 336 335 328 bytes

a=0
A={}
V={}
def f(l):
 if l[0]in"d p i":c,u=l;U=V[u]=V.get(u,0)+"pi".find(c);S=U<0;V[u]+=S;c<"p"or print(u,"=",U)
 else:d=lambda q:[w.isdigit()and l[int(w)]or w for w in A[l[0]][q]];S=f(d(1+f(d(0))))
 return S
for z in open("P"):
 l=z.split("#")[0].split()
 if"a "==z[:2]:a,s,*x=3,l[1]
 elif l*a:x+=l,;a-=1;A[s]=x
 elif l:f(l)

(-6 bytes gracias a @orlp)

Todavía jugando al golf. Asume que el programa está almacenado en un archivo llamado P.

Poner las llamadas fdentro del lambda dahorraría unos pocos bytes, pero haría que el último caso de prueba alcanzara la profundidad máxima de recursión.

Algunos programas de Prindeal

El programa de sustracción inútil

Aquí hay un programa de resta inútil . Es inútil porque, aunque resta correctamente, no devuelve el éxito / fracaso en consecuencia.

La salida debe ser:

a = 15
b = 6
__________ = 0
a = 9
b = 6

Contar hasta

a helper
 p 1
 countup 1
 i success

a countup
 i 1
 helper 1
 d failure

countup n

Cuenta hacia arriba e imprime npara siempre. Posiblemente podría funcionar como una prueba para la velocidad del intérprete (tenga cuidado con las trazas largas en la interrupción del teclado)

Sp3000
fuente
2
Todos en esta pregunta se han perdido este golf, no entiendo por qué. l[:(l+"#").find("#")]y todas sus variaciones pueden ser reemplazadas por una simple l.split('#')[0].
orlp
@orlp Estaba tan concentrado findque olvidé que podrías splitincluso si #no estuvieras allí. Gracias :)
Sp3000
6

JavaScript (ES6), 273 258

Editó errores solucionados y agregó un conjunto de pruebas real.

Sin contar los espacios iniciales y las nuevas líneas.

Seguramente se puede jugar un poco más al golf.

Demasiado cansado para escribir una explicación ahora, creo que es un buen ejemplo del uso de cierres para mantener vivos los valores temporales (parámetros).

Pruebe a ejecutar el fragmento en cualquier navegador compatible con EcmaScript 6 (en particular, no Chrome ni MSIE. Probé en Firefox, Safari 9 podría funcionar)

F=p=>(
  p=p.match(/^[^#\n]+/gm).filter(r=>r.trim(o='',v=[])),
  s={
    '':_=>1,
    p:a=>o+=a+` = ${v[a]||0}\n`,
    i:a=>v[a]=-~v[a],
    d:a=>v[a]&&v[a]--,
    a:(n,j)=>s[n]=(u,t,a)=>x(p[!x(p[j+1],0,a,1)+j+2],0,a,1)
  },
  p.map(x=(r,i,w,l,a=r.split(/ +/).slice(l).map(x=>-x?w[x]:x))=>s[a[0]](a[1],i,a)),
  o
)

// TEST

$('#O tr').each(function() {
  var $cells = $(this).find('td')
  var prg = $cells.eq(0).text()
  console.log(prg)
  var output = F(prg)
  $cells.eq(1).text(output)
})
#O td { vertical-align:top; white-space: pre; border: 1px solid #888; font-family:monospace }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr><th>Program</th><th>Outpout</th></tr>
<tbody id=O>  
<tr><td>p _MyVariable_321
p screaming_hairy_armadillo</td><td></td></tr>
<tr><td>i alpaca
p alpaca
i alpaca
p alpaca</td><td></td></tr>
<tr><td>i malamute
p malamute
d malamute    #success
p malamute
d malamute    #failure
p malamute
d akita       #failure
p akita</td><td></td></tr>
<tr><td>a increment_frog_twice
 i frog
 i frog
 d frog
p frog
increment_frog_twice
p frog</td><td></td></tr>
<tr><td>a increment_twice
 i 1
 i 1
 d 1 #never reached
a increment_both_twice
 increment_twice 1
 increment_twice 2
 d 1 #never reached
increment_both_twice platypus duck
p platypus
p duck</td><td></td></tr>
<tr><td>a set_to_zero
 d 1
 set_to_zero 1
 i _dummy_
i oryx
i oryx
i oryx
p oryx
set_to_zero oryx
p oryx</td><td></td></tr>
<tr><td>#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##

#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f

#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C  
</td><td></td></tr>
</tbody>
</table>

edc65
fuente
Agregué algunos comentarios más al programa de prueba y parece que hicieron que su código no funcionara.
Aficiones de Calvin
@ Calvin'sHobbies un primer parche rápido
edc65
3

C # 6, 653 bytes

Aquí está mi entrada, en medio de un mar de Python ...

class P{string[]l;string r="";Dictionary<string,int>v=new Dictionary<string,int>();Dictionary<string,int>s=new Dictionary<string,int>();public string R(string t){l=t.Split('\n');for(int i=0;i<l.Length;i++){var z=l[i].Split(' ');if(z[0]=="a"){s.Add(z[1],i);i+=3;}else E(i, null);}return r;}bool E(int n,string[]p){var z=l[n].Split(' ');var a=z.Skip(1).Select(x=>Char.IsDigit(x[0])?p[int.Parse(x)-1]:x).ToArray();if(a.Length>0&&!v.ContainsKey(a[0]))v[a[0]]=0;if (z[0]=="p")r+=$"{a[0]} = {v[a[0]]}\n";else if(z[0]=="i")v[a[0]]++;else if(z[0]=="d")if(v[a[0]]>0)v[a[0]]--;else return false;else{var y=s[z[0]];return E(y+1,a)?E(y+2,a):E(y+3,a);}return true;}}

Ampliado y comentado:

class Prindeal
{
    string[] lines;
    string result = "";
    Dictionary<string, int> variables = new Dictionary<string, int>();
    Dictionary<string, int> statements = new Dictionary<string, int>();

    public string Run(string text)
    {
        lines = text.Split('\n');

        for (int i = 0; i < lines.Length; i++)
        {
            // Split on spaces to get the statement and any arguments
            var z = lines[i].Split(' ');

            // Are we defining a new statement?
            if (z[0] == "a")
            {
                // Add to the statements dictionary, step over definition statements
                statements.Add(z[1], i);
                i += 3;
            }
            else
            {
                // Execute the statement
                Execute(i, null);
            }
        }

        return result;
    }

    bool Execute(int lineNumber, string[] parameters)
    {
        // Split on spaces to get the statement and any arguments
        var z = lines[lineNumber].Split(' ');

        // Parse the arguments - if it's a number, get the corresponding 
        // parameter from the calling statement
        var arguments = z.Skip(1).Select(
            x => Char.IsDigit(x[0]) ? 
            parameters[int.Parse(x) - 1] : 
            x)
            .ToArray();

        // If the first argument isn't already in the variables dict, add it
        if (arguments.Length > 0 && !variables.ContainsKey(arguments[0])) variables[arguments[0]] = 0;

        // Print statement, using string interpolation
        if (z[0] == "p")
            result += $"{arguments[0]} = {variables[arguments[0]]}\n";
        // Increment statement
        else if (z[0] == "i")
            variables[arguments[0]]++;
        // Decrement statement
        else if (z[0] == "d")
            if (variables[arguments[0]] > 0)
                variables[arguments[0]]--;
            else
                return false;
        else
        {
            // Get the line number to jump to
            var y = statements[z[0]];

            // Execute A ? B : C
            return Execute(y + 1, arguments) ? Execute(y + 2, arguments) : Execute(y + 3, arguments);
        }

        // If we reach this point, it's from a 'p', 'i' or 'd' statement which has succeeded
        return true;
    }
}

Para usarlo, simplemente crea una instancia de la clase y llama al R()método, por ejemplo:

string prindealText = new StreamReader("prindeal.txt").ReadToEnd();
Console.WriteLine(new P().R(prindealText));
Sok
fuente
3

Lisp común, 758 646 619

(progn(set-macro-character #\#(get-macro-character #\;))(setf(readtable-case *readtable*):invert)(#3=defun v(s)(if(boundp s)(eval s)0))(#3# i(s)(set s(1+ (v s))))(#3# d(s)(and(plusp(v s))(set s(1-(v s)))))(#3# p(s)(format t"~A = ~A~%"s(v s)))(defmacro a(n . p)`(#3#,(cadr n)(&rest g)(if,@p)))(#3# k(s)(typecase s(integer`(nth,(1- s)g))(symbol `',s)(t(list*(car s)(mapcar 'k(cdr s))))))(#3# r()(prog(l p q)$(setf p()l(make-string-input-stream(or(read-line()()())(return))))@(when(setf p(read l()()))(push p q)(go @))(if q(return(k(reverse q)))(go $))))(do ((x(r)(r)))((not x))(eval(if(eq(car x)'a)`(,@x,(r),(r),(r))x))))

Pon esto file.lispy llama por ejemplo sbcl --script file.lisp; la entrada se lee de la secuencia de entrada estándar.

Esta versión analiza un superconjunto de Prindeal: sin muchas dificultades, puede acceder a Common Lisp desde una fuente de Prindeal. Considero que esto es una característica del intérprete.

Versión comentada

;; copy-readtable is only used during development, so that I do not 
;; mess with my running environment. The real code starts with the
;; progn below, which is superfluous of course inside a let.
(let ((*readtable* (copy-readtable)))

  ;; I use PROGN in the golfed version so that I can have the whole
  ;; program as a unique tree. This allows me to define reader 
  ;; variables like #3=defun in order to gain a few bytes by writing
  ;; #3# instead of defun. Reader variables are removed in
  ;; this human-friendly version.
  (progn
    ;; Let # point to the same reader function as ;
    ;; Of course, ; is still usable as a comment delimiter
    (set-macro-character #\#
                         (get-macro-character #\;))

    ;; :invert does what is necessary to enable case-sensitive reading
    ;; and printing of symbols
    (setf (readtable-case *readtable*) :invert)

    ;; value of symbol, or zero
    (defun v(s)(if(boundp s)(eval s)0))

    ;; increment
    (defun i(s)(set s(1+ (v s))))

    ;; decrement
    (defun d(s)(and(plusp(v s))(set s(1-(v s)))))

    ;; print
    (defun p(s)(format t"~A = ~A~%"s(v s)))

    ;; alias: wrap an "if" inside a "defun".
    ;; YES, that means you can redefine ANY lisp function with "a" !
    ;; A safer version would properly intern symbols in a dedicated package.
    ;;
    ;; Notice the G variable.  We take advantage of the "unhygienic"
    ;; (what a bad adjective) nature of macros to create a context
    ;; where G is bound to the argument list. The same G is referenced
    ;; implicitely later.
    (defmacro a(n . p)`(defun,(cadr n)(&rest g)(if,@p)))

    ;; Canonicalize expressions:
    ;;
    ;; - if s is a symbol, return s quoted. All functions manipulate
    ;; symbols in order to allow the undeclared use of variables. With
    ;; symbols, we can check for boundness.
    ;;
    ;; - if s is an integer, then we are inside an alias definition. The
    ;; integer is replaced by an access to the s'th element of the
    ;; implicit argument list G using (nth (1- s) g). G will be bound
    ;; when the expressions is injected in the defun corresponding to
    ;; the alias, or else an error will be signaled: either because G
    ;; is unbound, or because you defined a variable named G which is
    ;; by construction not a list. Since we do not sanitize properly
    ;; the input, you could bind G globally to a list, but that would be
    ;; nasty.
    ;; 
    ;; - Finally, if s is a list, apply k to all but the first
    ;; elements of s.  The first element is a symbol but we do not
    ;; need to quote it because we want to call the function
    ;; associated with the symbol. Due to the Lisp-2-ness
    ;; of Common Lisp, functions and variables can coexist
    ;; with the same name.
    ;;
    (defun k(s)(typecase s
                 (integer`(nth,(1- s)g))
                 (symbol`',s)
                 (t(list*(car s)(mapcar #'k(cdr s))))))

    ;; Reader function
    (defun r()
      (prog (l ; current line, as an input-stream reading a string
             p ; current read form
             q ; whole line and return value, as a list
             )

         ;; PROG includes an implicit TAGBODY. Below, $ and @ are
         ;; labels for GO statements (gotos).

       $ (setf
          ;; emtpy p
          p ()

          ;; Read a whole line and if we do not fail, build an input
          ;; stream to read from it.
          l (make-string-input-stream
             (or (read-line()()()) ;; try to read a line,
                 (return)          ;; but return from prog if we reach
                                   ;; the end of file.
                 )))
       @ (when (setf p (read l()()))
           ;; Read a lisp expression, put it in p and if p is not nil
           ;; push it into q.  A nil could happen at the end of the
           ;; line or if someone (you know who) inserted an empty list
           ;; in the file being read.
           ;; 
           ;; Thanks to the readtable which now handles comments
           ;; and spaces for us, nothing needs to be done here to
           ;; preprocess the input.

           (push p q) (go @))

         ;; If we read an empty line, q can be nil. In this case, go
         ;; back to $ and read another line. If q is not nil, reverse
         ;; it (we pushed, remember), canonicalize it and return the
         ;; result.
         (if q (return(k(reverse q))) (go $)))
      )

    ;; Read/eval loop.  When reading "(a name)", we read the three
    ;; next lines and append them to the first so that it builds a
    ;; call the the alias definition macro a. Otherwise, just eval x.
    (do((x(r)(r))((not x))
      (eval (if (eq(car x'a))
                `(,@x,(r),(r),(r))
                x)))))

Ejemplo

~$ sbcl --script file.lisp < testfile

A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729

Si reemplazamos evalpor printen el ciclo de lectura / evaluación, entonces podemos ver lo que se está evaluando:

(a 's (i '_) (d '_) (d '_)) 
(a 'f (d '_) (d '_) (d '_)) 
(a 'z (d (nth 0 g)) (z (nth 0 g)) (s)) 
(a 'n (z (nth 0 g)) (i (nth 0 g)) (s)) 
(a 'move (moveH (nth 0 g) (nth 1 g)) (move (nth 0 g) (nth 1 g)) (s)) 
(a 'moveH (d (nth 0 g)) (i (nth 1 g)) (f)) 
(a 'dupe (dupeH1 (nth 0 g) (nth 1 g) (nth 2 g))
   (dupe (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'dupeH1 (d (nth 0 g)) (dupeH2 (nth 1 g) (nth 2 g)) (f)) 
(a 'dupeH2 (i (nth 0 g)) (i (nth 1 g)) (s)) 
(a 'copy (z (nth 1 g)) (copyH (nth 0 g) (nth 1 g)) (s)) 
(a 'copyH (dupe (nth 0 g) (nth 1 g) '_copy) (move '_copy (nth 0 g)) (s)) 
(a 'addTo (copy (nth 1 g) '_add) (move '_add (nth 0 g)) (s)) 
(a 'add (z (nth 0 g)) (addH (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'addH (addTo (nth 0 g) (nth 1 g)) (addTo (nth 0 g) (nth 2 g)) (s)) 
(a 'mul (mulH1 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 2 g)) (s)) 
(a 'mulH1 (z (nth 0 g)) (copy (nth 1 g) '_mul) (s)) 
(a 'mulH2 (mulH3 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'mulH3 (d '_mul) (addTo (nth 0 g) (nth 1 g)) (f)) 
(a 'mulBy (mul '_mulBy (nth 0 g) (nth 1 g)) (copy '_mulBy (nth 0 g)) (s)) 
(a 'pow (powH1 (nth 0 g) (nth 2 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH1 (n (nth 0 g)) (copy (nth 1 g) '_pow) (s)) 
(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH3 (d '_pow) (mulBy (nth 0 g) (nth 1 g)) (f)) 
(p 'A) 
(p 'B) 
(p 'C) 
(n 'A) 
(n 'B) 
(add 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(add 'B 'A 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(mul 'd 'B 'C) 
(p '____) 
(p 'd) 
(mulBy 'd 'B) 
(p '____) 
(p 'd) 
(d 'A) 
(mulBy 'd 'A) 
(p '____) 
(p 'd) 
(pow 'A 'C 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'A 'B 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C)

Macroexpansion

Si elegimos la siguiente definición de alias:

(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s))

... podemos ver referencias a una variable llamada gque no se encuentra en ninguna parte del ámbito léxico. Pero después de la macroexpansión, aquí está el código real que se está evaluando:

(defun powH2 (&rest g)
  (if (powH3 (nth 0 g) (nth 1 g))
      (powH2 (nth 0 g) (nth 1 g))
      (s))) 

Ahora, se grefiere a la lista de argumentos de la función que se está definiendo.

volcado de memoria
fuente
2

Python 2, 486 bytes

Esta es la solución de referencia que jugué más (actualmente -98 bytes).

import sys;sys.setrecursionlimit(2000)
def r(s):
 n=s[0]
 if n in A:f=lambda i:r([s[int(t)]if'0'<t[0]<':'else t for t in A[n][i]]);return f(1+(f(0)or 0))
 k=s[1]
 if'i'<n:print k,'=',V.get(k,0)
 elif'd'<n:V[k]=-~V[k]if k in V else 1
 elif'a'<n:
    if~-(k in V)or V[k]<1:return 1
    V[k]-=1
 else:A[k]=s[2:]
A={};V={};c=filter(bool,([l,l[:l.find('#')]]['#'in l]for l in input().split('\n')))
while c:
 s=c[0].split();c=c[1:]
 if'a'!=s[0]:r(s)
 else:r(['a',s[1]]+map(str.split,c[:3]));c=c[3:]

Cambios (que recuerdo):

  • conversión automática de entero booleano ( [l,l[:l.find('#')]]['#'in l]).
  • establecer o incrementar en una declaración ( V[k]=-~V[k]if k in V else 1)
  • más alias a expresiones más largas ( k=s[1])
  • sin contador en el bucle principal, borrando la lista de entrada en su lugar
  • printAgregar espacios automáticamente ( print k,'=',V.get(k,0))
  • dígitos de control 1-9 ( '0'<t[0]<':')
  • voltear los valores de retorno de ralrededor para guardar returns
  • eliminar la repetición de rebanar y partir ( map(str.split,c[:3])))
PurkkaKoodari
fuente
1

Python 3, 1322 bytes

Golfizado:

import re,sys;sys.setrecursionlimit(2000);F,L=filter,list
class P:
 N,O,F=0,{},{}
 def __init__(S,c):
  S.B,S.E={"p":S.P,"i":S.I,"d":S.D,"a":S.L},dict(enumerate(F(None,[i.split('#')[0].rstrip()for i in c.splitlines()])))
  while S.N in S.E:S.X(S.E[S.N])
 def V(S, v, y, z=0):
  if re.match("[\w_][\d\w_]*",v):
   if not v in y:
    if z is not None:y[v]=z
    else:return False
   return True
  return False
 def A(S):S.N+=1
 def P(S,v):
  if S.V(v,S.O):print("{0} = {1}".format(v, S.O[v]));return True
  return False
 def I(S,v):
  if S.V(v, S.O):S.O[v]+=1;return True
  return False
 def D(S,v):
  if S.V(v,S.O)and S.O[v]>0:S.O[v]-=1;return True
  return False
 def L(S,v):
  e=[]
  if S.V(v,S.F,e):
   for i in range(3):S.A();e.append(S.E[S.N].lstrip())
   return True
  return False
 def C(S,c,v):
  def R(Z,v):
   for i in re.findall("\s(\d+)", Z):Z=Z.replace(" %s"%i," %s"%v[int(i)-1])
   return Z
  Q,m,f=map(lambda l:R(l,v),S.F[c])
  if S.X(Q,False):return S.X(m,False)
  return S.X(f,False)
 def X(S,Z,C=True):
  u=re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?",Z)
  if u:
   c,v=map(lambda i:''if i is None else i,u.groups());v=L(F(None,v.split(' ')))
   if S.V(c,S.F,None):
    T=S.C(c, v)
    if C:S.A()
   elif S.V(c,S.B,None):
    T=S.B[c](*v)
    if C:S.A()
   else:return False
   return T
  return False

Sin golf:

import re

class Prindeal:
    iline = 0
    local = {}
    udef = {}
    content  = {}

    def __init__(self, c):
        self.built = {
            "p": self.print,
            "i": self.increment,
            "d": self.decrement,
            "a": self.alias,
        }
        self.content = dict(enumerate(filter(None, [i.split('#')[0].rstrip()for i in c.splitlines()])))
        while self.iline in self.content:
            self.execute_line(self.content[self.iline])

    def validate_name(self, varname, stack, default=0):
        if re.match("[\w_][\d\w_]*", varname):
            if not varname in stack:
                if default is not None:
                    stack[varname] = default
                else:
                    return False
            return True
        return False

    def advance_stack(self):
        self.iline += 1

    def print(self, varname):
        if self.validate_name(varname, self.local):
            print("{0} = {1}".format(varname, self.local[varname]))
            return True
        return False

    def increment(self, varname):
        if self.validate_name(varname, self.local):
            self.local[varname] += 1
            return True
        return False

    def decrement(self, varname):
        if self.validate_name(varname, self.local) and self.local[varname] > 0:
            self.local[varname] -= 1
            return True
        return False

    def alias(self, aliasname):
        indexed_lines = []
        if self.validate_name(aliasname, self.udef, indexed_lines):
            for i in range(3):
                self.advance_stack()
                indexed_lines.append(self.content[self.iline].lstrip())
            return True
        return False

    def execute_alias(self, cmd, variables):
        def parse_args(line, variables):
            for i in re.findall("\s(\d+)", line):
                line = line.replace(" %s" % i, " %s" % variables[int(i) - 1])
            return line
        init, success, failure = map(lambda l: parse_args(l, variables), self.udef[cmd])
        if self.execute_line(init, False):
            return self.execute_line(success, False)
        return self.execute_line(failure, False)

    def execute_line(self, line, cont=True):
        valid_execution = re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?", line)
        if valid_execution:
            cmd, variables = map(lambda i: '' if i is None else i, valid_execution.groups())
            variables = list(filter(None, variables.split(' ')))
            if self.validate_name(cmd, self.udef, None):
                temp = self.execute_alias(cmd, variables)
                if cont:
                    self.advance_stack()
            elif self.validate_name(cmd, self.built, None):
                temp = self.built[cmd](*variables)
                if cont:
                    self.advance_stack()
            else:
                return False
            return temp
        return False

Uso:

P(c)

¿Dónde cestá el contenido del texto?

Ejemplos:

Se aceptan cadenas de una línea:

  • P("p cat")
  • P("p dog\ni dog\np dog")

También se aceptan cuerdas con líneas múltiples:

P("""
p dog
i dog
p dog
""")

O:

P("""p dog
i dog
p dog""")

Etc.

Notas:

Esto funciona correctamente para todos los casos de prueba, pero alcanza el límite de recurrencia en:

pow C A B   #C = A ^ B = 9 ^ 3 = 729

De ahí el sys.setrecursionlimit(2000).

Puertas de Zach
fuente
1
Utilizará algunos bytes, pero ¿no podría usar sys.setrecursionlimit () para que esto funcione correctamente con el alias de pow?
Corwin
Podría, pero OP declaró que los lenguajes como Python (que tienen límites de recursión) se aceptan tal cual. Sin embargo, agregaré la solución si OP me lo solicita. @Corwin
Zach Gates
Lo suficientemente justo. Perdí eso en la especificación. @ZachGates
Corwin
1

Python - 695 688 bytes

def p(v):print v,"=",w.get(v,0)
def i(v):w[v]=w.get(v,0)+1
def d(v):
 if v in w:
<TAB>w[v]-=1
<TAB>if not w[v]:del w[v]
 else:return 1
def a(n,b,d,h):
 def g(*a):
<TAB>i=1;f=b;s=d;t=h
<TAB>for v in a:v=q+v+q;k=q+j(i)+q;f=c(f,k,v);s=c(s,k,v);t=c(t,k,v);i+=1
<TAB>y=u(t,e)if u(f,e)else u(s,e);i=1;return y
 e[n]=g
q="'";w=x={};u=eval;e={'a':a,'d':d,'i':i,'p':p};import sys;l=sys.stdin.readlines();r="";j=str;c=j.replace;sys.setrecursionlimit(2000)
for h in l:
 h = h.strip()
 if not h:continue
 l = h.split();f=l[0];n=f+"("
 if "#" in f:continue
 for g in l[1:]:
<TAB>b=g.find("#")+1
<TAB>if b:g=g[:b-1]
<TAB>if g:n+="'%s',"%g
<TAB>if b:break
 if x:x-=1;d+='"%s)",'%n
 else:x=(f=="a")*3;d=n
 if not x:d+=")\n";r+=d
exec r in e

<TAB> es un carácter de tabulación literal.

pppery
fuente
1

C ++, 1111 bytes

Este es C ++, tan idiomático como podría hacerlo.
Eso significa hacerlo más C ++ - ish y menos C-ish.
Eso también significa que es más grande que el programa C equivalente.
Creo que C ++ rivaliza con Java para la biblioteca estándar detallada.
Se compila con VS2013 y g ++ 4.9.2 (con -std = c ++ 11)

#include<array>
#include<iostream>
#include<map>
#include<regex>
#include<sstream>
#include<stack>
#define B std::
#define a first
#define b second
#define c(s);else if(x.a==s)
#define d(n)B getline(B cin,r##n)
#define e(n)r##n=B regex_replace(r##n,q,"$1");
#define f(n)do{d(n);e(n)}while(r##n.empty());
#define g B string
#define h B istream_iterator<g>
#define i p.top().a
#define j p.empty()
#define k B pair
#define u continue;
#define w B back_inserter
typedef B vector<g>s;typedef B array<g,3>A;typedef k<k<long,A>,s>t;B map<g,A>m;B map<g,long>n;B stack<t>p;B regex q("^ *(.*?) *(#.*)?$");int main(){g r0,r1,r2,r3;while(d(0)){e(0)if(r0.empty())u p.push(t{{0,{{r0,"",""}}},{}});bool z;while(!j){k<g,s>x;B istringstream ss(i.b[i.a]);ss>>x.a;B copy(h(ss),h(),w(x.b));s o;B transform(B begin(x.b),B end(x.b),w(o),[](g y){int v=atoi(y.c_str());return v>0?p.top().b[v-1]:y;});z=true;if(0)c("")c("p")B cout<<o[0]<<" = "<<n[o[0]]<<B endl c("i")n[o[0]]++c("d")n[o[0]]-=(z=n[o[0]])c("a"){f(1)f(2)f(3)m.insert(B make_pair(o[0],A{{r1,r2,r3}}));}else{p.push(t{{0,m[x.a]},o});u}while(!j&&i.a)p.pop();if(!j)i.a+=1+!z;}}}

Debajo está el original. Si alguien puede pensar en una forma de hacerlo más idiomático y más corto al mismo tiempo, hágamelo saber.

#include <array>
#include <iostream>
#include <map>
#include <regex>
#include <sstream>
#include <stack>

typedef std::vector<std::string> List;
typedef std::pair<std::string, List> Statement;
typedef std::array<std::string, 3> Alias;
typedef std::pair<long, Alias> IndexedAlias;
typedef std::pair<IndexedAlias, List> Item;

std::map<std::string, Alias> aliases;
std::map<std::string, long> variables;
std::stack<Item> stack;
std::regex re("^ *(.*?) *(#.*)?$");

int main()
{
    std::string line, line1, line2, line3;
    while (std::getline(std::cin, line)) // control-Z to exit
    {
        line = std::regex_replace(line, re, "$1");
        if (line.empty()) continue;
        stack.push(Item{ { 0, { { line, "", "" } } }, {} });

        bool flag;
        while (!stack.empty())
        {
            Statement statement;
            std::istringstream ss(stack.top().first.second[stack.top().first.first]);
            ss >> statement.first;
            std::copy(std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>(), std::back_inserter(statement.second));

            List arguments;
            std::transform(std::begin(statement.second), std::end(statement.second), std::back_inserter(arguments),
                [](std::string arg){ int i = atoi(arg.c_str()); return i > 0 ? stack.top().second[i - 1] : arg; });

            flag = true;
            if (statement.first == "")
                ;
            else if (statement.first == "p")
                std::cout << arguments[0] << " = " << variables[arguments[0]] << std::endl;
            else if (statement.first == "i")
                variables[arguments[0]]++;
            else if (statement.first == "d")
                variables[arguments[0]] -= (flag = variables[arguments[0]]);
            else if (statement.first == "a")
            {
                do { std::getline(std::cin, line1); line1 = std::regex_replace(line1, re, "$1"); } while (line1.empty());
                do { std::getline(std::cin, line2); line2 = std::regex_replace(line2, re, "$1"); } while (line2.empty());
                do { std::getline(std::cin, line3); line3 = std::regex_replace(line3, re, "$1"); } while (line3.empty());
                aliases.insert(std::make_pair(arguments[0], Alias{ { line1, line2, line3 } }));
            }
            else
            {
                stack.push(Item{ { 0, aliases[statement.first] }, arguments });
                continue;
            }

            while (!stack.empty() && stack.top().first.first) stack.pop();
            if (!stack.empty()) stack.top().first.first += 1 + !flag;
        }
    }

    std::cout << "-- Variables --" << std::endl;
    std::transform(std::begin(variables), std::end(variables), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, long>::value_type pair){ std::ostringstream ss; ss << pair.first << " = " << pair.second; return ss.str(); });
    std::cout << "-- Aliases --" << std::endl;
    std::transform(std::begin(aliases), std::end(aliases), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, Alias>::value_type pair){ std::ostringstream ss; ss << pair.first << " = [1]:" << pair.second[0] << " [2]:" << pair.second[1] << " [3]:" << pair.second[1]; return ss.str(); });
    std::cout << "---------------" << std::endl;

    return 0;
}
Jerry Jeremiah
fuente
0

Haskell, 1009

Hice mi mejor esfuerzo para jugar golf; mi código sin golf consistía en más de 3,000 caracteres. En este punto no puedo recordar qué hacen todas las funciones, por lo que jugar al golf significa adivinar qué lo romperá y qué no.

import qualified Data.Map as M
import Control.Monad.State.Lazy
import Data.List
type A=M.Map String
data P=P(A Int)(A([String]->StateT P IO Int))
a f=evalStateT f(P M.empty$M.fromList[("i",\(b:_)->(+1)%b),("d",\(b:_)->pred%b),("p",\(b:_)->i b>>= \v->liftIO(putStrLn$b++"="++show v)>>q 1)])
e(k:l)=do{(P v a)<-get;put.P v$M.insert k(m l)a;q 1}
g t s f= \a->t a>>= \b->if b>0then s a else f a
f%k=f<$>i k>>= \v->if v<0then k#0>>q 0else k#v>>q 1
i k=get>>= \(P v _)->q$M.findWithDefault 0 k v
k#v=get>>= \(P b a)->put$P(M.insert k v b)a
l k=get>>= \(P _ a)->q$a M.!k
f s=let(f:a)=r s in($a)<$>l f>>=id
m(t:s:f:_)=g(k t)(k s)(k f)
k s=let(f:b)=r s in\a->($(map((\y z->if all(\c->c>'/'&&c<':')z then y!!(read z-1)else z)a)b))<$>l f>>=id
n=dropWhileEnd(==' ').takeWhile(not.(=='#')).dropWhile(==' ')
o[]=[]
o(l:ls)|(head.r$l)=="a"=(l:take 3 ls):(o$drop 3 ls)|1>0=[l]:o ls
p s|length s>1=e$(n.tail.head$s):tail s|1>0=f.head$s
q=return
main=join$a.(\s->mapM_ p(o.filter(not.null).map n.lines$s))<$>getContents
r=words
ankh-morpork
fuente