Verity 0.10, optimizado para el tamaño del código fuente (1944 bytes)
Originalmente leí mal la pregunta y la interpreté como un código de golf. Probablemente fue lo mejor, ya que es mucho más fácil escribir un quine con un código fuente corto que un código objeto corto bajo las restricciones de la pregunta; eso hizo la pregunta lo suficientemente fácil como para sentir que razonablemente podría producir una respuesta, y podría funcionar como un trampolín en el camino hacia una mejor respuesta. También me impulsó a usar un lenguaje de nivel superior para la entrada, lo que significa que necesitaría expresar menos en el programa en sí. No creé Verity como un lenguaje de golf para hardware (en realidad, me contrataron para crearlo hace un tiempo en un contexto completamente diferente), pero hay una gran reminiscencia (por ejemplo, es un nivel sustancialmente más alto que un HDL típico, y tiene mucho menos repetitivo; también es mucho más portátil que el HDL típico).
Estoy bastante seguro de que la solución correcta para el código de objeto corto implica almacenar los datos en algún tipo de estructura de árbol, dado que la pregunta no permite el uso de ROM de bloque, que es donde normalmente lo almacenarías en un programa práctico; Podría intentar escribir un programa que use este principio (no estoy seguro de qué idioma, tal vez Verity, tal vez Verilog; VHDL tiene demasiadas repeticiones para ser óptimo para este tipo de problema) en algún momento. Eso significaría que no necesitaría pasar cada bit del código fuente a cada bit de su "ROM creada manualmente". Sin embargo, el compilador Verity actualmente sintetiza la estructura de la salida en función de la precedencia y la asociatividad de la entrada, lo que significa que representa efectivamente el puntero de instrucción (por lo tanto, el índice de la tabla de búsqueda) en unario,
El programa en sí:
import <print>new x:=0$1296in(\p.\z.\a.new y:=(-a 5-a 1-a 1-a 2-a 4-a 2-a 3-a 2-a 6-a 2-a 0-a 3-a 0-a 4-a 4-a 7-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 6-a 7-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 4-a 3-a 2-a 7-a 5-a 7-a 0-a 6-a 4-a 4-a 1-a 6-a 2-a 6-a 1-a 7-a 6-a 6-a 5-a 1-a 2-a 2-a 0-a 5-a 0-a 0-a 4-a 2-a 6-a 5-a 0-a 0-a 6-a 3-a 6-a 5-a 0-a 0-a 5-a 0-a 6-a 5-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 5-a 3-a 2-a 7-a 5-a 7-a 0-a 5-a 5-a 5-a 1-a 4-a 4-a 3-a 1-a 5-a 5-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 4-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 5-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 4-a 7-a 3-a 6-a 2-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 6-a 3-a 3-a 5-a 1-a 7-a 2-a 6-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 6-a 3-a 1-a 5-a 3-a 7-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 5-a 7-a 5-a 7-a 4-a 6-a 5-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 5-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 4-a 1-a 7-a 7-a 6-a 3-a 7-a 4-a 2-a 0-a 4-a 3-a 6-a 2-a 6-a 3-a 7-a 4-a 2-a 0-a 5-a 4-a 6-a 0-a 7-a 2-a 0-a 1-a 4-a 5-a 3-a 4-a 4-a 4-a 4-a 3-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 3-a 7-a 4-a 2-a 0-a 4-a 4-a 6-a 5-a 6-a 3-a 7-a 5-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 5-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 1-a 5-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 7-a 2-a 7-a 1-a 5-a 1-a 4-a 2-a 3-a 7-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 6-a 6-a 1-a 5-a 1-a 5-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 0-a 5-a 1-a 4-a 4-a 3-a 4-a 4-a 4-a 4-a 6-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 0-a 5-a 0-a 0-a 0-a 1-a 6-a 5-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 2-a 0-a 0-a 1-a 4-a 7-a 4-a 7-a 1-a 6-a 2-a 6-a 1-a 7-a 3-a 6-a 3-a 7-a 0-a 6-a 1-a 5-!x)in while!x>0do(p(if z<32then z+92else z);if z==45then while!y>0do(p 97;p 32;p(48^!y$$3$$32);p 45;y:=!y>>3)else skip;x:=!x>>6))print(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
Más legible:
import <print>
new x := 0$1296 in
(\p.\z.\a.
new y := (-a 5-a 1-
# a ton of calls to a() omitted...
-a 1-a 5-!x) in
while !x>0 do (
p(if z<32 then z+92 else z);
if z==45
then while !y>0 do (
p 97;
p 32;
p(48^!y$$3$$32);
p 45;
y:=!y>>3 )
else skip;
x:=!x>>6
)
)(print)(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
La idea básica es que almacenamos todos los datos en la variable x
. (Como es habitual para un quine, tenemos una sección de código y una sección de datos; los datos codifican el texto del código y también se pueden usar para regenerar el texto de los datos). Desafortunadamente, Verity actualmente no permite las constantes se escribirán en el código fuente (usa enteros OCaml durante la compilación para representar enteros en la fuente, lo que claramente no es correcto en un lenguaje que admita tipos enteros arbitrariamente anchos) y, además, no permite que las constantes sean especificado en octal, por lo que generamos el valor de x
en tiempo de ejecución mediante llamadas repetidas a una funcióna
. Podríamos crear una función nula y llamarla repetidamente como declaraciones separadas, pero eso dificultaría identificar dónde comenzar a generar el texto de la sección de datos. Entonces, en cambio, a
devolví un número entero y utilicé la aritmética para almacenar los datos (Verity garantiza que la aritmética evalúe de izquierda a derecha). La sección de datos está codificada x
con un solo -
signo; cuando esto se encuentra en tiempo de ejecución, se expande al máximo -a 5-a 1-
, etc., mediante el uso de y
.
Inicializar y
como una copia de x
aquí es bastante sutil. Debido a que a
devuelve cero específicamente, la mayor parte de la suma es solo cero menos cero menos ... y se cancela por sí solo. Terminamos con !x
(es decir, "el valor de x
"; en Verity, como en OCaml, el nombre de una variable funciona más como un puntero que cualquier otra cosa, y debe desreferenciarlo explícitamente para obtener el valor de la variable). Las reglas de Verity para el menos unario son un poco complejas: el menos unario v
se escribe como (-v)
, por lo que se (-0-0-0-!x)
analiza como (-(0-0-0-!x))
, que es igual a !x
, y terminamos inicializando y
como una copia de x
. (También vale la pena señalar que Verity no esllamada por valor, sino que permite que las funciones y los operadores elijan el orden en que evalúan las cosas; -
evaluará el argumento izquierdo antes del argumento derecho y, en particular, si el argumento izquierdo tiene efectos secundarios, serán visibles cuando se evalúe el argumento derecho).
Cada carácter del código fuente se representa utilizando dos dígitos octales. Esto significa que el código fuente está limitado a 64 caracteres diferentes, por lo que tuve que crear mi propia página de códigos para uso interno. La salida está en ASCII, por lo que necesitaba convertir internamente; esto es para lo que (if z<32 then z+92 else z)
sirve. Aquí está el conjunto de caracteres que usé en la representación interna, en orden numérico (es decir, \
tiene el punto de código 0, ?
tiene el punto de código 63):
\]^_`abcdefghijklmnopqrstuvwxyz{ !"#$%&'()*+,-./0123456789:;<=>?
Este conjunto de caracteres nos da la mayoría de los personajes importantes para Verity. Faltan caracteres notables }
(lo que significa que no podemos crear un bloque usando {}
, pero afortunadamente todas las declaraciones son expresiones, por lo que podemos usar ()
en su lugar); y |
(es por eso que tuve que usar un OR exclusivo en lugar de inclusivo al crear el valor de x
, lo que significa que necesito inicializarlo a 0; sin embargo, necesitaba especificar qué tan grande era de todos modos). Algunos de los caracteres críticos que quería asegurar que estaban en el conjunto de caracteres eran <>
(para importaciones, también cambios), ()
(muy difícil de escribir un programa que se pueda analizar sin estos), $
(para todo lo que tenga que ver con el ancho de bits) y \
( para lambdas; teóricamente podríamos solucionar esto conlet…in
pero sería mucho más detallado).
Con el fin de acortar un poco el programa, construí abreviaturas para print
y para !x$$6$$32
(es decir, "los 6 bits inferiores de !x
, emitidos para ser utilizables en la print
biblioteca) uniéndolos temporalmente a argumentos lambda.
Finalmente, está el problema de la producción. Verity proporciona una print
biblioteca destinada a la salida de depuración. En un simulador, imprime los códigos ASCII a la salida estándar, que es perfectamente utilizable para probar el programa. En una placa de circuito físico, depende de print
que se haya escrito una biblioteca para el chip y la placa en particular que lo rodean; Hay una print
biblioteca en la distribución de Verity para una placa de evaluación a la que tuve acceso que imprime la salida en pantallas de siete segmentos. Dado que la biblioteca terminará ocupando espacio en la placa de circuito resultante, puede valer la pena usar un lenguaje diferente para una solución optimizada a este problema para que podamos generar los bits de la salida directamente en los cables.
Por cierto, este programa es O (n²) en hardware, lo que significa que es mucho peor en un simulador (sospecho que O (n⁴); sin embargo, no estoy seguro, pero fue lo suficientemente difícil de simular que parece poco probable que sea incluso cúbico , y en función de cómo reaccionó el tiempo a mis cambios mientras escribía el programa, la función parece crecer muy rápidamente). El compilador Verity necesitaba 436 pases de optimización (que es mucho, mucho más de lo que normalmente usaría) para optimizar el programa, e incluso después de eso, simular que era muy difícil para mi computadora portátil. La ejecución completa de compilación y simulación tomó el siguiente tiempo:
real 112m6.096s
user 105m25.136s
sys 0m14.080s
y alcanzó su punto máximo en 2740232 kibibytes de memoria. El programa toma un total de 213646 ciclos de reloj para ejecutarse. ¡Sin embargo, funciona!
De todos modos, esta respuesta realmente no cumple la pregunta ya que estaba optimizando para la cosa incorrecta, pero dado que todavía no hay otras respuestas, esta es la mejor por defecto (y es bueno ver cómo se vería una quine de golf en un lenguaje de hardware). Actualmente no estoy seguro de si trabajaré o no en un programa que tenga como objetivo producir una salida más optimizada en el chip. (Es probable que sea mucho más grande en términos de fuente, ya que una codificación de datos O (n) sería bastante más compleja que la que se ve aquí).