Linguaggi esoterici: Brainfuck

Articolo del 12/04/2023 - Redatto da Di Muro Francesco

Il mondo della programmazione ai giorni d'oggi è un mondo pieno di concetti, istruzioni, strutture e nozioni grazie alle quali il programmatore può creare un'applicazione utile allo scopo per la quale viene progettata, testata, revisionata e infine consegnata per l'utilizzo.

I linguaggi di programmazione più conosciuti e utilizzati tendono ad avere delle istruzioni human-readable, in modo tale da facilitarne l'utilizzo da parte dei programmatori, definendo in maniera quanto più generica possibile un set di istruzioni utilizzabili all'interno del programma stesso per descriverne l'esecuzione.
Esiste però una piccola famiglia di linguaggi di programmazione che, pur avendo come gli altri linguaggi un loro set di istruzioni per descrivere il comportamento di un programma, tendono a non essere così human-readable, motivo per cui prendono il nome di "esoterici", ovvero "incomprensibile ai più".

Esistono tantissimi linguaggi esoterici nel mondo della programmazione, ma quello del quale voglio parlare in questo articolo è "Brainfuck", in una definizione più estesa ed elegante, "cose così complicate o insolite da superare i limiti della propria comprensione".


Un po' di storia

Brainfuck nasce nel 1993 da Urban Müller, un fisico svizzero con l'obbiettivo di creare un linguaggio di programmazione semplice, ma allo stesso tempo completo, per una macchina di Turing , implementando un compilatore più piccolo possibile.
Grazie a un set di otto istruzioni, il linguaggio risulta avere l'equivalenza Turing o essere Turing-completo, permettendo al programmatore di scrivere una grande varietà di programmi.

La sintassi

Come anticipato, i programmi Brainfuck sono scritti utilizzando un set di otto istruzioni con dei ruoli ben precisi, basandosi sulla manipolazione di un'area di memoria di almeno 30000 celle da 1 Byte ognuna, all'interno delle quali è possibile rappresentare valori da 0 a 255 (2^8 - 1).
Per spostarsi all'interno del buffer di memoria viene utilizzato un puntatore, il quale ha come posizione iniziale la prima cella a sinistra (cella 0 o c0).
Di ogni cella di memoria se ne può incrementare, decrementare e stampare il valore;
inoltre, è possibile salvare il valore inserito dall'utente in una cella di memoria.

Le otto istruzioni messe a disposizione dal linguaggio sono:


Esempio pratico

Supponiamo di voler stampare a video l'alfabeto inglese (dalla A alla Z), per un totale di 26 lettere.
Per eseguire questo compito, abbiamo a disposizione otto istruzioni con le quali possiamo muoverci all'interno di un buffer di 30000 celle di memoria da 1 Byte ognuna, all'interno della quale possiamo salvare un numero da 0 a 255, e del quale, nel caso in cui ci sia una rappresentazione grafica nella codifica ASCII, questa venga stampata a video.

Sapendo che nella codifica ASCII la lettera "A" corrisponde al codice decimale 65, come primo compito sarà necessario incrementare la c0 (cella 0) fino al numero 65, e per fare ciò sarà necessario utilizzare l'unica istruzione di incremento messa a disposizione dal linguaggio, ovvero l'istruzione +.
Grazie a quest'istruzione, sarà possibile incrementare il valore della c0 fino a 65, e senza l'utilizzo di ulteriore istruzioni, significherebbe scrivere sessantacinque volte il simbolo +, cosa possibile ma non pratica e facile da interpretare.

Per questo motivo, è conveniente utilizzare le istruzioni [ e ] (il ciclo iterativo) per salvare il numero 65, dal quale poi stampare le 26 lettere dell'alfabeto.

Da definizione, il ciclo iterativo del linguaggio Brainfuck viene eseguito finché il valore della cella di memoria precedente all'istruzione di inizio del ciclo stesso risulta diverso da 0;
quindi, in che modo possiamo eseguire questo compito sapendo che non è possibile definire un numero finito di iterazione in maniera diretta?

Matematicamente, il numero 65 è divisibile per 1, 5, 13 e per se stesso;
di conseguenza, per utilizzare il ciclo iterativo, sarà necessario inizializzare il valore della c0 a 5 (65/13), incrementando il valore di c1 di 13 unità a ogni iterazione, sfruttando c0 come contatore, il quale valore dovrà essere decrementato a ogni iterazione in modo tale da raggiungere lo 0 e passare all'istruzione successiva al simbolo di fine ciclo.
Grazie a questo meccanismo, dopo aver salvato il valore 65 nella c1, concettualmente basterà incrementarne il valore di un'unità per ventisei volte e stamparne ogni volta il valore per visualizzare a video l'alfabeto.
Anche per questo compito, possono essere utilizzare svariati approcci, ma come visto nel task precedente, la soluzione più semplice e immediata è quella di stampare e incrementare il valore di c1 di un'unità per ventisei volte, e per eseguire quanto descritto, sarà necessario creare anche dei cicli iterativi.
Dato che il numero 26 non ci da modo di incrementare di un'unità c1  per ventisei volte con un ciclo iterativo più "pulito", l'approccio utilizzato sarà quello di contare cinque volte cinque (per un totale di venticinque iterazioni), stampando il valore di c1 e incrementandone il valore di un'unità a ogni iterazione, per poi incrementare c1 di un'unità per stampare la lettera Z a completamento del ciclo.
Conseguentemente, sarà necessario l'utilizzo delle celle c2  e c3, all'interno delle quali verrà inizializzato il valore 5, utilizzandole quindi come contatori per due cicli iterativi che, oltre a contare fino a venticinque, il ciclo iterativo nested stamperà il valore di c1 e ne incrementerà il valore di un'unità;
infine, sarà necessario incrementare il valore di c1 di un'unità e stamparne il valore per visualizzare a video la lettera Z.

Il programma dell'alfabeto in Brainfuck sarà quindi:

Rappresentato in questa maniera, il programma risulta molto criptico, motivo per cui Brainfuck è un linguaggio esoterico.
Vediamo una rappresentazione del programma più descrittiva: 

Di seguito, l'esecuzione del programma in un debugger Brainfuck:

Esecuzione del codice

Come (quasi) ogni linguaggio esoterico, la finalità della creazione ed esecuzione di un programma creato per l'appunto con uno di questi linguaggi, è pensare fuori dagli schemi (o "think out of the box"), per allenare la mente e aggiungere al proprio bagaglio culturale un argomento in più.