Schreiben Sie einen Dolmetscher für 99

99

99 (ausgesprochen "neunundneunzig") ist eine brandneue esoterische Programmiersprache (nicht zu verwechseln mit 99 , beachten Sie die Kursivschrift). Deine Aufgabe bei dieser Herausforderung ist es, einen Dolmetscher für 99 zu schreiben , der so kurz wie möglich ist. Die Einsendung mit den wenigsten Bytes gewinnt. Tiebreaker geht zu dem Beitrag, der zuerst veröffentlicht wurde.

Da diese Frage etwas ausführlicher ist als üblich und ich auf gute Antworten gespannt bin, werde ich meiner Lieblingsantwort eine Prämie von 250 Wiederholungen gewähren (nicht unbedingt der Gewinner).

99 spez

99 ist eine imperative Sprache. Jede Zeile in einem 99- Programm ist eine einzelne Anweisung , und während der Ausführung beginnt der Befehlszeiger in der obersten Zeile und durchläuft die nachfolgenden Zeilen nacheinander, wobei er sie auf dem Weg ausführt. Das Programm endet, wenn die letzte Zeile ausgeführt wurde. Goto- Anweisungen können den Pfad des Befehlszeigers umleiten.

Zeilenumbruch, Leerzeichen und 9sind die einzigen drei Zeichen, die in einem 99- Programm eine Rolle spielen . Alle anderen Zeichen werden vollständig ignoriert. Darüber hinaus werden nachfolgende Leerzeichen in jeder Zeile ignoriert und mehrere Leerzeichen in einer Zeile werden als ein Leerzeichen gelesen. ("Newline" bezieht sich auf alle gängigen Zeilenumbruchkodierungen . Es spielt keine Rolle, welchen Ihr Interpreter verwendet.)

Also dieses Programm:

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

Ist identisch mit diesem Programm:

 9 99 9999
9 9

Variablen

Variablen in 99 haben alle Namen, 9die aneinander 9+gereiht sind ( in Regex). Beispielsweise sind 9, 99und 9999999999alle unterschiedliche Variablen. Natürlich gibt es unendlich viele (außer Speicherbeschränkungen).

Der Wert jeder Variablen ist eine vorzeichenbehaftete Ganzzahl mit beliebiger Genauigkeit . Standardmäßig ist jede Variable einer eigenen numerischen Darstellung zugeordnet. Sofern es nicht neu zugewiesen wurde, ist der Wert der Variablen 9die Zahl 9 und der Wert der Variablen 99die Zahl 99 und so weiter. Sie können sich vorstellen, dass die Variablen als reine Zahlen behandelt werden, bis sie explizit zugewiesen werden.

Ich werde Vunten auf einen beliebigen Variablennamen verweisen.
Jede Instanz Vkönnte ersetzt werden 9, 99, 999, 9999etc.

Aussagen

Es gibt fünf verschiedene Anweisungstypen in 99 . Jede Zeile in einem 99- Programm enthält genau eine Anweisung.

Bei der hier beschriebenen Syntax wird davon ausgegangen, dass alle überflüssigen Zeichen entfernt wurden, alle nachgestellten Leerzeichen entfernt wurden und alle Folgen mehrerer Leerzeichen durch einzelne Leerzeichen ersetzt wurden.

1. Keine Operation


Eine leere Zeile ist ein No-Op . Es tut nichts (außer den Befehlszeiger zu erhöhen).

2. Ausgabe

V

Eine einzelne Variable Vin einer Zeile gibt diese Variable als Standardausgabe aus.

Wenn Ves eine ungerade Zahl von 9's ( 9, 999usw.) gibt, wird der ganzzahlige Wert Vgeteilt durch 9 (in Dezimalzahl) ausgegeben.

Wenn Ves eine gerade Anzahl von 9's ( 99, 9999usw.) gibt, wird das ASCII- Zeichen mit dem Vdurch 9 geteilten Code , Mod 128, gedruckt. (Das ist (V / 9) % 128ein Wert von 0 bis 127.)

Beispiel : Das Programm

9
9999

würde drucken 1W. Die erste Zeile wird gedruckt, 1weil 9/9 1 ist. Die zweite Zeile wird gedruckt, Wweil 9999/9 1111 ist und 1111 mod 128 87 ist und 87 der Zeichencode für ist W.

Beachten Sie, dass zwischen Ausgabe-Token keine Zeilenumbrüche gedruckt werden. \nmuss für einen Zeilenumbruch explizit gedruckt werden.

3. Eingabe

 V

Eine einzelne Variable Vin einer Zeile mit einem führenden Leerzeichen nimmt die Eingabe von stdin entgegen und speichert sie in dieser Variablen.

Wenn Ves eine ungerade Anzahl von 9's gibt, kann der Benutzer eine beliebige vorzeichenbehaftete Ganzzahl eingeben und Vwird auf das 9-fache dieses Werts gesetzt.

Wenn Ves eine gerade Anzahl von 9's gibt, kann der Benutzer ein beliebiges ASCII- VZeichen eingeben und wird auf das 9-fache seines Zeichencodes gesetzt.

Beispiel : Gegeben -57und Aals Eingabe dieses Programm

 9
9
 99
99

würde ausgeben -57A. Intern hätte die Variable 9den Wert -513 und 99den Wert 585.

Ihr Interpreter kann davon ausgehen, dass die Eingaben immer syntaktisch gültig sind.

4. Abtretung

Diese Aussage kann beliebig lang sein. Es sind zwei oder mehr Variablen in einer Zeile, die durch Leerzeichen getrennt sind:

V1 V2 V3 V4 V5 ...

Dies wird der Summe aller Indizes mit geraden Indizes abzüglich der Summe der Indizes mit ungeraden Indizes (ausgenommen ) zugewiesen . Zuweisungen sind nach Wert und nicht nach Referenz.V1VVV1

Es könnte in die meisten Sprachen übersetzt werden als .V1 = V2 - V3 + V4 - V5 + ...

Wenn es also nur zwei Variablen gibt, ist dies eine normale Zuweisung:

V1 V2V1 = V2

Wenn es drei gibt, dann ist es Subtraktion:

V1 V2 V3V1 = V2 - V3

Und das +/ --Zeichen wechselt mit jeder weiteren Variablen hin und her:

V1 V2 V3 V4V1 = V2 - V3 + V4

Beispiel : Dieses Programm würde Folgendes ausgeben 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. Springen (springe wenn alles Null ist)

Diese Aussage kann auch beliebig lang sein. Es sind zwei oder mehr Variablen in einer durch Leerzeichen getrennten Zeile mit einem führenden Leerzeichen :

 V1 V2 V3 V4 V5 ...

Wenn einige der Werte ungleich Null sind, verhält sich dies wie ein No-Op. Der Anweisungszeiger wird wie gewohnt in die nächste Zeile verschoben.V1

Wenn alle der Werte außer sind Null, dann wird der Befehlszeiger bewegt Nummer Zeile . Die Zeilen sind mit einem Index von Null versehen. Wenn also Null ist, bewegt sich der Zeiger zur obersten Zeile. Das Programm wird (normalerweise ohne Fehler) beendet, wenn es negativ oder größer als der höchstmögliche Index ist (Anzahl der Zeilen minus eins).V1 V1V1V1

Beachten Sie, dass hier nicht durch 9 geteilt wurde. Und da es unmöglich ist, eine Variable als Wert zu definieren, der kein Vielfaches von 9 ist, kann nur zu Zeilennummern gesprungen werden, die ein Vielfaches von 9 sind.V1

Beispiele:

Dieses Programm wird für immer drucken 1:

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.

Dieses Programm

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

gibt die Zahlen 11 bis 1 in absteigender Reihenfolge aus, umgeben von G:

G11G10G9G8G7G6G5G4G3G2G1G

Zusätzliche Details

Der ideale Interpreter wird von der Befehlszeile aus mit dem Namen der 99- Programmdatei als Argument ausgeführt. Die E / A-Vorgänge werden auch direkt in der Befehlszeile ausgeführt.

Sie können jedoch einfach eine Interpreterfunktion schreiben, die das Programm als Zeichenfolge sowie eine Liste der Eingabe-Token (z ["-57", "A"]. B. ) aufnimmt . Die Funktion sollte die Ausgabezeichenfolge drucken oder zurückgeben.

Etwas andere Methoden zum Ausführen des Interpreters und zum Behandeln von E / A sind in Ordnung, wenn diese Optionen in Ihrer Sprache nicht möglich sind.


Bonus: Schreibe etwas Cooles in 99 und ich werde es gerne in diesen Beitrag als Beispiel setzen.


Hoffe, du hast meine 99. Herausforderung genossen ! : D

Calvins Hobbys
quelle
9
Ich habe darüber nachgedacht, mich zu erheben, aber Ihr aktueller Punktestand ist 9…
wchargin
30
@WChargin sieht aus wie jetzt müssen Sie versuchen, es 99 zu bekommen.
Trlkly
5
Sicher gibt es einen Bonus für Selbsthosting ( 99 Dolmetscher in 99 schreiben ), nein?
Gabe
5
@Gabe Eine solche Antwort würde wahrscheinlich das Kopfgeld bekommen, aber wenn das die einzige Antwort wäre, was würde der Dolmetscher interpretieren? ;)
Calvins Hobbys
1
@Optimizer es funktioniert: pastebin.com/raw.php?i=h73q58FN
Coredump

Antworten:

16

CJam, 157 Bytes

{: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}

Probieren Sie es online aus:

Erläuterung

Der Versuch, dies mit den richtigen Einrückungen und Kommentaren zu formatieren, würde wahrscheinlich ewig dauern, daher gebe ich nur eine algorithmische Zusammenfassung.

Der Code ist ein Block, CJam ist analog zu anonymen Funktionen. Der Block erwartet bei der Ausführung die Programmzeichenfolge und die Liste der Eingaben auf dem Stapel.

Die Initialisierung besteht aus drei Schritten. Zunächst wird die Eingabeliste gespeichert. Dann wird jedes Zeichen im Programm, das nicht aussagekräftig ist, entfernt und das Ergebnis in eine Liste von Zeilen aufgeteilt und gespeichert. Zuletzt wird die Variablenliste initialisiert. Diese Liste ordnet jede Variable, die nach der Länge des Namens indiziert ist, ihrem durch 9 geteilten Wert zu (eine Variable kann niemals einen Wert enthalten, der kein Vielfaches von 9 ist, und alle Operationen außer goto profitieren von dieser Änderung). Die Liste wird bis zur Länge der längsten Zeile initialisiert, die eine Obergrenze für den längsten vorhandenen Variablennamen darstellt. Es gibt auch eine gewisse implizite Initialisierung aufgrund der anfänglichen Variablenwerte: Die Zeilennummer ist 0 und der Eingabeindex ist -1.

Der Interpreter wird wie erwartet implementiert: Eine Schleife, die die nächste Zeile liest, die Zeilennummer erhöht und die Zeile ausführt, während die Zeilennummer auf eine vorhandene Zeile zeigt. Bei der Zeilenanalyse wird zunächst überprüft, ob die Zeile nicht leer ist. Anschließend werden Verzweigungen basierend darauf ausgeführt, ob die Arität 1 oder> 1 ist. Anschließend werden Verzweigungen basierend darauf ausgeführt, ob ein führendes Leerzeichen vorhanden war. Diese vier Zweige emulieren die vier Operationen (ohne No-Op) auf meist unkomplizierte Weise, obwohl sie wie alles andere aggressiv golfen. Vielleicht ist eine Optimierung der Anmerkung, dass, da eine gültige Eingabesequenz immer ein Element des vom Programm erwarteten Typs erzeugen sollte, ich es unterlassen habe, separate Eingabefälle basierend auf der Länge des Variablennamens zu erstellen. Es wird einfach angenommen, dass das aus der Eingabeliste gelesene Element vom erwarteten Typ ist.

Runer112
quelle
15
+1. Ziemlich kurz. Können Sie jetzt einen CJam-Interpreter in 99 schreiben ? ;-)
coredump
9
@ coredump * schaudert *
Runer112
Verdammt, ich kann nur 195 bekommen und habe dann die Hoffnung verloren und aufgegeben: P
Optimizer
Dies erfordert nicht das richtige Modulo, wenn negative Werte gedruckt werden. Dies kann durch Ersetzen fixiert werden 128%mit 128,=.
Martin Ender
26

Python 3, 421 414 410 404 388 395 401 Bytes

Golf gespielt:

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='')

Im Grunde genommen nur eine wörtliche Umsetzung der Spezifikation, soweit ich das beurteilen kann.

Führen Sie den Befehl über die Befehlszeile aus, indem Sie eine 99-Quellcodedatei als einziges Argument angeben (z. B. das letzte Beispiel aus dem OP):

> python3 ninetynine.py countdown.txt
G11G10G9G8G7G6G5G4G3G2G1G
>

Als zusätzlichen Bonus gibt es hier eine (ziemlich schlechte) Implementierung von "99 Flaschen" in 99 : http://pastebin.com/nczmzkFs

Mac
quelle
1
@DLosc: zu deinem ersten Punkt: Ich auch, dass elsenach einer Nummer entfernt werden konnte, aber als ich es früher versuchte, bekam ich einen Syntaxfehler. Ihre anderen Tipps werden jedoch sehr geschätzt!
Mac
3
@coredump: Wie die Spezifikation geschrieben wird, hat jede Variable immer einen Wert, der durch neun teilbar ist. Ich fand es prägnanter, zuzulassen, dass die Variablen einen beliebigen Wert annehmen und nach Bedarf nur mit neun multipliziert / dividiert werden (insbesondere in der gotoRoutine und beim Abrufen des Standardwerts der Variablen). Für einen Sprachanwender macht dies keinen Unterschied.
Mac
2
Nicht das elseSelbst, nur der Raum davor. Eg 3*n+1if n%2else n//2.
DLosc
1
@ DLosc: sorry, ich habe falsch geschrieben - ich meinte in der Tat das Leerzeichen, nicht das else. Zum Beispiel habe ich versucht , Ersatz print(w if L(e)%2 else chr(w%128))mit print(w if L(e)%2else chr(w%128))und bekam Ausnahme eine Syntax.
Mac
1
Seltsam - Ich habe es auf ideone.com getestet und es hat funktioniert, aber Sie haben Recht, es funktioniert nicht im aktuellen Python3-Interpreter (3.4.0 unter Ubuntu). In diesem Tippbeitrag wird klargestellt: Eine Zahl gefolgt von einem alphabetischen Token funktioniert im Allgemeinen, jedoch nicht für Token, die mit eoder beginnen E, und (aus den Kommentaren) auch nicht für 0orbeide.
DLosc
16

Common Lisp, 1180 857 837 836 Bytes

Ich weiß, das wird nicht gewinnen, aber ich hatte Spaß beim Golfen. Ich konnte 343 Bytes entfernen, das sind mehr als zwei 99 Interpreter, die in CJam geschrieben wurden.

Außerdem ist es ziemlich amüsant, je mehr ich versuche, es zu komprimieren, desto mehr bin ich überzeugt, dass es für Common Lisp kürzer ist , den Code zu kompilieren, als ihn im laufenden Betrieb zu interpretieren.

(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)>))))
  • lexikalische Analyse und Codegenerierung sind verschachtelt: Ich speichere die interne Darstellung nicht, sondern bearbeite jede Zeile direkt.
  • Es gibt eine einzige tagbody, die 2 Schleifen ausführt:

     (... (tagbody % ... (go %) $ ... (go $)) result)
    
  • lokale Variablen werden in deklariert &aux

  • Erzeugen Sie keinen Closure, sondern direkt den interpretierten Code
  • usw.

Ungolfed, kommentierte

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

Bei der Auswertung wird die Standardeingabe / -ausgabe verwendet, dh Standard readund princFunktionen werden verwendet. Daher kann der resultierende Code in der Befehlszeile ausführbar gemacht werden (siehe unten).

Eingaben werden bei der Ausführung von 99 Programmen nicht vollständig bereinigt : Es wird davon ausgegangen, dass der Benutzer weiß, welche Werte erwartet werden.

Der einzig mögliche Laufzeit- Overhead kann beim Springen auftreten, da wir den Wert einer Variablen auswerten und diesen Wert einer Beschriftung zuordnen müssen. Abgesehen davon muss der Dolmetscher sehr effizient sein.

Basierend auf der cleveren Beobachtung von Mac, dass wir nicht jedes Mal 9 dividieren und multiplizieren müssen, schafft es die aktuelle Version, während der Ausführung niemals 9 zu dividieren oder zu multiplizieren.

Beispiel

Wenn wir ersetzen defmacrodurch defun, sehen wir den generierten Code. Zum Beispiel:

(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

")

Hier ist der resultierende Code:

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

Bei Ausführung wird "G11G10G9G8G7G6G5G4G3G2G1G" gedruckt.

Befehlszeile

Wir können eine ausführbare Datei erstellen, indem wir einen Core sichern und die toplevelFunktion angeben . Definieren Sie eine Datei mit dem Namen, boot.lispin der Sie die ablegen defmacro, und schreiben Sie Folgendes:

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

Laufen sbcl --load boot.lispgibt die folgende Ausgabe aus:

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

Führen Sie dann das kompilierte 99- Programm aus:

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

99 Flaschen

Wenn Sie interessiert sind, finden Sie hier den kompilierten Code für das 99-Flaschen-Programm, der in Macs Antwort geschrieben ist : http://pastebin.com/ZXe839CZ (dies ist die alte Version, in der wir Lambda und hübschere Arithmetik verwenden jmpund endkennzeichnen).

Hier ist eine Ausführung mit der neuen Version, um zu beweisen, dass sie immer noch funktioniert: http://pastebin.com/raw.php?i=h73q58FN

Core-Dump
quelle
6

TI-84 Basic (Calculator Script), 376 373 377 381 Byte

Wenn es auf einem TI-84-Rechner läuft, können Sie es für einen standardisierten Test verwenden ... also ist es nützlich;)

Minimale Betriebssystemversion - 2.53MP (MathPrint) aufgrund des Summensigmas

#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

PS-ASCII-Richtlinien konnten nicht genau befolgt werden, aber in TI-Basic :steht ein Zeilenumbruch. Somit bedeuten alle tatsächlichen Zeilenumbrüche im Code, dass die :oder #am Anfang jeder Zeile nicht erforderlich sind. Die ersten Token :und #unterscheiden nur zwischen Kommentaren und Code.

Original Hex Dump (376 Bytes)

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

Edit # 1 - Optimiert 3 Bytes mit Macs Beobachtung. Edit # 2 & # 3 - Behobene Fehler, die von Runer112 entdeckt wurden.

Timtech
quelle
11
In stressigen Situationen wie standardisierten Tests einfach zu bedienen, ist genau das , wofür ich 99 entwickelt habe .
Calvins Hobbys
1
Kann ich vorschlagen, ein anderes Zeichen #für die Kommentare zu verwenden? (Anmerkung: Kommentare im aktuellen Code werden als Zeile mit nur einer nicht geschlossenen Zeichenfolge implementiert, die Ans überfordert.)
Riking
8
Haben Sie tatsächlich versucht, dies auszuführen? Ich habe es nicht getan, aber nur ein bisschen genauer betrachtet, habe ich mindestens ein halbes Dutzend Käfer entdeckt. Beispiel: Variablen werden nicht mit ihren Werten initialisiert, die AnsEingabe wird überschrieben, sodass Ans->Str0in Zeile 6 ein Fehler auftritt. In mehreren Fällen kann das Längenargument eines sub()Befehls Null sein, was zu einem Fehler führt. In AnsZeile 11 wird ein String angezeigt so Ans-Jwird fehler ... und ich habe nur über die erste hälfte des programms geschaut.
Runer112
1
@ Timtech Das lässt aber noch die anderen Probleme. Wie bereits erwähnt, fehlen die Zeichen-E / A, die Variableninitialisierung und mehrere Instanzen, in denen ein sub()Befehl die Länge Null haben und einen Fehler auslösen kann. Und sobald die sub()Beschwerden behoben sind, kann es leider weitere Probleme geben.
Runer112
1
@ Timtech Ich bezog mich darauf: "Standardmäßig ist jede Variable einer eigenen numerischen Darstellung zugeordnet. Wenn sie also nicht neu zugewiesen wurde, ist der Wert der Variablen 9die Zahl 9 und der Wert der Variablen 99die Zahl 99. und so weiter." Und Zeichenfolgen der Länge 0 können mit ähnlichen Mitteln erzeugt werden "", aber es ist eine Art Fehler, den im Grunde kein Zeichenfolgenmanipulationsbefehl verbrauchen oder eine leere Zeichenfolge erzeugen kann, einschließlich sub().
Runer112
5

C 426 458 481 497

Bearbeiten Vielleicht gehe ich zu weit, aber das funktioniert mit Visual C: stdio.h wurde entfernt, wobei int anstelle von FILE * für fopen und getc verwendet wurde

Bearbeiten 2 Ausführungsschritt neu ordnen, mehr Unordnung, 32 Zeichen gespeichert

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

Eigenständiges Konsolenprogramm, Programmname in der Befehlszeile und Eingabe / Ausgabe über die Konsole.

Alter K & R-Stil, Standardtyp int für globale Variablen und Parameter. Angenommen, EOF ist als -1 definiert (wie in jeder mir bekannten C-Implementierung).

Kompiliert mit Warnungen mit Visual Studio 2010 (Win32-Konsolen-C ++ - Projekt, kompiliert als C) Kompiliert auf Ideone, kann jedoch nicht ausgeführt werden, da es eine Datei benötigt.

Im ersten Schritt wird der Quellcode gelesen und analysiert. Jede Zeile wird als Folge von ganzen Zahlen gespeichert, basierend auf den Zahlen von 9s. Wenn ein führendes Leerzeichen vorhanden ist, ist die erste Zahl negativ. Also: 9 BLAH 99 9a9bb9c9( 9 99 9999) wird -1,2,4 Es gibt eine Abkürzung - nicht so legal: Alle ASCII-Codes, die kleiner als '' sind, werden als Zeilenumbrüche betrachtet.

In diesem Schritt werden alle verwendeten Variablen vorinitialisiert.

Der Ausführungsschritt folgt den Vorgaben, ohne Schnickschnack, und speichert Zahlen geteilt durch 9.

Mehr lesbaren gleichen Code (ich hoffe), Leerzeichen und Zeilenumbrüche hinzugefügt

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
quelle
1
Funktioniert auch mit GCC 4.8.2. Kompiliert als C99!
EMBLEM
4

Haskell, 550 Bytes

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

Beispiellauf mit dem in der Datei gespeicherten "Countdown" -Programm i.99

$ ./99 i.99
G11G10G9G8G7G6G5G4G3G2G1G

Ungolfed-Version:

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
quelle
4

JavaScript (ES6) 340 352

Eine Funktion mit 2 Parametern

  • Programmcode als mehrzeilige Zeichenfolge
  • Eingabe als Array

Der dritte optionale Parameter (Standard 10k) gibt die maximale Anzahl von Iterationen an. Ich mag kein Programm, das für immer ausgeführt wird

JSFiddle Zum testen

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
quelle
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

Das Skript ist eine Mischung aus q und k, also definiere ich zuerst ein paar q Schlüsselwörter, die ich in k Funktionen mehrmals verwenden möchte. (im Grunde #define Makros)

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

f Liest die in das Programm übergebene Datei und entfernt unnötige Zeichen

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 Nimmt eine Liste / einen Vektor und multipliziert die ungeraden Indizes mit -1

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

b ist nur eine leere Funktion, die für die No-Op-Zeilen verwendet wird

p ist die Druckfunktion.

Kist eine Funktion, die eine Variable untersucht. Wenn die Variable existiert, wird sie zurückgegeben, andernfalls wird nur das Literal zurückgegeben.

//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 ist die Zuweisungsfunktion.

g ist die goto-Funktion.

r Nimmt eine Zeichenfolge und entscheidet, welche Operation angewendet werden muss.

Und zum Schluss iteriere ich einfach durch die fListe der Zeichenfolgen mit nals Iterator. Die goto-Funktion wird nnach Bedarf aktualisiert .

tmartin
quelle
3

Perl, 273 266 255 244 238

Zeilenumbrüche zur Verdeutlichung hinzugefügt.

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

Programmname in Kommandozeile übernommen:

$ perl 99.pl 99beers.99

Jede Programmzeile wird in Perl-Code konvertiert, zum Beispiel:

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

Mehr Details

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
quelle