Brainfuck es un lenguaje de programación esotérico creado en 1993 por el estudiante de física suizo Urban Müller. [1] Diseñado para ser extremadamente minimalista, el lenguaje consta de solo ocho comandos simples, un puntero de datos y un puntero de instrucciones . [2]
Brainfuck es un ejemplo de lo que se denomina un tarpit de Turing : se puede utilizar para escribir cualquier programa, pero no es práctico hacerlo, porque proporciona tan poca abstracción que los programas se vuelven muy largos o complicados. Si bien Brainfuck es completamente Turing completo , no está destinado a un uso práctico, sino a desafiar y divertir a los programadores . [3] [4] Brainfuck requiere que uno divida los comandos en pasos microscópicos.
El lenguaje toma su nombre del término del argot brainfuck , que se refiere a cosas tan complicadas o inusuales que exceden los límites de la comprensión, ya que no fue pensado ni creado para diseñar software real sino para desafiar los límites de la programación informática .
Debido a que el nombre del lenguaje contiene blasfemias , se utilizan muchos sustitutos, como brainfsck, branflakes, brainoof, brainfrick, BrainF y BF. [5]
Müller diseñó Brainfuck con el objetivo de implementar el compilador más pequeño posible , [6] inspirado en el compilador de 1024 bytes para el lenguaje de programación FALSE . [7] El compilador original de Müller se implementó en lenguaje de máquina y se compiló en un binario con un tamaño de 296 bytes. Subió el primer compilador Brainfuck a Aminet en 1993. El programa venía con un archivo "Readme" , que describía brevemente el lenguaje y desafiaba al lector "¿Quién puede programar algo útil con él? :)". Müller también incluyó un intérprete y algunos ejemplos. Una segunda versión del compilador utilizó solo 240 bytes. [8]
El lenguaje consta de ocho comandos . Un programa de brainfuck es una secuencia de estos comandos, posiblemente intercalados con otros caracteres (que se ignoran). Los comandos se ejecutan secuencialmente, con algunas excepciones: un puntero de instrucción comienza en el primer comando y cada comando al que apunta se ejecuta, después de lo cual normalmente avanza al siguiente comando. El programa termina cuando el puntero de instrucción se mueve más allá del último comando.
El lenguaje brainfuck utiliza un modelo de máquina simple que consiste en el programa y el puntero de instrucción, así como una matriz unidimensional de al menos 30.000 celdas de bytes inicializadas a cero; un puntero de datos móvil (inicializado para apuntar al byte más a la izquierda de la matriz); y dos flujos de bytes para entrada y salida (generalmente conectados a un teclado y un monitor respectivamente, y utilizando la codificación de caracteres ASCII ).
Los ocho comandos de idioma constan cada uno de un solo carácter:
[
y ]
coinciden como suelen hacerlo los paréntesis: cada uno [
coincide exactamente con uno ]
y viceversa, el [
va primero y no puede haber ningún número [
o número que no coincida ]
entre los dos.
Los programas Brainfuck suelen ser difíciles de comprender. Esto se debe en parte a que cualquier tarea medianamente compleja requiere una larga secuencia de comandos y en parte a que el texto del programa no da indicaciones directas del estado del programa . Estas, así como la ineficiencia de Brainfuck y sus limitadas capacidades de entrada/salida, son algunas de las razones por las que no se utiliza para la programación seria. No obstante, como cualquier lenguaje completo de Turing, Brainfuck es teóricamente capaz de calcular cualquier función computable o simular cualquier otro modelo computable, si se le da acceso a una cantidad ilimitada de memoria. [9] Se han escrito una variedad de programas Brainfuck. [10] Aunque los programas Brainfuck, especialmente los complicados, son difíciles de escribir, es bastante trivial escribir un intérprete para Brainfuck en un lenguaje más típico como C debido a su simplicidad. También existen intérpretes Brainfuck escritos en el propio lenguaje Brainfuck. [11] [12]
Como primer ejemplo simple, el siguiente fragmento de código sumará el valor de la celda actual a la celda siguiente: cada vez que se ejecuta el bucle, la celda actual disminuye, el puntero de datos se mueve hacia la derecha, la celda siguiente se incrementa y el puntero de datos se mueve nuevamente hacia la izquierda. Esta secuencia se repite hasta que la celda inicial sea 0.
[ - > + < ]
Esto se puede incorporar a un programa de suma simple de la siguiente manera:
++ Celda c0 = 2 > +++++ Celda c1 = 5 [ Comience sus bucles con el puntero de su celda en el contador de bucles (c1 en nuestro caso) < + Sumar 1 a c0 > - Restar 1 de c1 ] Finalice sus bucles con el puntero de su celda en el contador de bucles En este punto, nuestro programa ha sumado 5 a 2, dejando 7 en c0 y 0 en c1, pero no podemos enviar este valor a la terminal ya que no está codificado en ASCII.Para mostrar el carácter ASCII "7" debemos sumar 48 al valor 7 Usamos un bucle para calcular 48 = 6 * 8++++ ++++ c1 = 8 y este será nuestro contador de bucle nuevamente [ < +++ +++ Suma 6 a c0 > - Resta 1 de c1 ] < . ¡Imprime c0 que tiene el valor 55 que se traduce a "7"!
El siguiente programa imprime "¡Hola mundo!" y una nueva línea en la pantalla:
[ Este programa imprime "¡Hola mundo!" y una nueva línea en la pantalla; su longitud es de 106 caracteres de comando activos . [ No es el más corto . ] Este bucle es un "bucle de comentario inicial" , una forma sencilla de agregar un comentario a un programa BF de modo que no tenga que preocuparse por ningún carácter de comando . Cualquier carácter " . " , " , " , " + " , " - " , " < " y " > " simplemente se ignoran , los caracteres " [ " y " ] " solo tienen que equilibrarse . Este bucle y los comandos que contiene se ignoran porque la celda actual tiene un valor predeterminado de 0; el valor 0 hace que se omita este bucle . ] ++++++++ Establecer la celda n.º 0 en 8 [ > ++++ Agregar 4 a la celda n.º 1; esto siempre establecerá la celda n.º 1 en 4 [ ya que el bucle borrará la celda > ++ Agregar 2 a la celda n.º 2 > +++ Agregar 3 a la celda n.º 3 > +++ Agregar 3 a la celda n.º 4 > + Agregar 1 a la celda n.º 5 <<<< - Decrementar el contador del bucle en la celda n.º 1 ] Bucle hasta que la celda n.º 1 sea cero; el número de iteraciones es 4 > + Agregar 1 a la celda n.° 2 > + Agregar 1 a la celda n.° 3 > - Restar 1 de la celda n.° 4 >> + Agregar 1 a la celda n.° 6 [ < ] Volver a la primera celda cero que encuentre; será la celda n.° 1 que fue borrada por el bucle anterior < - Decrementar el contador del bucle en la celda n.° 0 ] Repetir hasta que la celda n.° 0 sea cero; el número de iteraciones es 8 El resultado de esto es: Número de celda: 0 1 2 3 4 5 6 Contenido: 0 0 72 104 88 32 8 Puntero: ^>> . La celda n.° 2 tiene el valor 72, que es 'H' > --- . Resta 3 de la celda n.° 3 para obtener 101, que es 'e' +++++++ .. +++ . Lo mismo para 'llo' de la celda n.° 3 >> . La celda n.° 5 tiene el valor 32 por el espacio < - . Resta 1 de la celda n.° 4 por 87 para obtener una 'W' < . La celda n.° 3 se estableció en 'o' desde el final de 'Hello' +++ . ------ . -------- . Celda n.° 3 para 'rl' y 'd' >> + . Suma 1 a la celda n.° 5 para obtener un signo de exclamación > ++ . Y, por último, una nueva línea de la celda n.° 6
Para facilitar su lectura, este código se ha distribuido en varias líneas y se han añadido espacios en blanco y comentarios. Brainfuck ignora todos los caracteres excepto los ocho comandos, +-<>[],.
por lo que no se necesita ninguna sintaxis especial para los comentarios (siempre que los comentarios no contengan los caracteres de los comandos). El código podría haberse escrito así:
++++++++ [ > ++++ [ > ++ > +++ > +++ > + <<<< - ] > + > + > - >> + [ < ] < - ] > > . > --- . +++++++ .. +++ . >> . < - . < . +++ . ------ . -------- . >> + . > ++ .
Este programa cifra su entrada con el cifrado ROT13 . Para ello, debe asignar los caracteres AM ( ASCII 65–77) a NZ (78-90), y viceversa. También debe asignar am (97-109) a nz (110-122) y viceversa. Debe asignar todos los demás caracteres a sí mismos; lee los caracteres uno a la vez y genera sus equivalentes cifrados hasta que lee un EOF (que aquí se supone que se representa como -1 o "sin cambios"), momento en el que el programa finaliza.
- , + [ Leer el primer carácter e iniciar el bucle de lectura de caracteres externos - [ Saltar hacia adelante si el carácter es 0 >> ++++ [ > ++++++++ < - ] Configurar divisor (32) para bucle de división (DISPOSICIÓN DE MEMORIA: dividendo copia resto divisor cociente cero cero) < + < - [ Configurar dividendo (x menos 1) e ingresar al bucle de división > + > + > - [ >>> ] Aumentar copia y resto / reducir divisor / Caso normal: saltar hacia adelante < [[ > + < - ] >> + > ] Caso especial: mover resto de regreso al divisor y aumentar cociente <<<<< - Decrementar dividendo ] Fin del bucle de división ] >>> [ - ] + Fin del bucle de salto; poner a cero el divisor anterior y reutilizar el espacio para una bandera > -- [ - [ < - > +++ [ - ]]] < [ Poner a cero esa bandera a menos que el cociente fuera 2 o 3; cociente a cero; comprobar bandera ++++++++++++ < [ Si bandera entonces configurar divisor (13) para segundo bucle de división (DISEÑO DE MEMORIA: cero copia dividendo divisor resto cociente cero cero) > - [ > + >> ] Reducir divisor; Caso normal: aumentar resto > [ + [ < + > - ] > + >> ] Caso especial:aumentar el resto / moverlo de nuevo al divisor / aumentar el cociente <<<<< - Disminuir el dividendo ] Fin del ciclo de división >> [ < + > - ] Agregar el resto de nuevo al divisor para obtener un útil 13 > [ Saltar hacia adelante si el cociente era 0 - [ Disminuir el cociente y saltar hacia adelante si el cociente era 1 - << [ - ] >> Cociente cero y divisor si el cociente era 2 ] << [ << - >> - ] >> Divisor cero y restar 13 de la copia si el cociente era 1 ] << [ << + >> - ] Divisor cero y sumar 13 a la copia si el cociente era 0 ] Fin del bucle de salto externo (saltar aquí si ((carácter menos 1)/32) no era 2 o 3) < [ - ] Borrar el resto de la primera división si se omitió la segunda división < . [ - ] Generar el carácter ROT13ed de la copia y borrarlo < - , + Leer el siguiente carácter ] Fin del bucle de lectura de caracteres
En 2024, un proyecto de investigación de Google utilizó Brainfuck como base de un entorno digital artificial. En este entorno, encontraron que los replicadores surgían de forma natural y competían entre sí por dominar el entorno. Blaise Agüera y Arcas; Alakuijala, Jyrki; Evans, James; Laurie, Ben; Mordvintsev, Alexander; Niklasson, Eyvind; Randazzo, Ettore; Versari, Luca (2024). "Computational Life: How Well-formed, Self-replicating Programs Emerge from Simple Interaction". arXiv : 2406.19108 [cs.NE].
]
comando puede traducirse como un salto incondicional al comando correspondiente [
, o viceversa; los programas se comportarán de la misma manera, pero se ejecutarán más lentamente, debido a una doble búsqueda innecesaria.