El objetivo es escribir un programa completo que emule la Máquina Universal de ICFP 2006 con el código más corto. La máquina universal tiene un conjunto de instrucciones muy simple explicado aquí . El emulador tiene que leer un nombre de archivo del argumento de la línea de comandos y ejecutar el archivo como el programa, por lo que su idioma debe admitir los argumentos de la línea de comandos y stdin / out de alguna manera. El emulador tiene que completar la marca de arena dentro de un tiempo razonable (no décadas). Aquí hay una breve explicación del conjunto de instrucciones:
La máquina tiene ocho registros, cada uno con un entero sin signo de 32 bits.
La máquina contiene un conjunto indexado de matrices de celdas enteras sin signo de 32 bits.
En pocas palabras, la instrucción de asignación devuelve un uint opaco de 32 bits que es el identificador de la matriz creada, que tiene un tamaño estático, y contiene elementos uint de 32 bits.
La matriz 0 'denota el programa. Se carga desde un archivo big-endian en el inicio.
También hay un puntero de instrucción que apunta a una celda en la matriz 0.
En cada paso, se lee una instrucción de la celda a la que apunta el puntero, y el puntero se incementa antes de que se haga algo.
Los 4 bits más significativos representan el código de operación.
Si el código de operación es 13, entonces los siguientes 3 bits representan el registro, y los otros 25 representan el número que está escrito en dicho registro.
De lo contrario, los 9 bits menos significativos representan tres registros, por ejemplo, A, B y C, donde C está representado por los 3 bits menos significativos.
Luego, dependiendo del código de operación, sucede lo siguiente:
0. A = B a menos que C == 0
1. A = B [C]
2. A [B] = C
3. A = B + C
4. A = B * C
5. A = B / C
6. A = ~ (B y C)
7. El emulador sale
8. B = asignar (C)
9. desasignar (C)
10. generar un carácter de C a stdout
11. ingresar un carácter de stdin a C
12. copie la matriz B en la matriz 0 y configure el puntero en C
He escrito una implementación (ab) innecesariamente compleja pero totalmente rápida usando el ensamblaje jitted x86_64 (la diversión comienza en emit ()) , lo que seguramente te ayudará si no entiendes algunos de los aspectos de la Máquina.
fuente
Respuestas:
PHP:
443 416384 bytes* Renovado de nuevo *. Es lo más pequeño que puedo obtener ahora. Mantuve algunas variables en el extremo más alejado del alfabeto para que la expresión regular que inserta los signos $ no altere la constante STDIN, así que aquí hay un pequeño glosario:
unpack()
devuelve las matrices)La división sin signo es una molestia sutil (
*1
es necesaria para garantizar que los números grandes vuelvan al int correcto) pero el resto de la aritmética es fácil de mantener 32 bits al OR el registro aritmético con 0 (A|=0
) después de cada instrucción.Encontré este proyecto realmente interesante, pero el esfuerzo por minimizar el recuento de caracteres lo hizo lento y poco elegante, por lo que también hice una versión Java simple (no golfizada), que puede completar la marca de arena en unos minutos en lugar de tomar todo el día:
fuente
Perl, 407
Parece que la pregunta puede parecer demasiado compleja, en realidad es muy simple.
Sin embargo, todavía soy muy nuevo en Perl, de todos modos aquí está
Funciona muy lento, probablemente 800 veces más lento que el JITed x86_64.
Además, un amigo mío hizo una implementación de referencia C
fuente
if(((Memory[++PC]>>28)&15) == 13) { Registers[(Memory[PC]>>25)&7] = (Memory[PC]&0x01ffffff);
?C,
924838825696646623Almaceno un "puntero" (desplazamiento de byte) en el registro designado
b
en la instrucción, y uso cualquier registro que designe una matriz en el pseudocódigo de la misma manera (o inversa, más bien, para reconstituir un puntero) para acceder a esa matriz más tarde. Todavía necesito probar el programa de prueba ...Editar: comentarios agregados.
Edición: instrucción fija 12. cambie el puntero, no la instrucción en la memoria. El recuento se elimina con todos los comentarios, sangrías y líneas nuevas.
Editar: parece estar ejecutándose ahora, suponiendo que estoy interpretando los resultados correctamente. :) La conclusión final fue que la matriz 0 de hecho está referenciada por el identificador 0, que se puede encontrar en un registro no inicializado. ¡Una pequeña máquina muy retorcida! :)
Editar: reescribió el aparato de depuración para usar en
write
lugar deprintf
... La idea aquí es eliminar los errores. :) Editar:putchar()
ygetchar()
también son no-nos consbrk
. Ahora funciona y parece bastante rápido.Solo para little-endian, hay una versión de 611 caracteres.
Sangrado y comentarios, con aparato de depuración comentado (extendido).
fuente
lbreak
y cómo puedes unary-*
anint
d000108f c0000030
y luego sale