Print, Increment, Decrement, Alias ​​- Interpréter Prindeal

30

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:

  1. 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.)
  2. Espace de fin sur n'importe quelle ligne.
  3. 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_lumpsuck3ret Spiny_lumpsuck3rsont 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:

  1. print a un nom de commande pet 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.)

  2. increment a un nom de commande iet 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.

  3. décrémenter a un nom de commande det 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 aet 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 acommandes 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 frogdeux 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 variable frog est ainsi incrémentée de 2. La increment_frog_twicecommande 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_twicesera incrémentée de 2 car 1c'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_twicetous 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_zerocommande 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 oryxest exécuté, d 1décrémente avec succès oryxde 3 à 2, puis set_to_zero 1est appelé, ce qui revient à rappeler set_to_zero oryx. Ainsi, le processus se répète jusqu'à ce que d 1soit 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, det ane 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?

Loisirs de Calvin
la source
Oh mec, je pense que pour une fois je vais laisser le Pyth tranquille et sortir un Lisp! Une question - les fonctions et les variables vivent dans des espaces de noms entièrement différents, non? Je peux donc incrémenter p, et ensuite p p, qui afficherait 1, non?
orlp
@orlp Correct. (Il y a quelques notes là-dessus.)
Calvin's Hobbies
2
Je ne peux pas être le seul à penser PRNDL quand je vois le nom de la langue.
Descendre
Y a-t-il un nombre maximum d'arguments qui seront transmis à une commande aliasée?
Zach Gates
@ZachGates Nope
Calvin's Hobbies

Réponses:

9

Pyth, 162 136 octets

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

Manifestation.

Golfed à 26 caractères par inline les variables et le changement de Iet le Eflux de commande sur la base de ?et le .xflux de commande sur la base.

Pour la première fois, j'ai manqué de variables en Pyth. Chaque variable unique en Pyth ( bdkGHNTYet JK) était en cours d'utilisation, et je voulais l'utiliser bcomme une nouvelle ligne. Heureusement, j'ai pu utiliser Npour signifier deux choses complètement différentes dans différentes parties du programme, et donc cela fonctionne toujours.

Non golfé (exécuté avec -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
la source
3
J'adore la façon dont je ne peux toujours pas dire ce qu'il fait même s'il n'est pas golfé ...
Jerry Jeremiah
Eh bien, cela conclut que Pyth n'est pas complet-Turing ...
Erik the Outgolfer
8

Python 2, 600 584 397 373 octets

Ceci est ma propre solution de référence pour le golf. Tout le monde est invité à l'améliorer ou à suivre sa logique dans sa propre réponse tant que l'attribution est donnée.

La partie intéressante à ce sujet est qu'aucune récursivité n'est effectuée, donc elle n'aura jamais de problèmes avec la limite de récursivité de Python. Par exemple, le programme Countup Prindeal de Sp peut s'exécuter indéfiniment.

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

C'est un programme qui prend la chaîne de programme citée avec des sauts de ligne, par exemple
'p _MyVariable_321\np screaming_hairy_armadillo'.

J'ai pris divers indices de golf à partir des réponses de Sp et Pietu . Merci les gars :)

Loisirs de Calvin
la source
6

Python 3, 345 336 335 328 octets

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 octets grâce à @orlp)

Toujours au golf. Suppose que le programme est stocké dans un fichier nommé P.

Mettre les appels à l' fintérieur du lambda déconomiserait quelques octets, mais cela ferait que le dernier cas de test atteindrait la profondeur de récursivité maximale.

Quelques programmes Prindeal

Le programme de soustraction inutile

Voici un programme de soustraction inutile . C'est inutile parce que, même s'il soustrait correctement, il ne renvoie pas le succès / l'échec en conséquence.

La sortie doit être:

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

Compter jusqu'à

a helper
 p 1
 countup 1
 i success

a countup
 i 1
 helper 1
 d failure

countup n

Compte vers le haut et imprime npour toujours. Pourrait éventuellement fonctionner comme un test pour la vitesse de l'interpréteur (méfiez-vous des longs retraits sur interruption du clavier).

Sp3000
la source
2
Tout le monde sur cette question a raté ce golf, je ne comprends pas pourquoi. l[:(l+"#").find("#")]et toutes ses variantes peuvent être remplacées par un simple l.split('#')[0].
orlp
@orlp était tellement concentré sur ce findque j'ai oublié que vous pourriez le faire splitmême si #vous n'y étiez pas. Merci :)
Sp3000
6

JavaScript (ES6), 273 258

Modifier les bugs corrigés et ajouté une vraie suite de tests.

Sans compter les grands espaces et les nouvelles lignes.

Peut certainement être un peu plus joué au golf.

Trop fatigué pour écrire une explication maintenant, je pense que c'est un bon exemple d'utilisation de fermetures pour maintenir en vie des valeurs temporaires (paramètres).

Testez l'exécution de l'extrait sur n'importe quel navigateur compatible EcmaScript 6 (notamment pas Chrome ni MSIE. J'ai testé sur Firefox, Safari 9 pourrait aller)

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
la source
J'ai ajouté quelques commentaires supplémentaires au programme de test et il semble qu'ils aient rendu votre code inopérant.
Calvin's Hobbies
@ Calvin'sHobbies un premier patch rapide
edc65
3

C # 6, 653 octets

Voici mon entrée, au milieu d'une mer 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;}}

Développé et commenté:

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;
    }
}

Pour l'utiliser, il suffit d'instancier la classe et d'appeler la R()méthode, par exemple:

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

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

Mettez ceci file.lispet appelez par exemple sbcl --script file.lisp; l'entrée est lue à partir du flux d'entrée standard.

Cette version analyse un sur - ensemble de Prindeal: sans trop de difficultés, vous pouvez accéder à tous les Common Lisp à partir d'une source Prindeal. Je considère cela comme une caractéristique de l'interprète.

Version commentée

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

Exemple

~$ 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 nous remplaçons evalpar printdans la boucle de lecture / évaluation, nous pouvons voir ce qui est évalué:

(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 nous choisissons la définition d'alias suivante:

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

... nous pouvons voir des références à une variable nommée gqui ne se trouve nulle part dans la portée lexicale. Mais après macroexpansion, voici le code en cours d'évaluation:

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

Désormais, gfait référence à la liste d'arguments de la fonction en cours de définition.

coredump
la source
2

Python 2, 486 octets

C'est la solution de référence sur laquelle j'ai le plus joué (actuellement -98 octets).

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

Changements (dont je me souviens):

  • conversion automatique booléenne-entier ( [l,l[:l.find('#')]]['#'in l]).
  • définir ou incrémenter dans une instruction ( V[k]=-~V[k]if k in V else 1)
  • plus d'alias pour des expressions plus longues ( k=s[1])
  • pas de compteur dans la boucle principale, effaçant la liste d'entrée à la place
  • printajout automatique d'espaces ( print k,'=',V.get(k,0))
  • vérification des chiffres 1-9 ( '0'<t[0]<':')
  • inverser les valeurs de retour de raround pour enregistrer returns
  • suppression de la répétition du découpage et du fractionnement ( map(str.split,c[:3])))
PurkkaKoodari
la source
1

Python 3, 1322 octets

Golfé:

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

Non 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

Usage:

P(c)

cest le contenu du texte.

Exemples:

Les chaînes unifilaires sont acceptées:

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

Les cordes à lignes multiples sont également acceptées:

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

Ou:

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

Etc.

Remarques:

Cela fonctionne correctement pour tous les cas de test, mais atteint la limite de récursivité sur:

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

D'où le sys.setrecursionlimit(2000).

Zach Gates
la source
1
Cela utilisera quelques octets mais ne pourriez-vous pas utiliser sys.setrecursionlimit () pour que cela fonctionne correctement avec l'alias pow?
Corwin
Je pourrais, mais OP a déclaré que les langages comme Python (qui ont des limites de récursivité) sont acceptés tels quels. Cependant, j'ajouterai le correctif si OP le demande. @Corwin
Zach Gates
C'est suffisant. Manqué cela dans la spécification. @ZachGates
Corwin
1

Python - 695 688 octets

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> est un caractère de tabulation littéral.

pppery
la source
1

C ++, 1111 octets

Celui-ci est C ++ - aussi idiomatique que je pourrais le faire.
Cela signifie le rendre plus C ++ - ish et moins C-ish.
Cela signifie également qu'il est plus grand que le programme C équivalent.
Je pense que C ++ rivalise avec Java pour la bibliothèque standard verbeuse.
Il se compile avec VS2013 et g ++ 4.9.2 (avec -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;}}}

Voici l'original. Si quelqu'un peut penser à un moyen de le rendre plus idiomatique et plus court en même temps, faites-le moi savoir.

#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
la source
0

Haskell, 1009

J'ai fait de mon mieux pour jouer au golf; mon code non golfé comprenait plus de 3 000 caractères. À ce stade, je ne me souviens pas de toutes les fonctions, donc jouer au golf signifie plus deviner ce qui le cassera et ce qui ne le fera pas.

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
la source