Prindeal (prononcé prin-dee-al ) est un nouveau langage de programmation ésotérique qui n'a que quatre commandes: pr int , in crement , de crement et al ias . Malgré son minimalisme, des opérations mathématiques complexes peuvent être effectuées dans Prindeal en combinant intelligemment les quatre commandes.
Votre tâche dans ce défi de golf de code est d'écrire le programme le plus court qui peut exécuter le code Prindeal.
La spécification est longue mais j'ai essayé de la rendre aussi claire que possible et je crois que si vous faites l'effort d'apprendre Prindeal, vous la trouverez assez élégante!
Intrépide Prindeal
Prétraitement
Avant qu'un programme Prindeal puisse être interprété, ces éléments doivent être supprimés dans cet ordre:
- Tout ce qui se trouve après un
#
signe à la fin de la ligne est allumé, plus le#
lui - même. (Ce sont des commentaires.) - Espace de fin sur n'importe quelle ligne.
- Lignes complètement vides.
Par exemple, le programme Prindeal
p cat #The next line has 7 trailing spaces.
p dog
#p mouse
serait prétraité en
p cat
p dog
À partir de là, nous supposerons que cette étape de prétraitement a été effectuée.
Variables
Nous devons rapidement définir des variables avant de montrer comment elles sont utilisées.
Les variables (et les références aux variables) sont ce qui est passé dans les arguments des commandes Prindeal. Les variables étant toujours globales , les modifications apportées à une variable, peu importe où elles se produisent, se reflètent partout.
Chaque variable contient un entier de précision arbitraire non négatif (0, 1, 2, 3, ...). Les variables n'ont pas besoin d'être pré-initialisées - elles commencent toujours par la valeur 0 la première fois qu'elles sont utilisées ou appelées.
Un nom de variable peut être une chaîne alphanumérique non vide et des traits de soulignement qui ne commencent pas par un chiffre - [a-zA-Z_][0-9a-zA-Z_]*
dans l' expression régulière . Ils sont sensibles à la casse spiny_lumpsuck3r
et Spiny_lumpsuck3r
sont donc des variables différentes.
Exécution
Prindeal est un langage de programmation impératif . Lorsqu'un programme Prindeal est exécuté, ses instructions sont exécutées de haut en bas dans l'ordre, puis le programme se termine.
Chaque ligne non en retrait dans un programme Prindeal est une instruction qui implique l'exécution d'une seule commande qui peut ou non prendre des arguments.
Les lignes en retrait n'apparaissent qu'après les commandes d' alias . Plus précisément, exactement trois lignes en retrait avec des espaces simples apparaissent après chaque commande d' alias et sont considérées comme faisant partie de celle-ci. Les instructions d' alias comportent donc en réalité quatre lignes. (Ils pourraient être une ligne, quatre est simplement plus lisible.)
Non alias Déclarations
À l'exception de l' alias , chaque instruction d'un programme Prindeal a la forme:
[command name] [argument 1] [argument 2] [argument 3] ...
Il peut y avoir un nombre arbitraire d'arguments (y compris aucun). Chaque argument est toujours une variable ou (comme nous le verrons lors de la discussion sur l' alias ) une référence à une variable .
Une fois l'exécution terminée, chaque instruction est signalée comme un échec ou un succès selon que des erreurs ont été rencontrées ou non. (Cela n'a d'importance que lorsque nous utilisons l' alias .)
L' impression , l' incrémentation et la décrémentation intégrées sont des instructions de la forme ci-dessus. Voici ce qu'ils font:
print a un nom de commande
p
et prend un argument. Il affiche le nom de la variable passée et sa valeur (en décimal) séparés par "=", puis un retour à la ligne. Il est toujours signalé comme un succès .Par exemple, le programme Prindeal
p _MyVariable_321 p screaming_hairy_armadillo
sortirait
_MyVariable_321 = 0 screaming_hairy_armadillo = 0
car toutes les variables commencent à 0. (Les espaces avant et après le signe égal sont obligatoires.)
increment a un nom de commande
i
et prend un argument. Il incrémente la valeur de la variable passée de 1. Il est toujours signalé comme un succès .Par exemple, le programme
i alpaca p alpaca i alpaca p alpaca
sortirait
alpaca = 1 alpaca = 2
Notez comment a
alpaca
été incrémenté de 0 à 1 même s'il n'y avait jamais eu accès auparavant.décrémenter a un nom de commande
d
et prend un argument. Si la variable transmise n'est pas nulle, sa valeur est décrémentée de 1 et l'instruction est signalée comme réussie . Si la variable transmise est 0, rien n'est fait et l'instruction est signalée comme un échec .Par exemple, le programme
i malamute p malamute d malamute #success p malamute d malamute #failure p malamute d akita #failure p akita
sortirait
malamute = 1 malamute = 0 malamute = 0 akita = 0
Notez que la décrémentation d'une variable avec la valeur 0 est le seul moyen de produire un échec .
L' instruction alias et les commandes aliasées
La commande alias a une syntaxe spéciale et est la plus puissante car elle peut être utilisée pour définir de nouvelles commandes. Le nom de la commande d' alias est a
et une instruction d' alias a la forme:
a [name of new command]
[statement A]
[statement B]
[statement C]
Où chacun [statement X]
représente une instruction non- alias , c'est-à-dire quelque chose avec le formulaire [command name] [argument 1] [argument 2] [argument 3] ...
.
Le nom de la commande aliasée [name of new command]
peut être n'importe quelle chaîne non vide de caractères alphanumériques et de soulignements qui ne commence pas par un chiffre - [a-zA-Z_][0-9a-zA-Z_]*
dans l'expression régulière.
(Il s'agit du même ensemble de noms que de variables, mais les commandes et variables aliasées sont différentes choses utilisées à différents endroits . Une variable peut être nommée de la même manière qu'une commande sans conséquences néfastes.)
Lorsqu'une instruction d' alias est exécutée, une nouvelle commande est ajoutée aux côtés des quatre p
i
d
a
commandes d'origine. La nouvelle commande peut être utilisée comme [command name]
instruction in et appelée avec des arguments comme toute autre commande non alias .
Lorsqu'une instruction avec un nom de commande aliasé est exécutée, exactement deux autres instructions de son instruction d' alias d' origine sont exécutées:
[statement A]
est toujours exécuté[statement B]
est exécuté si[statement A]
était un succès[statement C]
est exécuté si[statement A]
était un échec
Les instructions A, B et C sont toujours exécutées paresseusement , c'est-à-dire qu'elles sont évaluées à la volée au moment de leur exécution.
Une fois l'exécution terminée, la commande aliasée est signalée avec le même indicateur de réussite ou d' échec que l'instruction B ou C, selon celle qui a été exécutée . (Les déclarations d' alias elles-mêmes n'ont pas besoin d'être marquées car elles ne peuvent pas se produire en elles-mêmes.)
Exemple d'alias 1
Disons que nous voulons une nouvelle commande qui incrémente la variable
frog
deux fois. Cette déclaration d'alias y parvient:a increment_frog_twice i frog i frog d frog
L'instruction A (
i frog
) est toujours exécutée et toujours signalée comme un succès, de sorte que l'instruction B (i frog
) est également toujours exécutée et la variablefrog
est ainsi incrémentée de 2. Laincrement_frog_twice
commande est toujours signalée comme un succès car l'instruction B est toujours exécutée et B est toujours un succès . L'instruction C (d frog
) n'est jamais exécutée.Ainsi, la sortie vers
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frog
serait
frog = 0 frog = 2
Nous pouvons généraliser cet exemple afin que toute variable puisse être incrémentée deux fois en donnant un argument à la commande aliasée.
Dans une instruction d' alias , les entiers positifs 1, 2, 3, etc. représentent les 1er, 2e, 3e, etc. arguments passés dans la commande alias. (Ces arguments peuvent être des variables simples ou des références aux variables elles-mêmes.) Ces nombres ne peuvent apparaître que dans les arguments des instructions A, B et C dans une instruction d' alias . Cela n'a aucun sens pour eux d'apparaître ailleurs.
Exemple d'alias 2
Cela généralise le dernier exemple - toute variable passée dans
increment_twice
sera incrémentée de 2 car1
c'est une référence au premier argument passé dans:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toad
Le résultat de ce programme serait
toad = 0 toad = 2
Nous pourrions alors alias une autre commande qui prend deux arguments et les appelle
increment_twice
tous les deux: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 sortie ici serait
platypus = 2 duck = 2
Il est important de réaliser que les commandes aliasées peuvent être récursives, car c'est là que réside leur véritable pouvoir. Par exemple, nous pouvons créer une commande qui définit toute variable transmise à 0:
Exemple d'alias 3
La
set_to_zero
commande prend un argument et définit sa variable à 0 et est signalée comme un succès lorsqu'elle est terminée: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
Le résultat de ce programme serait
oryx = 3 oryx = 0
Ce qui se passe, c'est que lorsque
set_to_zero oryx
est exécuté,d 1
décrémente avec succèsoryx
de 3 à 2, puisset_to_zero 1
est appelé, ce qui revient à rappelerset_to_zero oryx
. Ainsi, le processus se répète jusqu'à ce qued 1
soit un échec , arrêtant la récursivité et incrémentant la_dummy_
variable pour qu'un succès soit produit.
Défi
Écrivez un programme qui peut exécuter le code Prindeal exactement comme décrit ci-dessus. Prenez le code Prindeal via stdin, la ligne de commande ou sous forme de fichier texte. Imprimez la sortie du programme Prindeal sur stdout ou l'alternative la plus proche de votre langue.
Alternativement, vous pouvez écrire une fonction qui prend le code sous forme de chaîne et imprime ou renvoie la chaîne de sortie.
De plus, vous pouvez supposer que:
- Le code Prindeal saisi ne contiendra que des sauts de ligne et des caractères ASCII imprimables et (éventuellement) qu'il se terminera par une ligne vide.
- Le code d'entrée sera Prindeal valide - bien formé et syntaxiquement correct.
- L'exécution du code ne produira aucune boucle infinie ni aucune référence invalide à des commandes qui n'ont pas été définies ou à des arguments qui n'ont pas été donnés.
- Les noms de commandes
p
,i
,d
eta
ne seront jamais plus aliasées. (Vous ne pouvez pas supposer que les variables n'auront pas ces noms.)
En outre, peu importe si vos valeurs de variable ne sont pas vraiment des entiers de précision arbitraire, car seuls les nombres inférieurs à environ 1000 seront testés. C'est aussi bien si votre langue a des limites de récursivité (comme Python ) que les programmes Prindeal plus complexes peuvent rencontrer tant que le programme de test ci-dessous fonctionne.
Programme de test
Voici un grand programme Prindeal qui construit les opérations d'addition, de multiplication et d'exponentiation grâce à l'utilisation de variables factices (à commencer _
par convention) et de nombreux alias d'assistance:
#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 vous jouez avec ce code, sachez que de nombreuses commandes échoueront si la même variable est donnée plusieurs fois comme argument. Cela peut facilement être corrigé mais le code résultant est plus long.)
Votre interprète Prindeal devrait être capable de produire la sortie exacte:
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
Notation
Le code le plus court en octets gagne. Tiebreaker revient à une soumission antérieure.
Brownie Bonus: Écrivez un programme sympa dans Prindeal. J'ai implémenté l'addition et la multiplication, pouvez-vous faire de la soustraction ou de la division?
p
, et ensuitep p
, qui afficherait 1, non?