Este artículo es una reedición, con algunos extras nuevos, del que publiqué en la columna Informática recreativa en la ahora extinta web de Lola Cárdenas El rincón del programador. También fue publicada con mi permiso en programacion.com. Pido disculpas si algunos enlaces no funcionan. También pido disculpas por el uso repetido de la palabra decrementar a lo largo del texto, no reconocida en el DRAE.
Ser aficionado a la informática y no saber programar es algo que
no casa bien. Un auténtico aficionado sabrá al menos hacer pinitos en
algún lenguaje de programación, sea Java, Basic, Pascal, C o cualquier
otro. Entre los lenguajes de programación también hay modas; un cierto
lenguaje «se lleva» más que otros en ciertos momentos, lo cual es
lógico habida cuenta de que la informática evoluciona y con ella las
necesidades.
No muchos conocen, sin embargo, la inmensa variedad de
lenguajes de programación
que hay disponibles. Los nombres que ahora mismo me
vienen a la cabeza sin consultar ninguna fuente de información son
COBOL, ALGOL, APL, BASIC, RPG, Forth, Fortran, Lisp, PL/I, Logo, C,
Pascal, Perl, Python, TCL, Ada, Java, ensamblador... y he omitido
deliberadamente nombrar variantes como Visual Basic, RPG III,
Scheme (un derivado de Lisp), C++ o Delphi (un Pascal ampliado).
Cuando llegamos al ensamblador, el tema de las variantes se
convierte en locura. Cada modelo de microprocesador tiene su propio
ensamblador, y existen miles y miles de modelos: Z80, PIC-16, 8051,
8086, 6809, 68000, 6502... A veces, como en el caso del 80x86 de
Intel, hay varios, incluso multitud de dialectos: el dialecto
oficial de Intel, el dialecto oficial del proyecto GNU
(formato AT&T),
el dialecto del ensamblador gratuito
NASM...
Para liar aún más la madeja existen personas que inventan su
propio lenguaje ensamblador, el de un procesador que no existe. El
ejemplo más notable quizá sea el de
Donald Ervin Knuth,
matemático y escritor de una serie de tratados sobre computación
famosos en todo el mundo. Existen simuladores de los dos lenguajes
ensambladores que ha inventado, que corresponden a los procesadores
imaginarios llamados MIX y MMIX.
Ahí no queda todo. Hay gente que no ve razón en que el
lenguaje a inventar sea un lenguaje ensamblador; simplemente
inventan un lenguaje nuevo. Las razones son muy variadas; está
claro por ejemplo que Sun Microsystems
tenía razones poderosas para inventar el Java. Pero no todo el
mundo necesita razones poderosas; aquí vamos a tratar precisamente
sobre los lenguajes de programación que han sido inventados por puro
entretenimiento. ¿Hay gente capaz de eso? Sí, y no pocos. Incluso
existen intérpretes de la mayoría de esos
lenguajes, y en algunos casos hasta compiladores.
Uno de los primeros lenguajes esotéricos (al menos que yo tenga
referencia, pues fue creado en 1972), y quizá de los más extendidos,
es el INTERCAL.
Su diseño se fundamenta en la pretensión de crear un lenguaje
totalmente distinto a cualquier otro en todo, aunque sin olvidar
desde luego el sentido del humor. En él, tenemos que pedir por
favor la ejecución de ciertas sentencias; en lugar del conocido
«go to»
(ir a) para saltar a otra instrucción, tenemos que escribir
«come from» (venir desde) en el lugar de destino, aunque
esto es una extensión posterior a la primera especificación.
Podemos pedir que se abstenga de ejecutar ciertas sentencias,
por ejemplo:
PLEASE ABSTAIN FROM CALCULATING evita que se ejecute
la orden CALCULATE. Cuando
llegamos al capítulo sobre la precedencia de operadores se
nos explica que no la hay, porque precisamente el objetivo en el
diseño del lenguaje era que no hubiera ningún precedente.
Como resultado de esta regla, cuando necesitamos concatenar dos
operaciones juntas hay que agrupar los operandos por pares
mediante un equivalente a los paréntesis. El nombre completo del
lenguaje, según los autores, es
«Lenguaje Compilado Carente de Acrónimo Pronunciable», cuya
abreviatura es, «por razones obvias», INTERCAL. Podemos ver el
aspecto que tiene un programa escrito en INTERCAL en la
figura 1.
La función de este programa, extraído del manual de INTERCAL, es
leer dos enteros de 32 bits, tratándolos como enteros con signo
en formato de complemento a dos, y escribir su valor absoluto.
DO (5) NEXT
(5) DO FORGET #1
PLEASE WRITE IN :1
DO .1 <- 'V":1~'#32768$#0'"$#1'~#3
DO (1) NEXT
DO :1 <- "'V":1~'#65535$#0'"$#65535'
~'#0$#65535'"$"'V":1~'#0$#65535'"
$#65535'~'#0$#65535'"
DO :2 <- #1
PLEASE DO (4) NEXT
(4) DO FORGET #1
DO .1 <- "'V":1~'#65535$#0'"$":2~'#65535
$#0'"'~'#0$#65535'"$"'V":1~'#0
$#65535'"$":2~'#65535$#0'"'~'#0$#65535'"
DO (1) NEXT
DO :2 <- ":2~'#0$#65535'"
$"'":2~'#65535$#0'"$#0'~'#32767$#1'"
DO (4) NEXT
(2) DO RESUME .1
(1) PLEASE DO (2) NEXT
PLEASE FORGET #1
DO READ OUT :1
PLEASE DO .1 <- 'V"':1~:1'~#1"$#1'~#3
DO (3) NEXT
PLEASE DO (5) NEXT
(3) DO (2) NEXT
PLEASE GIVE UP
Figura 1: Programa en lenguaje INTERCAL
Los programas en INTERCAL, desde luego, no son fáciles de
escribir. De todas formas no es el lenguaje esotérico en el que
más cuesta escribir un programa. Por ejemplo, en lenguaje
Piet
es imposible escribir un programa: hay que
dibujarlo. Vemos en la figura 2 un ejemplo de un
programa en lenguaje Piet.
Figura 2: Programa en lenguaje Piet.
El programa de la figura 2 se limita
a escribir la conocida frase «Hello, world!» («¡Hola,
mundo!»). Hay que reconocer, sin embargo, que hemos hecho abuso del
significado de la palabra escribir en este contexto. Diseñar el
programa es algo que en Piet no es una tarea demasiado difícil.
Quien busque algo en lo que sea verdaderamente difícil programar,
puede intentar usar
Malbolge.
El nombre procede del Malebolge (el octavo círculo del
infierno de Dante), aunque seguramente el nombre fue recortado a causa de
una limitación del sistema operativo para el que fue originariamente
escrito (MS-DOS), que limita la longitud de los nombres a ocho caracteres.
Esta alusión al infierno puede dar una somera idea de la complicación del
lenguaje, aunque obtendremos una idea más aproximada conociendo la
dificultad que supuso para
Andrew Cooke
escribir un programa en Malbolge que simplemente escribiera
«Hello, world» (digamos que lo hizo por métodos de tipo
«ensayo y error», y ni siquiera consiguió uniformidad en las
mayúsculas y minúsculas ni signos de puntuación: el programa escribía
«HEllO WORld»). En la figura 3 vemos el programa que consiguió la hazaña.
(=<`$9]7<5YXz7wT.3,+O/o'K%$H"'~D|#z@b=`{^Lx8%$Xmrkpohm-kNi;g
sedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543s+O<oLm
Figura 3: Programa en lenguaje Malbolge.
No hay que desesperarse por no entender nada. Esa es justo la
intención que el inventor del lenguaje tenía. De hecho, el
cripticismo buscado por el autor le resta cierto interés al
lenguaje: está deliberadamente encriptado, razón por la cual Cooke
propone un «Malbolge normalizado», que es lo que queda
después de la fase de desencriptación. No hay que dejarse llevar
por la idea, sin embargo, de que esto le reste dificultad al
lenguaje. Se puede (si es que se puede) escribir un programa en
Malbolge normalizado y después aplicar la fase final de
encriptación para obtener un programa válido en Malbolge.
La figura 4 muestra el aspecto del programa «Hello world» en
Malbolge normalizado.
jpp<jp<pop<<jo*<popp<o*p<pp<pop<pop<jijoj/o<vvjpopoopo<ojo/o
vooooooooooooooooooooooooooooooooooooooooooooooooooo*p<v*<*
Figura 4: Programa en lenguaje Malbolge normalizado.
Aunque resulta algo más legible que antes, sigue siendo complicado de
descifrar sin una descripción de qué significa cada símbolo (y aun con
ella). En el fichero de la distribución de Malbolge existe una
descripción completa del lenguaje.
Pero de entre todos los lenguajes esotéricos hay uno que llama la
atención por su exquisita simplicidad y potencia. Se llama Brainfuck, que en inglés suena tan mal como en español su traducción de
Jodecerebros, con perdón. Debido a lo ofensivo del nombre, no es
tan fácil buscar referencias en WWW usando buscadores ya que hay
muchas variaciones del nombre para ocultar su carácter ofensivo:
Brainf*ck, Brainf***, Brainfsck, BF... La intención de su creador,
Urban Müller,
era la de crear un lenguaje para el cual se pudiera escribir el
compilador más corto del mundo.
A pesar del nombre, escribir un programa en Brainfuck no es tan
difícil como cabe pensar. Está demostrado que es un lenguaje
Turing-completo, lo que expresado de forma simple significa
que se puede codificar cualquier algoritmo con él. Existen
únicamente ocho instrucciones, cada una de las cuales está
asociada a un símbolo. Hay un resumen en la tabla 1. Existe un
puntero de programa, que representa la
posición en la que se está ejecutando actualmente el código,
como en cualquier otro lenguaje imperativo (con algunas salvedades),
y un puntero de datos, que es una característica propia
de Brainfuck y que sirve precisamente para manipular los datos.
El puntero de datos apunta a la posición cero de una zona de
memoria, inicialimente rellena de ceros. No se considera correcto
un programa si intenta aplicar un decremento al puntero de datos
cuando éste está en la posición cero. Usualmente se considera que
el contenido de cada posición de memoria es un byte sin signo (un
número de 0 a 255). Al incrementar uno de estos bytes cuando su
valor era 255, generalmente pasa a valer 0. Al aplicarle un
decremento cuando su valor era 0, generalmente pasa a valer 255.
Decimos «generalmente» porque según la implementación
puede haber versiones del lenguaje que adopten otros convenios.
| Instrucción |
Descripción |
Equivalente C |
- |
Restar 1 al contenido de la celda de memoria actualmente apuntada por
el puntero de datos. |
--*p; |
+ |
Sumar 1 al contenido de la celda de memoria actualmente apuntada por
el puntero de datos. |
++*p; |
< |
Decrementar el puntero de datos en una unidad. Es un error ejecutar
esta instrucción cuando el puntero de datos tiene el valor 0. |
--p; |
> |
Incrementar el puntero de datos en una unidad. |
++p; |
[ |
Si el contenido de la celda de memoria a la que apunta el puntero
de datos es cero, buscar el correspondiente «]» y colocar
el puntero de instrucción inmediatamente después. Es posible
anidarlos. No se considera
un programa válido aquél en el que no hay un balance correcto entre
los «[» y los «]». |
while(*p){ |
] |
Colocar el puntero de programa en el «[» anterior
correspondiente. |
} |
, |
Introducir un carácter desde entrada estándar y colocarlo en la
celda apuntada por el valor actual del puntero de datos. |
*p=getchar(); |
. |
Escribir el carácter ASCII correspondiente al valor de la celda
actual en la salida estándar. |
putchar(*p); |
Tabla 1: Instrucciones del lenguaje Brainfuck.
Aquellos que sepan C pueden valerse de las equivalencias aproximadas
que se dan en la tabla para entender
un poco mejor las acciones que cada instrucción realiza. Con tal sencillez
estructural, mal puede sorprendernos la facilidad con la que se puede
escribir un intérprete, y hasta un compilador de Brainfuck. Pero antes de
entrar en detalles respecto al intérprete, analizaremos un poco el lenguaje
y veremos algunas construcciones comunes.
Por ejemplo, la secuencia de instrucciones [-] se usa a
menudo en los programas Brainfuck, y sirve para poner a cero la celda de
memoria actual: equivale en C a la instrucción
«while(*p){--*p;}», es decir, mientras la celda
de memoria actual sea distinta de cero, decrementarla, lo cual obviamente
tiene como efecto ponerla a cero.
Si queremos mover el valor actual una posición a la derecha
(aquí entendemos que «izquierda» implica disminuir el
puntero de datos y «derecha» aumentarlo), utilizaremos el
siguiente programa: [>+<-]. Esto va incrementando el
byte de la derecha de la posición inicial y decrementando el actual,
hasta que éste es cero. Como resultado, al byte situado a la derecha de
la posición inicial se le suma lo que hubiera en dicha posición, que
acaba siendo cero. Suponiendo que a la derecha hubiera un cero, habremos
acabado moviendo el byte de esa posición un lugar a la derecha.
Copiar un valor es un problema más complicado; asumiendo que
las dos posiciones situadas a la derecha de la actual sean cero, se
procederá de la siguiente forma: primero, usaremos
[>+>+<<-], que tendrá el efecto de copiar el
valor en la posición actual a la posición de su derecha y a la siguiente
más a la derecha, y poner a cero la posición actual; después iremos dos
posiciones a la derecha usando >>, tras lo cual
usaremos una versión modificada del fragmento mover:
[<<+>>-], es decir, mover el byte de la
posición p (siendo p la posición del puntero de datos)
a la posición p-2. Al terminar, retrocederemos otra vez dos posiciones
usando <<, aunque dependiendo de la aplicación que
queramos darle este último paso puede ser innecesario.
Si no estamos seguros de si están a cero las dos posiciones de la
derecha de la actual, siempre podemos asegurarnos usando el siguiente
fragmento: >[-]>[-]<<. Normalmente,
sin embargo, no necesitaremos ejecutar este código, ya que estará
a cero al comenzar.
Juntando todo esto, nos damos cuenta de que si al empezar tenemos
en la posición p el valor a, en la posición p+1 el valor 0 y en la
posición p+2 el valor 0, tras ejecutar el siguiente fragmento de
código acabaremos teniendo en p el valor a, en p+1 el valor a, y en
p+2 el valor 0:
[>+>+<<-]>>[<<+>>-]<<
¿Confuso? Considerando cada parte por separado, se ve claramente
que simplemente estamos construyendo fragmentos cada vez más grandes
mediante los bloques básicos que anteriormente hemos considerado.
Multiplicar un byte por una constante es sencillo: basta con utilizar
el programa de mover, pero en lugar de incrementar una vez la variable de
la derecha, la incrementamos tantas veces como haga falta. Por ejemplo,
para multiplicar el byte actual por 7 podemos utilizar esto:
[>+++++++<-], que deja el resultado a la derecha del
byte actual, y éste a cero.
Multiplicar dos bytes entre sí requiere algo más de esfuerzo. La idea
es sumar el segundo sobre el byte a su derecha tantas veces como diga el
primero. Veamos cómo hacemos esto. Primero fijémonos en que la operación
de copiar puede ser usada también para sumar, puesto que si el valor en la
posición de la derecha de la actual no es cero, se le sumará el contenido
del byte actual. Así pues, si llamamos c a la operación
copiar, con este código resolveremos el problema: [>c<-],
es decir, ir a la derecha, sumar el byte que hay allí al siguiente, ir a la
izquierda, decrementar y continuar hasta que éste sea cero. Si se desea que
no se pierda el primer multiplicando, habrá que empezar copiándolo a un
lugar seguro.
El código completo queda así:
[>
[>+>+<<-]
>>
[<<+>>-]
<<
<-]
Veamos ahora un programa simple que imprime la
«respuesta a la Gran Pregunta de la Vida, el Universo y
Todo lo demás» (según Douglas Adams, escritor y humorista
británico fallecido hace pocos años). Dicha respuesta es 42. El
siguiente programa calcula el código ASCII del número 4 y lo
imprime, y a continuación calcula el código ASCII del número 2 y
lo imprime: +++++++[>+++++++<-]>+++.--.. El
código ASCII del dígito 4 es 52 = 7×7+3, por tanto primero
multiplicamos 7×7, y luego sumamos 3 al resultado y lo
imprimimos. Después restamos 2 a lo que quedaba, ya que el código
ASCII del dígito 2 es 50 = 52-2. Lo imprimimos, y ya lo tenemos.
Dejamos al cuidado del lector el resto del aprendizaje del
lenguaje Brainfuck, para centrarnos en una forma de elaborar un
intérprete de dicho lenguaje. Esta es una tarea realmente
sencilla, como veremos enseguida; la única parte que presenta
alguna complicación (poca, realmente) es llevar el balance de
qué «]» corresponde con un
«[» y viceversa. Precisamos de dos
memorias, compuestas por sendos arrays (o
arreglos, según algunos traductores): la de programa
y la de datos, llamadas respectivamente mp y
md. También necesitaremos dos variables:
p y d, que serán los punteros de
programa y datos, respectivamente.
He aquí un esquema del algoritmo principal del intérprete:
Leer el programa en la memoria de programa, mp,
guardando en la variable pfin la posición siguiente
a la última usada, que indicará fin de programa.
Poner a cero todos los elementos de la memoria de datos.
Hacer p = 0, d = 0.
Comienzo del bucle (1):
Si p = pfin, fin de programa: ir a la etiqueta (4).
Si la instrucción mp[p] es «-», hacer md[d] = md[d] - 1.
Si la instrucción mp[p] es «+», hacer md[d] = md[d] + 1.
Si la instrucción mp[p] es «<», hacer d = d - 1.
Si la instrucción mp[p] es «>», hacer d = d + 1.
Si la instrucción mp[p] es «,», leer un carácter y guardarlo en md[d].
Si la instrucción mp[p] es «.», escribir el carácter que hay en md[d].
Si la instrucción mp[p] es «[» y md[d] es cero, hacer lo siguiente:
Hacer nivel = 0.
Comienzo del bucle (2):
Si mp[p] es «[», hacer nivel = nivel + 1.
Si mp[p] es «]», hacer nivel = nivel - 1.
Hacer p = p + 1.
Si nivel = 0, ir al comienzo del bucle (1).
Ir al comienzo del bucle (2).
Si la instrucción mp[p] es «]», hacer lo siguiente:
Hacer nivel = 1.
Comienzo del bucle (3):
Hacer p = p - 1.
Si mp[p] es «]», hacer nivel = nivel + 1.
Si mp[p] es «[», hacer nivel = nivel - 1.
Si nivel = 0, ir al comienzo del bucle (1).
Ir al comienzo del bucle (3).
Hacer p = p + 1.
Ir al comienzo del bucle (1).
Etiqueta (4): fin de programa.
El intérprete recién descrito asume que los programas son
sintácticamente correctos, es decir, que tienen un balance
correcto de «[» y
«]» y que el programa nunca utiliza
la instrucción «<» cuando
d = 0, ni la instrucción
«>» cuando d está en
la última posición disponible de md. También
asume que el lenguaje efectúa correctamente el
«salto» de 255 a 0 al incrementar, y de 0 a 255
en los decrementos. Si esto no fuera así, habrá que poner
condiciones adicionales, convirtiéndose la operación de
incrementar en la siguiente:
Si md[d] = 255, hacer md[d] = 0; si no, hacer md[d] = md[d] + 1.
y la de decrementar, similarmente:
Si md[d] = 0, hacer md[d] = 255; si no, hacer md[d] = md[d] - 1.
Durante el desarrollo de un programa Brainfuck es fácil
equivocarse de forma que no cumpla los requisitos arriba
expuestos. Para evitar sorpresas es aconsejable incluir en el
intérprete comprobaciones en lugares estratégicos: comprobar
antes de decrementar d que éste no es cero, y
antes de incrementarlo que no está ya en el límite derecho; en
los bucles que buscan el «[» y
«]» correspondientes a su pareja, hay
que comprobar que no llegamos a p = 0 o a
p = pfin sin encontrar la pareja
correspondiente, o en caso contrario detener la ejecución. Con
esas precauciones el intérprete debería ser seguro frente a
cualquier programa Brainfuck por mal construido que esté.
Ya para terminar, una pregunta: ¿cuál es la longitud del
programa Brainfuck más corto capaz de escribir una o minúscula
(código ASCII 111) dejando el resto de la memoria de datos
con ceros?
Intérprete en JavaScript
El siguiente intérprete puede servir para probar código en
Brainfuck. Incluye entrada y salida, pero no ejecución paso a
paso ni visualización de los datos (véase la sección de enlaces
para un depurador de Brainfuck más completo en C).
Enlaces
Por ahora la mayoría de las páginas enlazadas desde aquí
están en inglés, ya que el tema parece que todavía no es muy
conocido en nuestro país. Esperemos que con el tiempo la
situación cambie.
Sobre lenguajes esotéricos en general
El lugar por excelencia donde encontrar información y referencias
sobre lenguajes esotéricos es sin duda el Esoteric Languages
Wiki:
http://www.esolangs.org/wiki/
Hay un índice de páginas apuntadas a un webring sobre
lenguajes esotéricos, que también contiene multitud de referencias:
http://b.webring.com/hub?ring=esolang
Una excelente página dedicada a los lenguajes experimentales,
mantenida por Chris Pressey:
Cat's Eye, http://catseye.tc/
Es de destacar la sección dedicada a los lenguajes de programación
esotéricos:
http://catseye.tc/projects/eso.html
Eric S. Raymond es el autor de un compilador de INTERCAL, el C-INTERCAL.
Su página llamada The Retrocomputing Museum contiene una selección
de joyas del esoterismo en programación. Brainfuck tiene ocho
instrucciones, pero ¿cuál es el mínimo número de
instrucciones que debe tener un lenguaje para ser Turing-completo? ¡Una!
Hay en esta página dos ejemplos, que llevan el concepto del RISC un paso
más lejos: OISC (One Instruction Set Computing) y URISC (Ultimate RISC).
Otra de las joyas presentes en este museo es el kvikkalkul,
un lenguaje pretendidamente utilizado en los submarinos nucleares suecos
en la década de 1950, aunque más probablemente sea una broma. El lenguaje
sólo soporta números en coma flotante sin parte entera, es decir, menores
que 1.
También destacamos el MIXAL, que es el lenguaje ensamblador del
procesador MIX de Knuth, mencionado en el texto, y un lenguaje de
programación Klingon llamado var'aq. Hay muchas otras perlas que
invitamos a descubrir.
http://www.catb.org/esr/retro/
La Enciclopedia de Lenguajes Estúpidos contiene una lista bastante
exhaustiva de lenguajes esotéricos, sus características, el nombre de su
creador, el año de creación y la página principal. Varios de los enlaces
disponibles aquí han sido obtenidos gracias a esa página.
http://www.kraml.at/stupid/
Listas de correo relacionados con lenguajes
esotéricos:
Archivo de la lista de correo de Cat's Eye
Selección de lenguajes esotéricos concretos
El lenguaje Whenever, cuyas instrucciones no
se ejecutan en un orden preestablecido sino aleatorio:
http://www.dangermouse.net/esoteric/whenever.html
Las instrucciones del lenguaje Wierd son cambios
de dirección en cadenas de símbolos. Esta es la especificación
del lenguaje:
http://catseye.tc/projects/wierd/doc/wierdspec.txt
El lenguaje Piet mencionado en el texto, cuyas
instrucciones son colores:
http://www.dangermouse.net/esoteric/piet.html
Descripción del lenguaje nihilista Sartre, creado por J. Colagioia,
otro lenguaje pensado con buen humor. Es bastante llamativa
la definición de la instrucción condicional IF.
http://catseye.tc/projects/sartre/doc/sartre.html
Sobre Brainfuck
He aquí un intérprete y depurador de Brainfuck escrito por el
autor de este artículo (en inglés), suministrado en forma de código
fuente en C. El código debería ser sumamente portable; compila
perfectamente con gcc tanto en Windows como en Linux. El
intérprete, además, realiza primero una pasada de optimización
del código para que se ejecute más rápido. El depurador contiene
funciones avanzadas que hasta ahora no he encontrado en ningún
programa similar.
brfd101.zip (11.077 bytes)
Una completa guía acerca de los bloques con los que construir
programas en Brainfuck:
http://home.planet.nl/~faase009/Ha_bf_intro.html
La demostración formal de que Brainfuck es Turing-completo:
http://home.planet.nl/~faase009/Ha_bf_Turing.html
Un intérprete de Brainfuck disponible en línea, escrito en JavaScript:
http://home.planet.nl/~faase009/Ha_bf_online.html
Propuestas de normalización del Brainfuck:
http://www.muppetlabs.com/~breadbox/bf/standards.html
y otra con cierto sentido del humor:
http://esoteric.sange.fi/ENSI/brainfuck-1.0.txt
Existen numerosos programas escritos en lenguaje Brainfuck;
entre ellos destacamos un programa para averiguar si un número es primo, varios intérpretes y compiladores de Brainfuck escritos precisamente en Brainfuck, y un programa para calcular PI, aunque éste último requiere una extensión del lenguaje que
permita que cada celda pueda almacenar números de 16 bits en vez de 8.
He aquí una biblioteca de programas escritos en Brainfuck. Destacamos
el programa SORT.BF, que lee una entrada, la ordena mediante el método de
la burbuja e imprime el resultado.
http://esoteric.sange.fi/brainfuck/bf-source/prog/
Otros enlaces indirectamente relacionados con lenguajes esotéricos
Una colección del programa Hello, World escrito en
gran variedad de lenguajes, incluyendo muchos esotéricos, entre
ellos BrainFuck e INTERCAL:
http://www.latech.edu/~acm/HelloWorld.shtml
99 Bottles of Beer es una canción popular inglesa y
estadounidense, al estilo de la canción popular española cuya letra
empieza así: Un elefante se balanceaba sobre la tela de
una araña... La versión en inglés requiere contar del 99 al
cero durante la canción. Escribir su letra es un ejercicio de
programación indudablemente más complicado que el de Hello,
World, ya que requiere un bucle finito, una cuenta atrás e
imprimir números, y por ello los programas tienden a ser más
largos pero a la vez más interesantes. He aquí una página dedicada
a los programas que imprimen la letra completa de la canción
99 Bottles of Beer
en diferentes lenguajes de programación; destacamos el escrito en
A+ ya que es capaz de imprimir las versiones europea y
americana. Por supuesto no faltan versiones Brainfuck ni INTERCAL del
programa, e incluso una en Malbolge.
http://www.99-bottles-of-beer.net/
Los quines son programas capaces de reproducirse a sí
mismos. No tienen, sin embargo, nada que ver con los virus: a
diferencia de éstos, el requisito que han de cumplir los quines
es que la salida del programa sea su propio código fuente, sin
utilizar ningún fichero externo. Pensando un poco sobre el tema
vemos enseguida que la tarea no es trivial, y que se parece al
problema del huevo o la gallina. Hay ejemplos en muchos
lenguajes, entre ellos INTERCAL y Brainfuck.
http://www.nyx.net/~gthompso/quine.htm
Lista exhaustiva de lenguajes de programación, antiguos y modernos:
http://ftp.wustl.edu/doc/misc/lang-list.txt
La Galería de los Descifradores CSS es una página creada para
defender que un programa de ordenador puede ser acogido dentro de
la ley de libertad de expresión, ya que al parecer un juez
estadounidense declaró ilegal un programa escrito en lenguaje C
que descifraba los sectores encriptados de los discos DVD, que
utilizan un sistema criptográfico denominado CSS. Hay ejemplos
en muchos otros lenguajes (entre ellos, cómo no, Brainfuck), y
otras muchas formas de expresar el programa original: versiones
recitadas del programa en formato MP3, descripciones detalladas
del algoritmo paso a paso que pueden ser utilizadas para
rehacerlo, y muchas otras divertidas maneras de convertir el
algoritmo a formas que sí están claramente protegidas por la
libertad de expresión. Dando vueltas a la tuerca, lleva el tema
a extremos que rayan el ridículo, como la existencia de números
primos ilegales.
http://www.cs.cmu.edu/~dst/DeCSS/Gallery/