Écrire un interprète pour 99

99

99 (prononcé "quatre-vingt-dix-neuf") est un tout nouveau langage de programmation ésotérique (à ne pas confondre avec 99 , notez les italiques). Votre tâche dans ce défi consiste à écrire un interprète pour 99 aussi court que possible. La soumission avec le moins d'octets gagne. Tiebreaker va à la soumission postée en premier.

Étant donné que cette question est un peu plus approfondie que d'habitude et que j'ai hâte de voir de bonnes réponses, j'attribuerai une récompense de 250 représentants à ma réponse préférée (pas nécessairement le gagnant).

99 Spec

99 est un langage impératif . Chaque ligne d'un programme 99 est une instruction unique et, au cours de l'exécution, le pointeur d'instruction commence à la ligne supérieure et parcourt chacune des lignes suivantes dans l'ordre, en les exécutant en cours de route. Le programme se termine lorsque la dernière ligne a été exécutée. Les instructions Goto peuvent rediriger le chemin du pointeur d'instruction.

Newline, space et 9sont les trois seuls personnages importants dans un programme 99 . Tous les autres personnages sont complètement ignorés. En outre, les espaces de fin sur chaque ligne sont ignorés et plusieurs espaces d'une ligne sont lus comme un seul espace. ("Newline" désigne tout codage de saut de ligne courant . Peu importe celui que votre interprète utilise.)

Donc ce programme:

   9      BLAH        99   9a9bb9c9
9 this line and the next have 6 trailing spaces 9      
      

Est identique à ce programme:

 9 99 9999
9 9

Variables

Les variables dans 99 ont toutes des noms qui sont un ou plusieurs 9chaînons ( 9+en regex). Par exemple, 9, 99et 9999999999sont toutes les variables distinctes. Naturellement, il y en a infiniment beaucoup (sauf limitations de mémoire).

La valeur de chaque variable est un entier signé, de précision arbitraire . Par défaut, chaque variable est affectée à sa propre représentation numérique. Donc, sauf si elle a été réaffectée, la valeur de la variable 9est le nombre 9 et la valeur de la variable 99est le nombre 99, et ainsi de suite. Vous pourriez penser que les variables sont traitées comme des nombres simples jusqu'à leur affectation explicite.

Je vais utiliser Vpour faire référence à un nom de variable arbitraire ci-dessous.
Chaque instance de Vpourrait être remplacé par 9, 99, 999, 9999, etc.

Les déclarations

Il existe cinq types d’énoncés différents dans 99 . Chaque ligne d'un programme 99 contient exactement une déclaration.

La syntaxe décrite ici suppose que tous les caractères superflus ont été supprimés, tous les espaces finaux ont été supprimés et que toutes les séquences d'espaces multiples ont été remplacées par des espaces simples.

1. Pas d'opération


Une ligne vide est un no-op . Cela ne fait rien (à part l'incrémentation du pointeur d'instruction).

2. sortie

V

Une seule variable Vsur une ligne imprime cette variable sur stdout.

Si Va un nombre impair de 9l »( 9, 999, etc.) , alors la valeur entière de Vdivisé par 9 à imprimer (en décimal).

Si Va un nombre pair de 9« s ( 99, 9999, etc.), le ASCII caractère avec le code Vdivisé par 9, mod 128 est imprimé. (C'est (V / 9) % 128une valeur de 0 à 127.)

Exemple : le programme

9
9999

serait imprimer 1W. La première ligne est imprimée 1car 9/9 correspond à 1. La deuxième ligne est imprimée Wcar 9999/9 correspond à 1111, et 1111 mod 128 à 87, et 87 correspond au code de caractère W.

Notez que les sauts de ligne ne sont pas imprimés entre les jetons de sortie. \ndoit être explicitement imprimé pour un saut de ligne.

3. entrée

 V

Une seule variable Vsur une ligne avec un espace de début prend l'entrée de stdin et la stocke dans cette variable.

Si Va un nombre impair de 9', l'utilisateur peut taper n'importe quel entier signé et Vsera défini sur 9 fois cette valeur.

Si Va un nombre pair de 9, l'utilisateur peut saisir n'importe quel caractère ASCII et Vsera défini sur 9 fois son code de caractère.

Exemple : donné -57et Aen entrée, ce programme

 9
9
 99
99

serait sortie -57A. En interne, la variable 9aurait la valeur -513 et 99la valeur 585.

Votre interprète peut supposer que les entrées sont toujours syntaxiquement valables.

4. Cession

Cette déclaration peut être arbitrairement longue. Il y a deux variables ou plus sur une ligne, séparées par des espaces:

V1 V2 V3 V4 V5 ...

Ceci assigne à la somme de tous les 'avec des indices pairs, moins la somme des ' avec des indices impairs (excluant ). Les assignations sont par valeur, pas par référence.V1VVV1

Il pourrait être traduit dans la plupart des langues par .V1 = V2 - V3 + V4 - V5 + ...

Donc, s’il n’ya que deux variables, l’affectation normale est la suivante:

V1 V2V1 = V2

S'il y en a trois, alors c'est une soustraction:

V1 V2 V3V1 = V2 - V3

Et le signe +/ -continue à basculer avec chaque variable supplémentaire:

V1 V2 V3 V4V1 = V2 - V3 + V4

Exemple : Ce programme afficherait 1110123:

999           Prints triple-nine divided by nine (111).
999 9 9       Assigns triple-nine to zero (nine minus nine).
999           Prints triple-nine divided by nine (0)
9 999 9       Assigns single-nine to negative nine (zero minus nine).
999 999 9     Adds nine to triple-nine (really subtracts negative nine).
999           Prints triple-nine divided by nine (1).
999 999 9     Adds nine to triple-nine (really subtracts negative nine).
999           Prints triple-nine divided by nine (2).
999 999 9     Adds nine to triple-nine (really subtracts negative nine).
999           Prints triple-nine divided by nine (3).

5. Aller à (sauter si tout à zéro)

Cette déclaration peut aussi être arbitrairement longue. Il y a deux variables ou plus sur une ligne, séparées par des espaces, avec un espace de gauche :

 V1 V2 V3 V4 V5 ...

Si certaines des valeurs sont non nulles, cela se comporte comme un non-op. Le pointeur d'instruction est déplacé vers la ligne suivante comme d'habitude.V1

Si toutes les valeurs sont égales à zéro, le pointeur d'instruction est déplacé vers le numéro de ligne . Les lignes sont indexées à zéro. Par conséquent, si la valeur est zéro, le pointeur se déplace vers la ligne supérieure. Le programme se termine (normalement, sans erreur) s'il est négatif ou supérieur à l'indice le plus élevé possible (nombre de lignes moins une).V1 V1V1V1

Notez que ce n'était pas divisé par 9 ici. Et comme il est impossible d'avoir une variable qui ne soit pas un multiple de 9, seuls les numéros de ligne qui sont des multiples de 9 peuvent être sautés.V1

Exemples:

Ce programme imprimera 1pour toujours:

9          Prints single-nine divided by nine (always 1).
99 9 9     Assigns double-nine to zero.
 99 99     Jumps to line zero (top line) if double-nine is zero.

Ce programme

99999999                                              Print G.
999 99                                                Set triple-nine to ninety-nine.
9999999999 9999999999 9999999999 99 99 9 9 999 999    Set 10-nine to zero.
99999999999 9999999999                                Set 11-nine to zero.





999                                                   Print triple-nine's value divided by nine. (This is the ninth line.)
99999999                                              Print G.
999 999 9                                             Subtract nine from triple-nine.
 99999 999                                            Jump to line 5-nines if triple-nine is zero (ends program).
 9 99999999999 9999999999                             Jump to line nine if 10-nine and 11-nine are zero (always jumps).

affichera les nombres 11 à 1, par ordre décroissant, entourés de G:

G11G10G9G8G7G6G5G4G3G2G1G

Détails supplémentaires

L'interpréteur idéal s'exécutera à partir de la ligne de commande avec le nom de fichier de programme 99 en tant qu'argument. Les E / S seront également effectuées à la volée dans la ligne de commande.

Cependant, vous pouvez simplement écrire une fonction interpréteur qui prend le programme sous forme de chaîne, ainsi qu'une liste des jetons d'entrée (par exemple ["-57", "A"]). La fonction doit imprimer ou renvoyer la chaîne de sortie.

Des méthodes légèrement différentes d’exécution de l’interprète et de gestion des E / S conviennent si ces options sont impossibles dans votre langue.


Bonus: écrivez quelque chose de cool dans 99 et je le mettrai volontiers dans cet article à titre d'exemple.


J'espère que vous avez apprécié mon 99ème défi! :RÉ

Les passe-temps de Calvin
la source
9
J'ai envisagé d'augmenter le nombre de votes, mais votre score actuel est de 9…
wchargin
30
@WChargin a l'air qu'il va maintenant falloir essayer de l'obtenir 99.
trlkly
5
Il y a sûrement un bonus pour l'auto-hébergement (écrire 99 interprètes sur 99 ), non?
Gabe
5
@Gabe Une réponse comme celle-là obtiendrait probablement la prime, mais si c'était la seule réponse, qu'interpréterait l'interprète? ;)
Calvin's Hobbies
1
@Optimizer cela fonctionne: pastebin.com/raw.php?i=h73q58FN
coredump

Réponses:

16

CJam, 157 octets

{:I;_N" 9"+--N/:P:,$W=){1a*Ab}%:V;{PT):T(=:LS%_{LS#\:,_,({(\{V=}%@{V-1@{2$*+0@-\}*\;t:V;}{:|T@V=9*?:T;}?}{~\{_V=\1&!{128%c}*o}{VIW):W=it:V;}?}?}R?Tg)TP,<*}g}

Essayez-le en ligne:

Explication

Essayer de formater ceci avec l'indentation et les commentaires appropriés prendrait probablement une éternité, je vais donc vous donner un résumé algorithmique.

Le code est un bloc, l'analogue de CJam aux fonctions anonymes. Le bloc attend la chaîne de programme et la liste des entrées de la pile lors de son exécution.

L'initialisation comprend trois étapes. Tout d'abord, la liste de saisie est enregistrée. Ensuite, chaque caractère du programme qui n'a pas de sens est supprimé et le résultat est divisé en une liste de lignes et enregistré. Enfin, la liste de variables est initialisée. Cette liste mappe chaque variable, indexée par la longueur du nom, sur sa valeur divisée par 9 (une variable ne peut jamais contenir une valeur qui n'est pas un multiple de 9, et toutes les opérations sauf celle qui permet de tirer parti de cette modification). La liste est initialisée jusqu'à la longueur de la ligne la plus longue, ce qui correspond à la limite supérieure du nom le plus long présent. Il existe également un peu d'initialisation implicite due aux valeurs initiales des variables: le numéro de ligne est 0 et l'index d'entrée est -1.

L'interpréteur est implémenté comme on pourrait s'y attendre: une boucle qui lit la ligne suivante, incrémente le numéro de ligne et exécute la ligne pendant que le numéro de ligne pointe sur une ligne existante. L'analyse de ligne vérifie d'abord que la ligne n'est pas vide, puis branche en fonction de si l'arité est 1 ou> 1, puis en fonction de l'existence ou non d'un espace. Ces quatre branches imitent les quatre opérations (à l’exclusion des opérations sans opposition) de manière généralement simple, bien qu’elles jouent au golf de manière agressive comme tout le reste. L’une des optimisations de la note est peut-être que, puisqu’une séquence d’entrée valide doit toujours produire un élément de type attendu par le programme, j’ai omis de créer des observations séparées en fonction de la longueur du nom de la variable. On suppose simplement que l'élément lu dans la liste d'entrée est du type attendu.

Runer112
la source
15
+1 Plutôt court. Maintenant, pouvez-vous écrire un interprète de CJam en 99 ? ;-)
coredump
9
@coredump * frémissements *
Runer112
Bon sang, je ne peux obtenir que 195 $ et j'ai perdu espoir et abandonné: P
Optimizer
Cela ne prend pas le bon modulo lors de l’impression de valeurs négatives. Cela peut être corrigé en remplaçant 128%par 128,=.
Martin Ender
26

Python 3, 421 414 410 404 388 395 401 octets

Golfé:

import sys,re
v,i,c,g,L={},0,[re.sub('( ?)[^9]+','\\1',l).rstrip().split(' ')for l in open(sys.argv[1])],lambda i:v.get(i,int(i)//9),len
while-1<i<L(c):
 d=c[i];l=L(d);e,*f=d;i+=1
 if l>1:
  x,*y=f
  if e:w=list(map(g,f));v[e]=sum(w[::2])-sum(w[1::2])
  elif l==2:j=input();v[x]=int(j)if L(x)%2 else ord(j)
  elif~-any(g(j)for j in y):i=g(x)*9
 elif e:w=g(e);print(w if L(e)%2 else chr(w%128),end='')

Ungolfed:

import sys, re

# Intialise variable table.
vars_ = {}
get_var = lambda i: vars_.get(i, int(i)//9)

# Parse commands.
commands=[re.sub('( ?)[^9]+','\\1',l).rstrip().split(' ') for l in open(sys.argv[1])]

# Run until the current instruction index is out of bounds.
index=0
while 0 <= index < len(commands):
    # Get the current command and increment the index.
    command = commands[index]
    l = len(command)
    first = command[0]
    index += 1

    if l > 1:
        # Handle the "assignment" command.
        if first:
            operands = [get_var(i) for i in command[1:]]
            vars_[first] = sum(operands[0::2]) - sum(operands[1::2])
        # Handle the "input" command.
        elif l==2:
            inp = input()
            vars_[command[1]] = int(inp) if len(command[1]) % 2 else ord(inp)
        # Handle the "goto" command.
        elif not any(get_var(i) for i in command[2:]):
            index = get_var(command[1]) * 9
    # Handle the "output" command.
    elif first:
        val = get_var(first)
        print(val if len(first) % 2 else chr(val % 128),end='')

Presque juste une mise en œuvre littérale de la spécification, mise en échec aussi loin que je peux l’obtenir.

Exécutez à partir de la ligne de commande en fournissant un fichier de code source 99 comme seul argument (par exemple, le dernier exemple de l'OP):

> python3 ninetynine.py countdown.txt
G11G10G9G8G7G6G5G4G3G2G1G
>

En prime, voici une implémentation (plutôt médiocre) de "99 bouteilles" dans 99 : http://pastebin.com/nczmzkFs

Mac
la source
1
@DLosc: en ce qui concerne votre premier point: moi aussi, je pense elsequ’après un numéro pourrait être supprimé, mais lorsque j’ai essayé plus tôt, j’ai eu une erreur de syntaxe. Vos autres conseils sont toutefois très appréciés!
Mac
3
@coredump: à la façon dont la spécification est écrite, chaque variable aura toujours une valeur divisible par neuf. J'ai trouvé plus concis de permettre aux variables de prendre n'importe quelle valeur et de ne multiplier / diviser par neuf que nécessaire (en particulier dans la gotoroutine et lors de l'obtention de la valeur par défaut de la variable). Pour un utilisateur de la langue, cela ne fait aucune différence.
Mac
2
Pas le elsesoi, juste l'espace avant. Par exemple 3*n+1if n%2else n//2.
DLosc
1
@DLosc: désolé, je me suis mal exprimé - j’ai bien voulu dire l’espace, pas le else. Par exemple, j'ai essayé de remplacer print(w if L(e)%2 else chr(w%128))par print(w if L(e)%2else chr(w%128))et une exception de syntaxe a été obtenue.
Mac
1
Odd - J'ai testé sur ideone.com et cela a fonctionné, mais vous avez raison, cela ne fonctionne pas avec l'interpréteur Python3 (3.4.0 sur Ubuntu). Cet article de conseil clarifie: un nombre suivi d'un jeton alphabétique fonctionne en général, mais pas pour les jetons commençant par eou E, et (d'après les commentaires), ni pour l'un 0orni pour l' autre.
DLosc
16

Common Lisp, 1180 857 837 836 octets

Je sais que cela ne va pas gagner, mais je me suis amusé à jouer au golf celui-ci. J'ai réussi à supprimer 343 octets, ce qui représente plus de deux interpréteurs 99 écrits en CJam.

Aussi, assez amusant, plus j'essaie de le compresser, plus je suis persuadé que pour Common Lisp, il est plus rapide de compiler le code que d'essayer de l'interpréter à la volée.

(defmacro g(g &aux a(~ -1)> d x q(m 0)r v(n t)c(w 0)? u z)(flet((w(n p)(intern(format()"~a~a"p n))))(#1=tagbody %(case(setf c(ignore-errors(elt g(incf ~))))(#\  #2=(when(> w 0)(pushnew w v)(if u()(setq ?(oddp w)))(#5=push(w w'V)u)(setf w 0))(setf z t))(#\9(incf w)(setf >(or >(and n z))z()n()))((#\Newline())#2#(#5#(when u(setf u(reverse u)a(pop u))(if >(if u`(when(every'zerop(list,@u))(setf @,a)(go ^))`(setf,a,(if ?'(read)'(char-code(read-char)))))(if u`(setf,a,(do(p m)((not u)`(-(+,@p),@m))(#5#(pop u)p)(#5#(if u(pop u)0)m)))`(princ,(if ? a`(code-char(mod,a 128)))))))r)(incf m)(setf ?()u()z()>()n t)))(if c(go %))$(decf m)(setq d(pop r))(if d(#5# d x))(when(=(mod m 9)0)(#5#(w #3=(/ m 9)'L)x)(#5#`(,#3#(go,(w #3#'L)))q))(if(>= m 0)(go $)))`(let(@,@(mapcar(lambda(n)`(,(w n'V),(/(1-(expt 10 n))9)))v))(#1#,@x(go >)^(case @,@q)>))))
  • l'analyse lexicale et la génération de code s'entrelacent: je ne stocke pas la représentation interne, mais traite directement chaque ligne.
  • il y en a un tagbodypour effectuer 2 boucles:

     (... (tagbody % ... (go %) $ ... (go $)) result)
    
  • les variables locales sont déclarées dans &aux

  • ne génère pas de fermeture, mais directement le code interprété
  • etc.

Ungolfed, commenté

(defmacro parse-99
    (string &aux
              (~ -1) ; current position in string
              a      ; first variable in a line 
              >      ; does current line starts with a leading space?
              d      ; holds a statement during code generation
              x      ; all statements (labels + expressions)
              q      ; all generated case statements 
              (m 0)  ; count program lines (first increases, then decreases) 
              r      ; list of parsed expressions (without labels)
              v      ; set of variables in program, as integers: 999 is 3
              (n t)  ; are we in a new line without having read a variable? 
              c      ; current char in string 
              (w 0)  ; currently parsed variable, as integer 
              ?      ; is first variable odd? 
              u      ; list of variables in current line, as integers
              z)     ; is the last read token a space?
  (flet((w(n p)
          ;; produce symbols for 99 variables
          ;; e.g. (10 'V) => 'V10
          ;;      (4 'L)  => 'L4
          (intern(format()"~a~a"p n))))
    (tagbody
     parse
       (case (setf c
                   ;; read current char in string,
                   ;; which can be NIL if out-of-bounds
                   (ignore-errors(aref string (incf ~))))

         ;; Space character
         (#\Space
          #2=(when(> w 0)
               (pushnew w v)            ; we were parsing a variable, add it to "v"
               (if u()(setq ?(oddp w))) ; if stack is empty, this is the first variable, determine if odd
               (push(w w'V)u)           ; add to stack of statement variable
               (setf w 0))              ; reset w for next variable

          ;; Space can either be significant (beginning of line,
          ;; preceding a variable), or not. We don't know yet.
          (setf z t))

         ;; Nine
         (#\9
          (incf w) ; increment count of nines
          (setf >(or >(and n z)) ; there is an indent if we were
                                 ; starting a newline and reading a
                                 ; space up to this variable (or if we
                                 ; already know that there is an
                                 ; indent in current line).
                ;; reset z and n
                z()n()))

         ;; Newline, or end of string
         ((#\Newline())
          #2#  ;; COPY-PASTE the above (when(> w 0)...) statement,
               ;; which adds previously read variable if necessary.

          ;; We can now convert the currently read line.
          ;; We push either NIL or a statement into variable R.

          (push(when u
                     (setf u (reverse u) ; we pushed, we must reverse
                           a (pop u))    ; a is the first element, u is popped
                     (if >
                         ;; STARTS WITH LEADING SPACE
                         (if u
                             ;; JUMP
                             `(when(every'zerop(list,@u))(setf @,a)(go ^))

                             ;; READ
                             `(setf,a,(if ?'(read)'(char-code(read-char)))))

                         ;; STARTS WITH VARIABLE
                         (if u

                             ;; ARITHMETIC
                             `(setf,a,(do(p m) ; declare p (plus) and m (minus) lists

                                         ;; stopping condition: u is empty
                                         ((not u)
                                          ;; returned value: (- (+ ....) ....)
                                          `(-(+,@p),@m))

                                        ;; alternatively push
                                        ;; variables in p and m, while
                                        ;; popping u

                                        (push(pop u)p)

                                        ;; first pop must succeed, but
                                        ;; not necessarly the second
                                        ;; one.  using a zero when u
                                        ;; is empty covers a lot of
                                        ;; corner cases.

                                        (push(if u (pop u) 0) m)))

                             ;; PRINT
                             `(princ,(if ? a`(code-char(mod,a 128)))))))
               r)
          ;; increase line count
          (incf m)
          ;; reset intermediate variables
          (setf ?()u()z()>()n t)))

       ;; loop until end of string
       (if c (go parse))


     build
       ;;; Now, we can add labels in generated code, for jumps

       ;; decrease line count M, which guards our second loop
       (decf m)

       ;; Take generated statement from R
       (setq d(pop r))

       ;; we pop from R and push in X, which means X will eventually
       ;; be in the correct sequence order. Here, we can safely
       ;; discard NIL statements.

       ;; We first push the expression, and THEN the label, so that
       ;; the label ends up being BEFORE the corresponding statement.
       (if d(push d x))

       ;; We can only jump into lines multiple of 9
       (when (=(mod m 9)0)
         ;; Push label
         (push(w #3=(/ m 9)'L)x)
         ;; Also, build a case statement for the jump table (e.g. 2(go L2))
         (push`(,#3#(go,(w #3#'L)))q))
       ;; loop
       (if(>= m 0)(go build)))

    ;; Finally, return the code
    `(let(@ ; target of a jump instruction

          ;; other variables: V3 represents 999 and has a default value of 111
          ,@(mapcar(lambda(n)`(,(w n'V),(/(1-(expt 10 n))9)))v))

       ;; build a tagbody, inject statements from X and case statements from Q
       ;; label ^ points to jump table : we go to ^ each time there is a JUMP
       ;; label > is the end of program

       ;; note that if the case does not match any authorized target
       ;; address, we simply end the programs.
       (tagbody,@x(go >)^(case @,@q)>))))

Nous utilisons des entrées / sorties standard lors de l'évaluation, ce qui signifie que nous utilisons standard readet des princfonctions. Par conséquent, le code résultant peut être rendu exécutable sur la ligne de commande, comme indiqué ci-dessous.

Les entrées ne sont pas complètement correctement filtrées lors de l'exécution de 99 programmes: on suppose que l'utilisateur sait quel type de valeurs est attendu.

Le seul surcoût d' exécution possible peut survenir lors du saut, car nous devons évaluer la valeur d'une variable et faire correspondre cette valeur à une étiquette. Sauf que, l'interprète doit être assez efficace.

Basé sur l' obseravtion astucieuse de Mac, qui ne nécessite pas de division et de multiplication par 9 à chaque fois, la version actuelle parvient à ne jamais diviser ni multiplier par 9 lors de l'exécution.

Exemple

Si nous remplaçons defmacropar defun, nous voyons le code généré. Par exemple:

(g
"99999999                                              Print G.
999 99                                                Set triple-nine to ninety-nine.
9999999999 9999999999 9999999999 99 99 9 9 999 999    Set 10-nine to zero.
99999999999 9999999999                                Set 11-nine to zero.





999                                                   Print triple-nine's value divided by nine. (This is the ninth line.)
99999999                                              Print G.
999 999 9                                             Subtract nine from triple-nine.
 99999 999                                            Jump to line 5-nines if triple-nine is zero (endsprogram).
 9 99999999999 9999999999                             Jump to line nine if 10-nine and 11-nine are zero (alwa

")

Voici le code résultant:

(LET (@
      (V5 11111)
      (V11 11111111111)
      (V1 1)
      (V10 1111111111)
      (V2 11)
      (V3 111)
      (V8 11111111))
  (TAGBODY
   L0
    (PRINC (CODE-CHAR (MOD V8 128)))
    (SETF V3 (- (+ V2) 0))
    (SETF V10 (- (+ V3 V1 V2 V10) V3 V1 V2 V10))
    (SETF V11 (- (+ V10) 0))
   L1
    (PRINC V3)
    (PRINC (CODE-CHAR (MOD V8 128)))
    (SETF V3 (- (+ V3) V1))
    (WHEN (EVERY 'ZEROP (LIST V3)) (SETF @ V5) (GO ^))
    (WHEN (EVERY 'ZEROP (LIST V11 V10)) (SETF @ V1) (GO ^))
    (GO >)
   ^
    (CASE @ (0 (GO L0)) (1 (GO L1)))
   >))

Une fois exécuté, imprime "G11G10G9G8G7G6G5G4G3G2G1G"

Ligne de commande

Nous pouvons construire un exécutable en vidant un noyau et en spécifiant la toplevelfonction. Définissez un fichier nommé boot.lispoù vous avez placé le defmacro, puis écrivez ce qui suit:

(defun main()(parse-99 <PROGRAM>))
(save-lisp-and-die "test-99" :executable t :toplevel #'main)

En cours d'exécution sbcl --load boot.lispdonne la sortie suivante:

$ sbcl --load boot.lisp 
This is SBCL 1.2.8.32-18c2392, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
[undoing binding stack and other enclosing state... done]
[saving current Lisp image into test-99:
writing 5824 bytes from the read-only space at 0x20000000
writing 3120 bytes from the static space at 0x20100000
writing 55771136 bytes from the dynamic space at 0x1000000000
done]

Ensuite, exécutez le programme compilé 99 :

$ time ./test-99
G11G10G9G8G7G6G5G4G3G2G1G
real    0m0.009s
user    0m0.008s
sys     0m0.000s

99 bouteilles

Si vous êtes intéressé, voici le code compilé pour le programme de 99 bouteilles écrit dans la réponse de Mac : http://pastebin.com/ZXe839CZ (il s'agit de l'ancienne version où nous avons jmpet des endétiquettes, un lambda environnant et une arithmétique plus jolie).

Voici une exécution avec la nouvelle version, pour prouver que cela fonctionne toujours: http://pastebin.com/raw.php?i=h73q58FN

coredump
la source
6

TI-84 Basic (Script de calculatrice), 376 373 377 381 octets

Si elle fonctionne sur une calculatrice TI-84, vous pourrez l’utiliser pour un test standardisé… donc utile;)

Version minimale du système d'exploitation - 2.53MP (MathPrint) en raison de la somme sigma

#Get input from STDIN
:Ans+":"->Str0
#Initialize instruction pointer
:1->I
#Initialize variable set
:DelVar L1999->dim(L1
#Strip out those pesky non-newline/space/9 characters
:For(J,1,length(Ans
:sub(Str0,J,1
:If not(inString(": 9",Ans
:sub(Str0,1,J-1)+sub(Str0,J+1,length(Str0)-J->Str0
:End
#Main interpreting loop
:While I<length(Str0
:sub(Str0,I+1,inString(Str0,":",I+1)-I-1->Str1
:DelVar A" "=sub(Ans,1,1->A
:inString(Str0,":",I+1->I
:If A
:sub(Str1,2,length(Str1)-1->Str1
:End
:length(Str1->L
#0 is Output, 1 is Input, 2 is Assignment, 3 is Goto
:2A+inString(Str1," ->B
:If not(Ans
:Disp L1(L
:If Ans=1
:Then
:Input C
:C->L1(L
:End
#Get those delimited variables
:If B>1
:Then
:"{"+Str1->Str2
:While inString(Ans," 
:inString(Ans," 
:sub(Str2,1,Ans-1)+sub(Str2,Ans+1,length(Str2)-Ans->Str2
:End
:log(expr(Ans)+1->L2
:End
:If B=2
#Gotta expand that -+ pattern
:Ans(2->L1(Ans(1
;Love that summation Σ
:If B=3 and Σ(L2(K),K,2,dim(L2
:Then
:DelVar IFor(K,0,9L2(1
:inString(Str0,":",I+1->I
:End
:End

Les instructions PS ASCII ne peuvent pas être suivies à la lettre, mais TI-Basic :est une nouvelle ligne. Ainsi, tous les sauts de ligne réels du code signifient que le :ou #au début de chaque ligne ne sont pas nécessaires. Les jetons de début :et #juste différencient les commentaires et le code.

Décharge hexadécimale originale (376 octets)

49 3f bb 54 5d 20 39 39 39 04 b5 5d 20 3f 72 04 aa 09 3f d3 4a 2b 31 2b bb 2b 72 3f bb 0c aa 09 2b 4a 2b 31 3f ce b8 bb 0f 2a 3e 29 39 2a 2b 72 3f bb 0c aa 09 2b 31 2b 4a 71 31 11 70 bb 0c aa 09 2b 4a 70 31 2b 72 71 4a 04 aa 09 3f d4 3f d1 49 6b bb 2b aa 09 3f bb 0c aa 09 2b 49 70 31 2b bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 11 71 49 71 31 04 aa 20 3f bb 54 41 2a 29 2a 6a bb 0c 72 2b 31 2b 31 04 41 3f bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 04 49 3f ce 41 3f bb 0c aa 20 2b 32 2b bb 2b aa 20 11 71 31 04 aa 20 3f d4 3f bb 2b aa 20 04 4c 3f 32 41 70 bb 0f aa 20 2b 2a 29 04 42 3f ce b8 72 3f de 5d 20 10 4c 11 83 39 3f ce 72 6a 31 3f cf 3f dc 43 3f 39 43 04 5d 20 10 4c 3f d4 3f ce 42 6c 31 3f cf 3f 2a 08 2a 70 aa 20 04 aa 01 3f d1 bb 0f 72 2b 2a 29 3f bb 0f 72 2b 2a 29 3f bb 0c aa 01 2b 31 2b 72 71 31 11 70 bb 0c aa 01 2b 72 70 31 2b bb 2b aa 01 11 71 72 04 aa 01 3f d4 3f c0 bb 2a 72 11 70 31 04 5d 01 3f d4 3f ce 42 6a 32 3f 72 10 32 04 5d 20 10 72 10 31 3f ce 42 6a 33 40 ef 33 5d 01 10 4b 11 2b 4b 2b 32 2b b5 5d 01 3f cf 3f bb 54 49 d3 4b 2b 30 2b 5d 01 10 31 3f bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 04 49 3f d4 3f d4 2e 76

Éditer # 1 - Optimisé 3 octets en utilisant l'observation de Mac Edits # 2 & # 3 - Correction de bugs repérés par Runer112.

Timtech
la source
11
Être facile à utiliser dans des situations stressantes telles que des tests standardisés est exactement ce pour quoi j'ai conçu 99 .
Les passe-temps de Calvin le
1
Puis-je suggérer d'utiliser un caractère différent, comme #, pour les commentaires? (NB: les commentaires dans le code actuel sont implémentés sous la forme d'une ligne comportant uniquement une chaîne non fermée, qui enchaîne Ans.)
Riking
8
Avez-vous réellement essayé d'exécuter ceci? Je ne l'ai pas fait, mais en le regardant un peu plus, j'ai repéré ce qui semble être au moins une demi-douzaine d'insectes. Par exemple: les variables ne sont pas initialisées avec leurs valeurs, l' Ansentrée est écrasée, donc Ans->Str0sur la ligne 6 une erreur se produit, l'argument de longueur d'une sub()commande peut être égal à zéro, ce qui entraîne une erreur; Anssur la ligne 11, il s'agira d'une chaîne de même que Ans-Jl'erreur ... Et je n'ai regardé que la première moitié du programme.
Runer112
1
@ Timtech Cela laisse toujours les autres problèmes, cependant. Comme je l'ai mentionné, il y a le manque d'E / S de caractère, le manque d'initialisation de variable et les multiples instances dans lesquelles une sub()commande peut avoir une longueur de zéro et générer une erreur. Et une fois que les sub()invocations sont corrigées, je crains que cela ne révèle plus de problèmes.
Runer112
1
@Timtech I se référait à ceci: "Par défaut, chaque variable est affectée à sa propre représentation numérique. Ainsi, sauf si elle a été réaffectée, la valeur de la variable 9est le nombre 9 et la valeur de la variable 99est le nombre 99, etc." Et des chaînes de longueur 0 peuvent être produites de différentes manières "", mais c’est en quelque sorte un bug qu’aucune commande de manipulation de chaîne ne peut consommer ou produire une chaîne vide, y compris sub().
Runer112
5

C 426 458 481 497

Edit Peut-être que je vais trop loin, mais cela fonctionne avec Visual C: stdio.h supprimé, en utilisant int au lieu de FILE * pour fopen et getc

Éditer 2 Réordonne l'étape d'exécution, plus d'encombrement, 32 caractères sauvegardés

B[99999],*r,*i[9999],V[999],v,w,m,n;unsigned p,s;
main(b,a)char*a[];{r=i[0]=B;m=fopen(a[1],"r");
do if(w=getc(m),n+=w==57,w<33){
if(n){for(v=1,b=n;--b;)v=v*10+1;V[n]=v;*r++=p?-n:n;b=n=0;};
w-32?(*r=p=0,b=i[++s]=++r):(p=b,b=0);}while(w>=0);
while(p<s)if(w=0,r=i[p++],v=*r++)
if(m=v>0,*r){for(;b=*r++;m=-m)w=w+m*V[b]|!m*V[b];m?V[v]=w:(p=w?p:9*V[-v]);
}else~v&1?!m?V[-v]=getchar():putchar(V[v]&127):m?printf("%d",V[v]):scanf("%d",V-v);
}

Programme console autonome, nom du programme pris sur la ligne de commande et entrée / sortie via la console.

Ancien style K & R, type par défaut int pour les vars et paramètres globaux. En supposant que EOF soit défini sur -1 (comme dans chaque implémentation C que je connais)

Compile avec des avertissements avec Visual Studio 2010 (projet C ++ de la console Win32, compilation en tant que C) Compile sur Ideone, mais ne peut pas s'exécuter car il a besoin d'un fichier.

Lors de la première étape, le code source est lu et analysé, chaque ligne est stockée sous la forme d’une séquence d’entiers basée sur le nombre de 9. S'il y a un blanc au début, le premier nombre est négatif. Donc: 9 BLAH 99 9a9bb9c9( 9 99 9999) devient -1,2,4 Il existe un raccourci - pas si légal: tous les codes ascii inférieurs à '' sont considérés comme des sauts de ligne.

Dans cette étape, toutes les variables utilisées sont pré-initialisées.

L'étape d'exécution suit les spécifications, sans fioritures, enregistrez les numéros de stockage divisés par 9.

Code même plus lisible (j'espère), espaces et nouvelles lignes ajoutés

B[99999],*r,*i[9999],V[999],v,w,m,n;
unsigned p,s;
main(b,a)char*a[];
{
  r=i[0]=B;
  m=fopen(a[1],"r");
  do if(w=getc(m),n+=w==57,w<33)
  {
     if(n){for(v=1,b=n;--b;)v=v*10+1;V[n]=v;*r++=p?-n:n;b=n=0;};
     w-32?(*r=p=0,b=i[++s]=++r):(p=b,b=0);
  }
  while (w>=0);
  while (p<s)
    if (w = 0, r = i[p++], v = *r++)
        if (m = v > 0, *r){
            for(; b = *r++; m = -m)
                w = w + m*V[b] | !m*V[b];
            m ? V[v]=w : (p = w ? p : 9*V[-v]);
        } else
            ~v & 1 
            ? !m ? V[-v] = getchar() : putchar(V[v] & 127)  
            : m ? printf("%d", V[v]) : scanf("%d", V - v);
}
edc65
la source
1
Fonctionne également avec GCC 4.8.2. Compile en C99!
EMBLEM
4

Haskell, 550 octets

import Data.List.Split
import System.Environment
a#b=takeWhile(/=a)b
(!)=map
main=do(f:_)<-getArgs;readFile f>>=e.(p!).lines
p l=(if ' '#l<'9'#l then[0]else[])++length!(wordsBy(/='9')l)
e l=(\x->div(10^x-1)9)%l where
 _%[]=return()
 v%([]:r)=v%r
 v%([n]:r)=putStr(if odd n then show(v n)else[toEnum$v n`mod`128])>>v%r
 v%([0,n]:r)=do i<-getLine;u n(if odd n then read i else fromEnum$head i)v%r
 v%((0:n:m):r)|any(/=0)(v!m)=v%r|v n<0=v%[]|1<2=v%drop(9*v n)l
 v%((n:m):r)=u n(sum$zipWith(*)(v!m)(cycle[1,-1]))v%r
u n i v= \x->if x==n then i else v x

Exemple exécuté avec le programme "compte à rebours" stocké dans le fichier i.99

$ ./99 i.99
G11G10G9G8G7G6G5G4G3G2G1G

Version non-golfée:

import Data.List.Split
import System.Environment

-- The main function takes the first command line argument as a file name,
-- reads the content, splits it into lines, parses each line and evaluates
-- the list of parsed lines.
main = do
 (f:_)<-getArgs
 readFile f >>= eval.map parse.lines

-- each line is coverted into a list of integers, which represent the number
-- of 9s (e.g. "999 99 9999" -> [3,2,4]). If there's a space before the first
-- 9, a 0 is put in front of the list (e.g. " 9 9 999" -> [0,1,1,3]).
parse l = (if takeWhile (/=' ') l < takeWhile (/='9') l then [0] else [])
   ++ map length (wordsBy(/='9') l)

-- The work is done by the helper function 'go', which takes two arguments
--   a) a functions which takes an integer i and returns the value of the
--      variable with i 9s (e.g: input: 4, output: value of 9999). To be
--      exact, the value divided by 9 is returned.
--   b) a list of lines to work on
-- 'eval' starts the process with a function that returns i 1s for every i and
-- the list of the parsed input. 'go' checks which statement has to be
-- executed for the next line and calls itself recursively
eval list = go (\x -> div (10^x-1) 9) list
   where
   go _ []                  = return ()
   go v ([]:r)              = go v r
   go v ([n]:r)             = putStr (if odd n then show(v n) else [toEnum (v n`mod`128)]) >> go v r
   go v ([0,n]:r)           = do i<-getLine ; go (update n (if odd n then read i else fromEnum$head i) v) r
   go v ((0:n:m):r)
      | any (/=0) (map v m) = go v r
      | v n < 0             = go v []
      | otherwise           = go v (drop (9*v n) list)
   go v ((n:m):r)           = go (update n (sum $ zipWith (*) (map v m) (cycle[1,-1])) v) r

-- updates a function for retrieving variable values.
-- n = position to update
-- i = new value
-- v = the function to update
update n i v = \x->if x==n then i else v x
nimi
la source
4

JavaScript (ES6) 340 352

Une fonction avec 2 paramètres

  • code de programme en tant que chaîne multiligne
  • entrée en tant que tableau

Le troisième paramètre facultatif (10k par défaut) est le nombre maximal d'itérations - je n'aime pas les programmes qui fonctionnent à l'infini

JSFiddle Pour tester

I=(c,i,k=1e5,
  V=v=>v in V?V[v]:v/9 // variable getter with default initial value
)=>(c=>{
 for(p=o='';--k&&p<c[L='length'];)
   (v=(r=c[p++].split(' '))[S='shift']())? // no leading space
      r[r.map(t=>w-=(m=-m)*V(t),w=0,m=1),0]?V[v]=w // Assign
      :o+=v[L]&1?V(v):String.fromCharCode(V(v)&127) // Output
   : // else, leading space
    (v=r[S]())&&
       (r[0]?r.some(t=>V(t))?0:p=9*V(v) // Goto
       :(t=i[S](),V[v]=v[L]&1?t:t.charCodeAt()) // Input
    )
})(c.replace(/ (?=[^9])|[^9\s]/g,'').split('\n'))  // code cleaning
||o
edc65
la source
4

q / k, 490 469

M:mod;T:trim;R:read0;S:set;s:" "
f:(rtrim')(f:R -1!`$.z.x 0)inter\:"9 \n"
k)m:{@[x;&M[!#x;2];-:]}
b:{}
k)p:{1@$$[1=M[#x;2];(K x)%9;"c"$M[(K x)%9;128]];}
k)i:{S[(`$T x);$[1=M[#T x;2];9*"J"$R 0;*9*"i"$R 0]]}
k)K:{$[#!:a:`$x;.:a;"I"$x]}
k)v:{(S).(`$*:;+/m@K'1_)@\:T's\:x}
k)g:{$[&/0=C:K'c:1_J:s\:T x;n::-1+K@*J;|/~0=C;;(d<0)|(d:*C)<#f;exit 0]}
k)r:{`b`p`i`v`g@*&(&/x=s;q&1=c;(e~s)&1=C;(q:e~"9")&1<c:#s\:x;((e:*x)~s)&1<C:#s\:1_x)}
k)n:0;while[~n>#o:(r')f;(o n)f n;n+:1]
\\

.

$ q 99.q countdown.txt -q
G11G10G9G8G7G6G5G4G3G2G1G

Le script est un mélange de q et k, donc d’abord, je définis quelques mots-clés q que je veux utiliser plusieurs fois dans k fonctions. (essentiellement des macros #define)

M:mod;T:trim;R:read0;S:set

f lit le fichier transmis au programme et supprime les caractères inutiles

q)f
"99999999"
"999 99"
"9999999999 9999999999 9999999999 99 99 9 9 999 999"
"99999999999 9999999999"
""
""
""
""
""
"999"
"99999999"
"999 999 9"
" 99999 999"
" 9 99999999999 9999999999"

m prend une liste / un vecteur et multiplie les indices impairs par -1

q)m 1 2 3 4 5
1 -2 3 -4 5

b est juste une fonction vide, utilisée pour les lignes sans opération

p est la fonction d'impression.

Kest une fonction qui examine une variable. Si la variable existe, elle la renvoie, sinon elle renvoie simplement le littéral.

//999 not defined, so just return 999
q)K "999"
999
//Set 999 to 9
q)v "999 9"
//K now returns 9
q)K "999"
9

v est la fonction d'affectation.

g est la fonction goto.

r prend une chaîne et décide quelle opération doit être appliquée.

Et puis finalement, je viens de parcourir la fliste des chaînes, avec ncomme itérateur. La fonction goto sera mise nà jour si nécessaire.

tmartin
la source
3

Perl, 273 266 255 244 238

Les sauts de ligne ont été ajoutés pour plus de clarté.

open A,pop;
for(@c=<A>){
y/ 9//cd;s/ +/ /g;s/ $//;
$p="((99)+|9+)";$a='+';
s/^ $p$/$1='$2'?ord<>:<>/;
s/^$p$/print'$2'?chr$1%128:$1/;
s/^ $p /\$_=$1*011unless/&&y/ /|/;
s/ /=/;s/ /$a=-$a/ge;
s!9+!${x.$&}=$&/9;"\$x$&"!eg}
eval$c[$_++]until/-/|$_>@c

Nom du programme pris en ligne de commande:

$ perl 99.pl 99beers.99

Chaque ligne du programme est convertie en code Perl, par exemple:

print'$x99'?chr$x99999999%128:$x99999999
$x999=$x99
$x9999999999=$x9999999999-$x9999999999+$x99-$x99+$x9-$x9+$x999-$x999
$x99999999999=$x9999999999





print''?chr$x999%128:$x999
print'$x99'?chr$x99999999%128:$x99999999
$x999=$x999-$x9
$_=$x99999*011unless$x999
$_=$x9*011unless$x99999999999|$x9999999999

Plus de détails

open A,pop; # open the source file
for(@c=<A>){ # read all lines into @c and iterate over them
y/ 9//cd; # remove all but spaces and 9's
s/ +/ /g;s/ $//; # remove duplicate and trailing spaces
$p="((99)+|9+)";$a='+';
s/^ $p$/$1='$2'?ord<>:<>/; # convert input
s/^$p$/print'$2'?chr$1%128:$1/; # convert output
s/^ $p /\$_=$1*011unless/&&y/ /|/; # convert goto
s/ /=/;s/ /$a=-$a/ge; # convert assignment
s!9+!${x.$&}=$&/9;"\$x$&"!eg} # initialize and convert variables
eval$c[$_++]until/-/|$_>@c # run (program counter is in $_)
nutki
la source