Escribe un intérprete para 99

99

99 (pronunciado "noventa y nueve") es un nuevo lenguaje de programación esotérico (que no debe confundirse con 99 , tenga en cuenta las cursivas). Su tarea en este desafío es escribir un intérprete para 99 que sea lo más breve posible. El envío con la menor cantidad de bytes gana. Tiebreaker va a la presentación publicada primero.

Dado que esta pregunta es un poco más profunda de lo habitual, y estoy ansioso por ver buenas respuestas, otorgaré una recompensa de 250 repeticiones a mi respuesta favorita (no necesariamente el ganador).

99 Especificaciones

99 es un lenguaje imperativo . Cada línea en un programa 99 es una declaración única , y durante la ejecución, el puntero de instrucción comienza en la línea superior y pasa por cada una de las líneas posteriores en orden, ejecutándolas en el camino. El programa finaliza cuando se ejecuta la última línea. Las instrucciones Goto pueden redirigir la ruta del puntero de instrucción.

Newline, space y 9son los únicos tres caracteres que importan en un programa 99 . Todos los demás personajes son completamente ignorados. Además, los espacios finales en cada línea se ignoran y se leen múltiples espacios seguidos como un solo espacio. ("Nueva línea" se refiere a cualquier codificación de salto de línea común . No importa cuál use su intérprete).

Entonces este programa:

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

Es idéntico a este programa:

 9 99 9999
9 9

Variables

Todas las variables en 99 tienen nombres que son uno o más 9encadenados ( 9+en expresiones regulares). Por ejemplo, 9, 99, y 9999999999son todas las variables distintas. Naturalmente, hay infinitos (salvo limitaciones de memoria).

El valor de cada variable es un entero de precisión arbitrario con signo. Por defecto, cada variable se asigna a su propia representación numérica. Entonces, a menos que haya sido reasignado, el valor de la variable 9es el número 9, y el valor de la variable 99es el número 99, y así sucesivamente. Se podría considerar que trata las variables como números simples hasta que se asignan explícitamente.

Usaré Vpara referirme a un nombre de variable arbitrario a continuación.
Cada instancia de Vpodría ser reemplazado con 9, 99, 999, 9999, etc.

Declaraciones

Hay cinco tipos de declaraciones diferentes en 99 . Cada línea en un programa 99 contiene exactamente una declaración.

La sintaxis descrita aquí supone que todos los caracteres extraños se han eliminado, todos los espacios finales se han eliminado y todas las secuencias de espacios múltiples se han reemplazado por espacios individuales.

1. Sin operación


Una línea vacía es un no-op . No hace nada (además de incrementar el puntero de instrucción).

2. Salida

V

Una sola variable Ven una línea imprime esa variable en stdout.

Si Vtiene un número impar de 9's ( 9, 999, etc.), entonces el valor entero de Vdividido por 9 será impreso (en decimal).

Si Vtiene un número par de 9's ( 99, 9999, etc.) entonces el ASCII carácter con código Vdividido por 9, mod 128 será impreso. (Es decir (V / 9) % 128, un valor de 0 a 127.)

Ejemplo : el programa

9
9999

imprimiría 1W. La primera línea se imprime 1porque 9/9 es 1. La segunda línea se imprime Wporque 9999/9 es 1111, y 1111 mod 128 es 87, y 87 es el código de caracteres para W.

Tenga en cuenta que los saltos de línea no se imprimen entre los tokens de salida. \ndebe imprimirse explícitamente para un salto de línea.

3. Entrada

 V

Una sola variable Ven una línea con un espacio inicial toma la entrada de stdin y la almacena en esa variable.

Si Vtiene un número impar de 9's, entonces el usuario puede escribir cualquier número entero con signo, y Vse establecerá en 9 veces ese valor.

Si Vtiene un número par de 9's, entonces el usuario puede escribir cualquier carácter ASCII, y Vse establecerá en 9 veces su código de caracteres.

Ejemplo : dado -57y Acomo entrada, este programa

 9
9
 99
99

haría de salida -57A. Internamente, la variable 9tendría el valor -513 y 99tendría el valor 585.

Su intérprete puede suponer que las entradas son siempre sintácticamente válidas.

4. Asignación

Esta declaración puede ser arbitrariamente larga. Son dos o más variables en una línea, separadas por espacios:

V1 V2 V3 V4 V5 ...

Esto se asigna a la suma de todos los 's con índices pares, menos la suma de los ' s con índices impares (excluyendo ). Las asignaciones son por valor, no por referencia.V1VVV1

Se podría traducir en la mayoría de los idiomas como .V1 = V2 - V3 + V4 - V5 + ...

Entonces, si solo hay dos variables, es una asignación normal:

V1 V2V1 = V2

Si hay tres, entonces es resta:

V1 V2 V3V1 = V2 - V3

Y el signo +/ -sigue cambiando de un lado a otro con cada variable adicional:

V1 V2 V3 V4V1 = V2 - V3 + V4

Ejemplo : este programa generaría 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. Ir a (saltar si todo es cero)

Esta declaración también puede ser arbitrariamente larga. Son dos o más variables en una línea, separadas por espacios, con un espacio inicial :

 V1 V2 V3 V4 V5 ...

Si algunos de los valores además no son cero, entonces esto se comporta como un no-op. El puntero de instrucciones se mueve a la siguiente línea como de costumbre.V1

Si todos los valores además son cero, entonces el puntero de instrucción se mueve al número de línea . Las líneas están indexadas a cero, por lo que si es cero, el puntero se mueve a la línea superior. El programa finaliza (normalmente, sin error) si es negativo o es mayor que el índice más alto posible (número de líneas menos uno).V1 V1V1V1

Tenga en cuenta que no se dividió por 9 aquí. Y dado que es imposible que una variable sea un valor que no sea múltiplo de 9, solo se puede saltar a los números de línea que son múltiplos de 9.V1

Ejemplos:

Este programa imprimirá 1para siempre:

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.

Este programa

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

mostrará los números del 11 al 1, en orden decreciente, rodeados por G's:

G11G10G9G8G7G6G5G4G3G2G1G

Detalles adicionales

El intérprete ideal se ejecutará desde la línea de comandos con el nombre del archivo del programa 99 como argumento. La E / S también se realizará sobre la marcha en la línea de comando.

Sin embargo, puede escribir una función de intérprete que tome el programa como una cadena, así como una lista de los tokens de entrada (por ejemplo ["-57", "A"]). La función debe imprimir o devolver la cadena de salida.

Formas ligeramente diferentes de ejecutar el intérprete y manejar la E / S están bien si estas opciones son imposibles en su idioma.


Bonificación: escriba algo genial en 99 y con gusto lo pondré en esta publicación como ejemplo.


¡Espero que hayas disfrutado de mi 99º desafío! :RE

Pasatiempos de Calvin
fuente
99
Pensé en votar, pero su puntaje actual es 9 ...
wchargin
30
@WChargin parece que ahora tendrás que intentar conseguirlo 99.
Trlyly
55
Seguramente hay un bono para autoalojamiento (escribiendo un 99 intérprete de 99 ), no?
Gabe
55
@Gabe Una respuesta como esa probablemente obtendría la recompensa, pero si esa fuera la única respuesta, ¿qué interpretaría el intérprete? ;)
Calvin's Hobbies
1
@Optimizer funciona: pastebin.com/raw.php?i=h73q58FN
coredump

Respuestas:

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}

Pruébalo en línea:

Explicación

Intentar formatear esto con la sangría adecuada y los comentarios probablemente tomaría una eternidad, por lo que solo daré un resumen algorítmico.

El código es un bloque, las funciones análogas a anónimas de CJam. El bloque espera la cadena del programa y la lista de entradas en la pila cuando se ejecuta.

La inicialización consta de tres pasos. Primero, se guarda la lista de entrada. Luego, se eliminan todos los caracteres del programa que no tienen sentido y el resultado se divide en una lista de líneas y se guarda. Finalmente, se inicializa la lista de variables. Esta lista asigna cada variable, indexada por la longitud del nombre, a su valor dividido entre 9 (una variable nunca puede contener un valor que no sea un múltiplo de 9, y todas las operaciones excepto goto se benefician de este cambio). La lista se inicializa hasta la longitud de la línea más larga, que es un límite superior en el nombre varaible más largo presente. También hay un poco de inicialización implícita debido a los valores de las variables iniciales: el número de línea es 0 y el índice de entrada es -1.

El intérprete se implementa como cabría esperar: un bucle que lee la siguiente línea, incrementa el número de línea y ejecuta la línea mientras el número de línea apunta a una línea existente. El análisis de líneas primero verifica que la línea no esté vacía, luego las ramas en función de si el arity es 1 o> 1, luego las ramas en función de si había un espacio inicial. Estas cuatro ramas emulan las cuatro operaciones (excluyendo no-op) de una manera principalmente directa, aunque de forma agresiva como cualquier otra cosa. Quizás una optimización de la nota es que, dado que una secuencia de entrada válida siempre debe producir un elemento de tipo esperado por el programa, omití hacer casos separados para la entrada en función de la longitud del nombre de la variable. Simplemente se supone que el elemento leído de la lista de entrada es del tipo esperado.

Runer112
fuente
15
+1. Bastante corto. Ahora, ¿puedes escribir un intérprete de CJam en 99 ? ;-)
coredump
99
@coredump * estremecimientos *
Runer112
Maldición, solo puedo obtener 195 y luego perdí la esperanza y me di por vencido: P
Optimizer
Esto no toma el módulo correcto al imprimir valores negativos. Esto se puede solucionar reemplazando 128%con 128,=.
Martin Ender
26

Python 3, 421 414 410 404 388 395 401 bytes

Golfizado:

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

Sin golf:

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

Prácticamente solo una implementación literal de la especificación, desarrollada hasta donde puedo obtenerla.

Ejecute desde la línea de comando proporcionando un archivo de código fuente 99 como único argumento (por ejemplo, el último ejemplo del OP):

> python3 ninetynine.py countdown.txt
G11G10G9G8G7G6G5G4G3G2G1G
>

Como beneficio adicional, aquí hay una implementación (bastante pobre) de "99 botellas" en 99 : http://pastebin.com/nczmzkFs

Mac
fuente
1
@DLosc: con respecto a su primer punto: yo también pensé que elsedespués de que se pudiera eliminar un número, pero cuando lo intenté antes recibí un error de sintaxis. ¡Tus otros consejos son muy apreciados!
Mac
3
@coredump: la forma en que se escribe la especificación, cada variable siempre tendrá un valor que es divisible por nueve. Me pareció más conciso permitir que las variables adquieran cualquier valor y solo multiplicar / dividir por nueve según sea necesario (en particular, en la gotorutina y al obtener el valor predeterminado de la variable). En lo que respecta a un usuario del idioma, no hay diferencia.
Mac
2
No el elsesí mismo, solo el espacio anterior. Por ej 3*n+1if n%2else n//2.
DLosc
1
@DLosc: lo siento, hablé mal, de hecho me refería al espacio, no al else. Por ejemplo, he intentado reemplazar print(w if L(e)%2 else chr(w%128))con print(w if L(e)%2else chr(w%128))y obtuve una excepción de sintaxis.
Mac
1
Extraño: probé en ideone.com y funcionó, pero tienes razón, no funciona en el intérprete real de Python3 (3.4.0 en Ubuntu). Esta publicación de consejos aclara: un número seguido de un token alfabético funciona en general, pero no para los tokens que comienzan con eo E, y (de los comentarios) no para 0orninguno.
DLosc
16

Lisp común, 1180 857 837 836 bytes

Sé que esto no va a ganar, pero me divertí jugando al golf. Logré eliminar 343 bytes, que son más de dos 99 intérpretes escritos en CJam.

Además, de manera bastante divertida, cuanto más intento comprimirlo, más me convencen de que para Common Lisp, es más corto compilar el código que intentar interpretarlo sobre la marcha.

(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)>))))
  • El análisis léxico y la generación de código están intercalados: no almaceno la representación interna, sino que proceso directamente cada línea.
  • Hay un solo tagbodypara realizar 2 bucles:

     (... (tagbody % ... (go %) $ ... (go $)) result)
    
  • las variables locales se declaran en &aux

  • no genere un cierre, sino directamente el código interpretado
  • etc.

Ungolfed, comentó

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

Usamos entrada / salida estándar durante la evaluación, lo que significa que usamos estándar readyprinc funciones. Por lo tanto, el código resultante puede hacerse ejecutable en la línea de comandos, como se muestra a continuación.

Las entradas no se desinfectan completamente cuando se ejecutan 99 programas: se supone que el usuario sabe qué tipo de valores se esperan.

El único tiempo de ejecución posible sobrecarga de de puede ocurrir al saltar, ya que debemos evaluar el valor de una variable y hacer coincidir ese valor con una etiqueta. Excepto que, el intérprete será bastante eficiente.

Basado en la observación inteligente de Mac que no necesitamos dividir y multiplicar por 9 cada vez, la versión actual se las arregla para nunca dividir ni multiplicar por 9 durante la ejecución.

Ejemplo

Si reemplazamos defmacropor defun, vemos el código generado. Por ejemplo:

(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

")

Aquí está el código resultante:

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

Cuando se ejecuta, imprime "G11G10G9G8G7G6G5G4G3G2G1G"

Línea de comando

Podemos construir un ejecutable volcando un núcleo y especificando la toplevelfunción. Defina un archivo llamado boot.lispdonde coloca el defmacro, y luego escriba lo siguiente:

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

Correr sbcl --load boot.lispda el siguiente resultado:

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

Luego, ejecutando el programa compilado 99 :

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

99 botellas

Si está interesado, aquí está el código compilado para el programa de 99 botellas escrito en la respuesta de Mac : http://pastebin.com/ZXe839CZ (esta es la versión anterior donde tenemos jmpy endetiquetas, una lambda circundante y una aritmética más bonita).

Aquí hay una ejecución con la nueva versión, para demostrar que aún funciona: http://pastebin.com/raw.php?i=h73q58FN

volcado de memoria
fuente
6

TI-84 básico (Simulador de escritura), 376 373 377 381 bytes

Si se ejecuta en una calculadora TI-84, podrá usarla en una prueba estandarizada ... así que es útil;)

Versión mínima del sistema operativo: 2.53MP (MathPrint) debido a la suma 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

Las directrices PS ASCII no se pudieron seguir exactamente, pero en TI-Basic :es una nueva línea. Por lo tanto, todas las nuevas líneas reales en el código significan que no se requiere el :o #al comienzo de cada línea. Los tokens iniciales :y #solo diferencian entre comentarios y código.

Volcado hexadecimal original (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

Edición n. ° 1 : 3 bytes optimizados mediante la observación de Mac. Ediciones n. ° 2 y n. ° 3 : errores corregidos detectados por Runer112.

Timtech
fuente
11
Ser fácil de usar en situaciones estresantes como pruebas estandarizadas es exactamente para lo que diseñé 99 .
Calvin's Hobbies
1
¿Puedo sugerir usar un carácter diferente, como #, para los comentarios? (Nota: Los comentarios en el código real se implementan como una línea única con una cadena sin cerrar, que clobbers Ans)
Riking
8
¿Realmente has intentado ejecutar esto? No lo he hecho, pero solo al mirarlo un poco más, he descubierto lo que parecen ser al menos media docena de errores. Por ejemplo: las variables no se inicializan con sus valores, la Ansentrada se sobrescribe, por lo que Ans->Str0en la línea 6 se producirá un error, hay varias instancias en las que el argumento de longitud de un sub()comando puede ser cero, lo que resulta en un error, Ansen la línea 11 será una cadena también lo Ans-Jhará el error ... Y solo miré aproximadamente la primera mitad del programa.
Runer112
1
@Timtech Eso todavía deja los otros problemas, sin embargo. Como mencioné, existe la falta de E / S de caracteres, falta de inicialización variable y múltiples instancias en las que un sub()comando puede tener longitud cero y arrojar un error. Y una vez que sub()se solucionan las invocaciones, me temo que puede revelar más problemas.
Runer112
1
@Timtech me refería a esto: "Por defecto, cada variable se asigna a su propia representación numérica. Entonces, a menos que se haya reasignado, el valor de la variable 9es el número 9, y el valor de la variable 99es el número 99, y así." Y las cadenas de longitud 0 pueden producirse por medios como "", pero es una especie de error que básicamente ningún comando de manipulación de cadenas puede consumir o producir una cadena vacía, incluida sub().
Runer112
5

C 426 458 481 497

Editar Tal vez estoy yendo demasiado lejos, pero esto funciona con Visual C: eliminó stdio.h, usando int en lugar de FILE * para fopen y getc

Editar 2 Reordenar paso de ejecución, más desorden, 32 caracteres guardados

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

Programa de consola independiente, nombre del programa tomado en la línea de comando y entrada / salida a través de la consola.

Estilo antiguo K&R, tipo predeterminado int para variables y parámetros globales. Asumiendo EOF definido como -1 (como lo estoy en cada implementación de C que conozco)

Compila con advertencias con Visual Studio 2010 (proyecto Win32 consola C ++, compila como C) Compila en Ideone, pero no puede ejecutarse ya que necesita un archivo.

Primer paso, el código fuente se lee y analiza, cada línea se almacena como una secuencia de enteros basada en los números de 9s. Si hay un espacio en blanco inicial, el primer número es negativo. Entonces: 9 BLAH 99 9a9bb9c9( 9 99 9999) se convierte en -1,2,4 Hay un acceso directo, no tan legal: todos los códigos ascii menores que '' se consideran líneas nuevas.

En este paso, todas las variables utilizadas se preinicializan.

El paso de ejecución sigue las especificaciones, sin lujos, guarda los números de almacenamiento divididos por 9.

Mismo código más legible (espero), espacios y líneas nuevas añadidas

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
fuente
1
También funciona con GCC 4.8.2. Compila como C99!
EMBLEMA
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

Ejemplo ejecutado con el programa "cuenta regresiva" almacenado en el archivo i.99

$ ./99 i.99
G11G10G9G8G7G6G5G4G3G2G1G

Versión sin golf:

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

JavaScript (ES6) 340 352

Una función con 2 parámetros.

  • código de programa como una cadena multilínea
  • entrada como una matriz

El tercer parámetro opcional (predeterminado 10k) es el número máximo de iteraciones: no me gusta un programa que se ejecute para siempre

JSFiddle para probar

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

El script es una mezcla de q y k, así que primero defino algunas palabras clave q que quiero usar varias veces en k funciones. (básicamente #define macros)

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

f lee el archivo pasado al programa y elimina los caracteres innecesarios

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 toma una lista / vector y multiplica los índices impares por -1

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

b es solo una función vacía, utilizada para las líneas no operativas

p Es la función de impresión.

Kes una función que examina una variable. Si la variable existe, la devuelve, de lo contrario solo devuelve el literal.

//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 es la función de asignación

g es la función goto.

r toma una cadena y decide qué operación debe aplicarse.

Y finalmente, solo itero a través de la flista de cadenas, ncomo iterador. La función goto se actualizará nsegún sea necesario.

tmartin
fuente
3

Perl, 273 266 255 244 238

Saltos de línea añadidos para mayor claridad.

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

Nombre del programa tomado en la línea de comando:

$ perl 99.pl 99beers.99

Cada línea del programa se convierte en código Perl, por ejemplo:

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

Más detalles

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
fuente