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:
- Qualquer coisa após um
#
sinal até o final da linha está ligado, mais o#
próprio. (Estes são comentários.) - Espaço em branco à direita em qualquer linha.
- 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_lumpsuck3r
e Spiny_lumpsuck3r
sã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:
print tem nome de comando
p
e 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.)
incremento tem o nome do comando
i
e 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
alpaca
foi incrementado de 0 para 1, mesmo que nunca tivesse sido acessado antes.decrement tem nome de comando
d
e 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 é a
e 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
a
comandos 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
frog
duas 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ávelfrog
é incrementada em 2. Oincrement_frog_twice
comando é 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_twice
será incrementada por 2 porque1
é 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_twice
dois: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_zero
comando 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 oryx
executado,d 1
diminui com êxitooryx
de 3 para 2 eset_to_zero 1
é chamado, o mesmo que chamarset_to_zero oryx
novamente. Portanto, o processo se repete até que hajad 1
uma 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
,d
ea
nunca 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?
p
, e entãop p
, o que imprimiria 1, certo?