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

30

Prindeal (pronunciado prin-dee-al ) é uma nova esotérica linguagem de programação que só tem quatro comandos: pr int , em Crement , de Crement , e ao IAS . Apesar de seu minimalismo, operações matemáticas complexas podem ser feitas no Prindeal combinando inteligentemente os quatro comandos.

Sua tarefa neste desafio de código de golfe é escrever o programa mais curto que pode executar o código do Prindeal.

A especificação é longa, mas tentei deixar o mais claro possível e acredito que se você se esforçar para aprender o Prindeal, verá que é bastante elegante!


Intrepreting Prindeal

Pré-processando

Antes que um programa Prindeal possa ser interpretado, é necessário removê-lo da seguinte maneira:

  1. Qualquer coisa após um #sinal até o final da linha está ligado, mais o #próprio. (Estes são comentários.)
  2. Espaço em branco à direita em qualquer linha.
  3. Linhas completamente vazias.

Por exemplo, o programa Prindeal

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

#p mouse

seria pré-processado em

p cat
p dog

A partir daqui, assumiremos que esta etapa de pré-processamento foi concluída.

Variáveis

Rapidamente precisamos definir variáveis ​​antes de mostrar como elas são usadas.

Variáveis ​​(e referências a variáveis) são o que são passados ​​para os argumentos dos comandos do Prindeal. As variáveis ​​são sempre globais ; portanto, as modificações em uma variável, não importa onde elas ocorram, são refletidas em todos os lugares.

Cada variável possui um número inteiro de precisão arbitrária não negativo (0, 1, 2, 3, ...). As variáveis ​​não precisam ser pré-inicializadas - elas sempre começam com o valor 0 na primeira vez em que são usadas ou solicitadas.

Um nome de variável pode ser qualquer sequência não alfanumérica e sublinhada que não comece com um dígito - [a-zA-Z_][0-9a-zA-Z_]*em regex . Eles diferenciam maiúsculas de minúsculas spiny_lumpsuck3re Spiny_lumpsuck3rsão variáveis ​​diferentes.

Execução

Prindeal é uma linguagem de programação imperativa . Quando um programa Prindeal é executado, suas instruções são executadas de cima para baixo em ordem e, em seguida, o programa termina.

Toda linha não recuada em um programa Prindeal é uma declaração que envolve a execução de um único comando que pode ou não receber argumentos.

Linhas recuadas ocorrem somente após comandos de alias . Especificamente, exatamente três linhas recuadas com espaços únicos ocorrem após cada comando de alias e são consideradas parte dele. Portanto, as declarações de alias têm realmente quatro linhas. (Eles podem ser uma linha, quatro é apenas mais legível.)

Não de alias Demonstrações

Com exceção do alias , todas as instruções em um programa Prindeal têm a forma:

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

Pode haver um número arbitrário de argumentos (incluindo nenhum). Cada argumento é sempre uma variável ou (como veremos ao discutir o alias ) uma referência a uma variável .

Uma vez concluída a execução, cada instrução é sinalizada como uma falha ou êxito, dependendo se erros foram encontrados ou não. (Isso realmente importa apenas quando usamos o alias .)

A impressão interna , incremento e decremento são instruções com o formulário acima. Aqui está o que eles fazem:

  1. print tem nome de comando pe aceita um argumento. Ele imprime o nome da variável transmitida e seu valor (em decimal) separado por "=" e, em seguida, uma nova linha. É sempre sinalizado como sucesso .

    Por exemplo, o programa Prindeal

    p _MyVariable_321
    p screaming_hairy_armadillo
    

    produziria

    _MyVariable_321 = 0
    screaming_hairy_armadillo = 0
    

    porque todas as variáveis ​​começam em 0. (Os espaços antes e depois do sinal de igual são necessários.)

  2. incremento tem o nome do comando ie leva um argumento. Ele incrementa o valor da variável passada por 1. Ele é sempre sinalizado como um sucesso .

    Por exemplo, o programa

    i alpaca
    p alpaca
    i alpaca
    p alpaca
    

    produziria

    alpaca = 1
    alpaca = 2
    

    Observe como alpacafoi incrementado de 0 para 1, mesmo que nunca tivesse sido acessado antes.

  3. decrement tem nome de comando de usa um argumento. Se a variável transmitida for diferente de zero, seu valor será decrementado por 1 e a instrução será sinalizada como um sucesso . Se a variável passada for 0, nada será feito e a instrução será sinalizada como uma falha .

    Por exemplo, o programa

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

    produziria

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

    Observe que decrementar uma variável com o valor 0 é a única maneira de produzir uma falha .

O alias instrução e comandos alias

O comando alias possui uma sintaxe especial e é o mais poderoso, pois pode ser usado para definir novos comandos. O nome do comando alias é ae uma instrução alias tem o formato:

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

Onde cada um [statement X]representa qualquer declaração de não- alias , ou seja, algo com o formulário[command name] [argument 1] [argument 2] [argument 3] ... .

O nome do comando com alias [name of new command]pode ser qualquer sequência de caracteres alfanuméricos e sublinhados não vazia que não comece com um dígito -[a-zA-Z_][0-9a-zA-Z_]* na regex.

(Esse é o mesmo conjunto de nomes das variáveis, mas comandos e variáveis ​​com alias são coisas diferentes usadas em lugares diferentes . Uma variável pode ter o mesmo nome de um comando, sem consequências ruins.)

Quando uma instrução de alias é executada, um novo comando é adicionado ao lado dos quatro p i d acomandos originais . O novo comando pode ser usado como as [command name]instruções in e chamado com argumentos como qualquer outro não- alias comando .

Quando uma instrução com um nome de comando com alias é executada, são executadas exatamente mais duas instruções de sua instrução de alias original :

  • [statement A] é sempre executado
  • [statement B]é executado se [statement A]foi um sucesso
  • [statement C]é executado se [statement A]houve uma falha

As instruções A, B e C são sempre executadas preguiçosamente , ou seja, são avaliadas em tempo real no momento em que são executadas.

Quando a execução é concluída, o comando alternativo é sinalizado com o mesmo sinalizador de êxito ou falha da instrução B ou C, o que tiver sido executado . (As declarações de alias em si não precisam ser sinalizadas, pois não podem ocorrer dentro de si.)

Exemplo alternativo 1

Digamos que queremos um novo comando que aumente a variável frogduas vezes. Esta declaração de alias alcança:

a increment_frog_twice
 i frog
 i frog
 d frog

A instrução A ( i frog) é sempre executada e sempre sinalizada como um sucesso, portanto, a instrução B ( i frog) também é sempre executada e a variável frog é incrementada em 2. O increment_frog_twicecomando é sempre sinalizado como um sucesso porque a instrução B é sempre executada e B é sempre uma variável. sucesso . A instrução C ( d frog) nunca é executada.

Então a saída para

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

seria

frog = 0
frog = 2

Podemos generalizar este exemplo para que qualquer variável possa ser incrementada duas vezes, dando um argumento ao comando aliasado.

Dentro de uma declaração de alias , os números inteiros positivos 1, 2, 3, etc. representam os argumentos do 1º, 2º, 3º etc. passados ​​para o comando do alias. (Esses argumentos podem ser variáveis ​​simples ou referências às próprias variáveis.) Esses números podem aparecer apenas dentro dos argumentos da instrução A, B e C em uma instrução de alias . Não faz sentido que eles apareçam em outro lugar.

Exemplo alternativo 2

Isso generaliza o último exemplo - qualquer variável passada increment_twiceserá incrementada por 2 porque 1é uma referência ao primeiro argumento passado em:

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

A saída deste programa seria

toad = 0
toad = 2

Poderíamos, então, alias outro comando que recebe dois argumentos e chama os increment_twicedois:

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

A saída aqui seria

platypus = 2
duck = 2

É importante perceber que os comandos com alias podem ser recursivos, pois é aí que reside o verdadeiro poder deles. Por exemplo, podemos criar um comando que define qualquer variável passada para 0:

Exemplo alternativo 3

O set_to_zerocomando pega um argumento e define sua variável como 0 e é sinalizado como um sucesso quando concluído:

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

A saída deste programa seria

oryx = 3
oryx = 0

O que está acontecendo é que, quando set_to_zero oryxexecutado, d 1diminui com êxito oryxde 3 para 2 e set_to_zero 1é chamado, o mesmo que chamar set_to_zero oryxnovamente. Portanto, o processo se repete até que haja d 1uma falha , interrompendo a recursão e incrementando a _dummy_variável para que um sucesso seja produzido.


Desafio

Escreva um programa que possa executar o código do Prindeal exatamente como descrito acima. Pegue o código do Prindeal via stdin, na linha de comando ou como um arquivo de texto. Imprima a saída do programa Prindeal para stdout ou a alternativa mais próxima do seu idioma.

Como alternativa, você pode escrever uma função que recebe o código como uma sequência e imprime ou retorna a sequência de saída.

Além disso, você pode assumir que:

  • O código Prindeal de entrada conterá apenas novas linhas e ASCII imprimíveis e (opcionalmente) que termina com uma linha vazia.
  • O código de entrada será válido no Prindeal - bem formado e sintaticamente correto.
  • A execução do código não produzirá loops infinitos nem referências inválidas a comandos que não foram definidos ou argumentos que não foram fornecidos.
  • Os nomes de comando p, i, de anunca será alias mais. (Você não pode assumir que variáveis ​​não terão esses nomes.)

Além disso, não importa se os valores das variáveis ​​não são números inteiros com precisão arbitrária, pois apenas números menores que 1000 serão testados. Também é bom se a linguagem tiver limites de recursão (como Python ) que os programas mais complexos do Prindeal podem encontrar, desde que o programa de teste abaixo funcione.

Programa de teste

Aqui está um grande programa do Prindeal que constrói as operações de adição, multiplicação e exponenciação através do uso de variáveis ​​fictícias (começando _por convenção) e muitos aliases 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

(Se você estiver brincando com esse código, saiba que muitos dos comandos falharão se a mesma variável for dada várias vezes como argumento. Isso pode ser facilmente corrigido, mas o código resultante é mais longo.)

Seu intérprete Prindeal deve ser capaz de produzir a saída exata:

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

Pontuação

O código mais curto em bytes vence. O desempatador vai para a submissão anterior.

Brownie Bonus: Escreva um programa interessante no Prindeal. Eu implementei adição e multiplicação, você pode fazer subtração ou divisão?

Passatempos de Calvin
fonte
Oh cara, acho que pela primeira vez deixarei o Pyth em paz e pegarei um Lisp! Uma pergunta - funções e variáveis ​​vivem em namespaces totalmente diferentes, certo? Então eu posso incrementar p, e então p p, o que imprimiria 1, certo?
orlp
@orlp Correto. (Há algumas notas sobre isso aqui.)
de Calvino Hobbies
2
Não posso ser o único que pensa em PRNDL quando vejo o nome do idioma.
Downgoat 12/08/2015
Existe um número máximo de argumentos que serão passados ​​para um comando com alias?
Zach Gates
@ZachGates Nope
Calvin's Hobbies

Respostas:

9

Pitão, 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

Demonstração.

Golfed para 26 caracteres por inlining variáveis e mudando a partir de Ie Ecom base fluxo de controlo para ?e.x fluxo de controle baseado.

Pela primeira vez, fiquei sem variáveis ​​em Pyth. Todas as variáveis ​​em Pyth ( bdkGHNTYe JK) estavam em uso, e eu queria usar bcomo uma nova linha. Felizmente, eu era capaz de usarN significar duas coisas completamente diferentes em partes diferentes do programa, e assim ainda funciona.

Ungolfed (execute com -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
fonte
3
Eu amo como eu ainda não posso dizer o que ele faz mesmo com que ungolfed ...
Jerry Jeremias
Bem, isso conclui que Pyth não é completo para Turing ...
Erik the Outgolfer
8

Python 2, 600 584 397 373 bytes

Esta é a minha própria solução de referência para golfe. Qualquer pessoa é bem-vinda para melhorá-lo ou seguir sua lógica em sua própria resposta, desde que seja atribuída.

A parte interessante é que nenhuma recursão é feita, portanto nunca haverá problemas com o limite de recursão do Python. Por exemplo, o programa Countup Prindeal da Sp pode ser executado 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

É um programa que recebe a sequência de programa citada com novas linhas escapadas, por exemplo
'p _MyVariable_321\np screaming_hairy_armadillo'.

Peguei várias pistas de golfe das respostas de Sp e Pietu . Obrigado rapazes :)

Passatempos de Calvin
fonte
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 graças a @orlp)

Ainda jogando golfe. Assume que o programa está armazenado em um arquivo chamado P.

Colocar as chamadas fdentro do lambda deconomizaria alguns bytes, mas faria com que o último caso de teste atingisse a profundidade máxima de recursão.

Alguns programas Prindeal

O programa de subtração inútil

Aqui está um programa de subtração inútil . É inútil porque, mesmo que subtraia corretamente, não retorna sucesso / falha de acordo.

A saída deve ser:

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

Countup

a helper
 p 1
 countup 1
 i success

a countup
 i 1
 helper 1
 d failure

countup n

Conta para cima e imprime npara sempre. Poderia funcionar como um teste de velocidade do intérprete (cuidado com os longos rastros na interrupção do teclado).

Sp3000
fonte
2
Todo mundo nessa questão perdeu esse golfe, não entendo o porquê. l[:(l+"#").find("#")]e todas as suas variações podem ser substituídas por uma simples l.split('#')[0].
orlp
@ orlp Estava tão focado findque eu esqueci que você poderia, splitmesmo que #não estivesse lá. Obrigado :)
Sp3000 13/08/2015
6

JavaScript (ES6), 273 258

Editar bugs corrigidos e adicionou um conjunto de testes real.

Sem contar espaços iniciais e novas linhas.

Certamente pode ser jogado um pouco mais.

Cansado demais para escrever uma explicação agora, acho que é um bom exemplo de uso de fechamentos para manter vivos valores temporários (parâmetros).

Teste a execução do snippet em qualquer navegador compatível com EcmaScript 6 (principalmente o Chrome, não o MSIE. Eu testei no Firefox, o Safari 9 poderia ir)

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
fonte
Adicionei mais alguns comentários ao programa de teste e parece que eles fizeram o seu código não funcionar.
Hobbies de Calvin
@ Calvin'sHobbies um primeiro patch rápido
edc65 13/08
3

C # 6, 653 bytes

Aqui está minha entrada, em meio a um 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;}}

Expandido e 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 usá-lo, basta instanciar a classe e chamar o R()método, por exemplo:

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

Lisp comum, 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))))

Coloque isso file.lispe chame por exemplo sbcl --script file.lisp; entrada é lida a partir do fluxo de entrada padrão.

Esta versão analisa um superconjunto do Prindeal: sem muitas dificuldades, você pode acessar todo o Common Lisp a partir de uma fonte do Prindeal. Considero isso uma característica do intérprete.

Versão 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)))))

Exemplo

~$ 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

Se substituirmos evalpor printno loop de leitura / avaliação, podemos ver o que está sendo avaliado:

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

Expansão de macro

Se escolhermos a seguinte definição de alias:

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

... podemos ver referências a uma variável nomeada gque não pode ser encontrada em nenhum lugar no escopo lexical. Mas após a macroexpansão, eis o código real que está sendo avaliado:

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

Agora, grefere-se à lista de argumentos da função que está sendo definida.

coredump
fonte
2

Python 2, 486 bytes

Esta é a solução de referência que eu joguei mais (atualmente -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:]

Mudanças (que eu me lembro):

  • conversão automática de número inteiro booleano ([l,l[:l.find('#')]]['#'in l] ).
  • definir ou incrementar em uma instrução (V[k]=-~V[k]if k in V else 1 )
  • mais aliases para expressões mais longas (k=s[1] )
  • nenhum contador no loop principal, limpando a lista de entradas
  • printadicionando espaços automaticamente ( print k,'=',V.get(k,0))
  • verificação de dígitos 1-9 ( '0'<t[0]<':')
  • inverter os valores de retorno de rvolta para salvar returns
  • removendo a repetição de fatiar e dividir ( map(str.split,c[:3])))
PurkkaKoodari
fonte
1

Python 3, 1322 bytes

Golfe:

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

Ungolfed:

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)

Onde cestá o conteúdo do texto.

Exemplos:

Cadeias de caracteres de linha única são aceitas:

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

Também são aceitas sequências de linhas múltiplas:

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

Ou:

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

Etc.

Notas:

Isso funciona corretamente para todos os casos de teste, mas atinge o limite de recursão em:

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

Daí o sys.setrecursionlimit(2000).

Zach Gates
fonte
1
Ele consome alguns bytes, mas você não pode usar o sys.setrecursionlimit () para que isso funcione corretamente com o pias?
Corwin
Eu poderia, mas o OP afirmou que linguagens como Python (que têm limites de recursão) são aceitas como estão. No entanto, adicionarei a correção se solicitado pelo OP. @Corwin
Zach Gates
Justo. Perdeu isso nas especificações. @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> é um caractere de tabulação literal.

pppery
fonte
1

C ++, 1111 bytes

Este é o C ++ - o mais idiomático possível.
Isso significa torná-lo mais C ++ - ish e menos C-ish.
Isso também significa que é maior que o programa C equivalente.
Eu acho que C ++ rivaliza com Java para biblioteca padrão detalhada.
Ele é compilado com o VS2013 e g ++ 4.9.2 (com -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;}}}

Abaixo está o original. Se alguém puder pensar em uma maneira de torná-lo mais idiomático e mais curto ao mesmo tempo, informe-me.

#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
fonte
0

Haskell, 1009

Eu fiz o meu melhor para jogar golfe; meu código não destruído consistia em mais de 3.000 caracteres. Neste ponto, não me lembro do que todas as funções estão fazendo, portanto, jogar golfe significa adivinhar o que irá quebrá-lo e o que não.

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
fonte