HACKING 120% {Hacking, programmazione, computer & molto altro}

BrainFuck!, 120% lolling programming!

« Older   Newer »
  Share  
vogy
view post Posted on 1/8/2014, 15:26     +1   +1   -1




BRAINFUCK FOR DUMMIES

(per fare qualcosa di carino, come sempre assolutamente inutile...e per la gioia di Wet)

1. ABOUT BRAINFUCK

BrainFuck è un 'linguaggio esoterico', cioè un linguaggio di programmazione normalmente fatto per gioco che risponde a logiche rovesciate rispetto ai linguaggi comuni; mentre questi ultimi sono progettati per essere il più leggibili possibile da parte di chi scrive un sorgente (pensiamo ai classici if, while, print ecc.) un listato scritto in un esolang deve apparire incomprensibile o quantomeno divertente.
BrainFuck è un esolang 'Touring Complete', cioè risponde perfettamente a tutte le qualifiche richieste ad un linguaggio per gestire una macchina di Touring (es. istruzioni condizionali, gestione della memoria...).

Il linguaggio è strutturato in questo modo:
esiste un array di base, nella forma originale a[0]..a[30.000], in cui un puntatore (proprio analogo a un *ptr di C) si muove tra gli elementi e ne modifica il valore, ogni elemento è un byte.

Tanto per aggiungere inutilità all'inutile, scriviamo l'inutile 'Hello World!'...

Prendiamo la nostra bella tabella dei caratteri ASCII: http://it.wikipedia.org/wiki/ASCII#Tabella_dei_caratteri
CODICE
H  => 72
e  => 101
l  => 108
l  => 108
o  => 111
  => 32 (spazio)
W  => 87
o  => 111
r  => 114
l  => 108
d  => 100
!  => 33
\r => 13
\n => 10

Adesso abbiamo i dati che ci servono, passiamo al linguaggio...
Brainfuck riconosce come istruzioni soltanto determinati caratteri, il resto non viene nemmeno letto; questo implica che ad esempio non esiste un sistema per segnalare i commenti, questi vengono scritti ovunque senza essere letti (di prassi si utilizza ';' modello asm soltanto per maggior chiarezza).
Le istruzioni sono:
CODICE
+/- => incrementa/decrementa di 1 il valore del puntatore
>/< => incrementa/decrementa il puntatore (sposta il puntatore al valore a destra/sinistra dell'array)
.   => invia in output il valore del puntatore in questione
,   => pone in input il valore del byte nel puntatore (analogo a getchar())
[/] => implementa tra parentesi quadre le istruzioni condizionali, controllate da un puntatore che va a zero (analogo ad un JNZ in asm)

Come interprete dei nostri esempi useremo 'beef', con l'appunto che almeno nel pacchetto per Ubuntu utilizza il fine riga Windows CRLF (\r\n).

Tutto chiaro giusto? Well...

Es.A. Hello World molto scemo; uso il byte #0 e lo incremento o decremento stampando ogni volta
CODICE
+++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ ++.         ; porto a 72 e stampo [H]
+++++ +++++ +++++ +++++ +++++ ++++.                                                             ; 101-72=29 [He]
+++++ ++..                                                                                      ; da 101 a 108 e stampo 2 volte [Hell]
+++.                                                                                            ; da 108 a 111 [Hello]
----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----. ; 111-32=79 [Hello ]
+++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++.                              ; 87-32=55 [Hello W]
+++++ +++++ +++++ +++++ ++++.                                                                   ; 111-87=24 [Hello Wo]
+++.                                                                                            ; 114 [Hello Wor]
----- -.                                                                                        ; 108 [Hello Worl]
----- ---.                                                                                      ; 100 [Hello World]
----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- --.               ; 100-33=67 [Hello World!]
----- ----- ----- -----.                                                                        ; 33-13=20 [Hello World!\r]
---.                                                                                            ; 10 [Hello World!\r\n]

Notate come questa forma di codice sia in versione più leggibile e commentabile, la vera forma del programma è questa:
CODICE
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++. +++++++++++++++++++++++++++++. +++++++.. +++. -------------------------------------------------------------------------------. +++++++++++++++++++++++++++++++++++++++++++++++++++++++. ++++++++++++++++++++++++. +++. ------. --------. -------------------------------------------------------------------. --------------------. ---.

salviamo come hello.bf (va bene qualunque estensione) e passiamo a beef:
CODICE
vogy@eve:~$ beef hello.bf
Hello World!

Es.B. Hello World Relax Version; BF è particolarmente rilassante se lo si usa con un po' di cervello...
Il nostro scopo è quello di ridurre il numero delle istruzioni (il codice precedente ha 390 caratteri), per farlo useremo la possibilità di spostarci sugli array/byte e le funzioni di loop [/].
CODICE
; Primo valore 72, 72=8*9
>                               ; mi sposto in #1
++++++++                        ; porto #1 al valore 8
[                               ; loop...
<                               ; mi sposto in #0
+++++++++                       ; aggiungo 9 a #0
>                               ; torno a #1 (che vale 8)
-                               ; lo decremento (ora #1 vale 7)
]                               ; riavvio il loop: incremento di 9 #0, finché #1 non va a 0, cioè 8 volte.
                               ; notare che il loop esce quando si legge 0 in #1, posizione in cui si trova
                               ; il puntatore a fine loop
<.                              ; torno quindi in #0 (che vale ora 72) e stampo
                               ; adesso: #0 vale 72, #1 vale 0
; ora scompongo allo stesso modo 29, come (5*6)-1
>+++++[<++++++>-]<-.            ; #0 vale 101
+++++++..                       ; lo porto a 108 e stampo due volte, notate che 108 mi servirà ancora...
; tengo il mio #0, #1 a 111 come (10*11)+1
>>++++++++++[<+++++++++++>-]<+. ; #1 vale 111, anche questo mi servirà di nuovo
; porto #2 a 32, (6*5)+2
>>++++++[<+++++>-]<++.          ; mi servirà un 33, quindi sta bene qui
; 87 in #3, (10*9)-3
>>++++++++++[<+++++++++>-]<---. ; riassunto: #0 108, #1 111, #2 32, #3 87
; mi serve il mio 111? Lo ristampo...
<<.
; 111 non serve più, ora 114...
+++.
; riprendo 108, che poi non serve più...
<.
; lo uso per 100
--------.
; faccio 33 dal mio #2
>>+.
; da 33 a 13? 33-20, 33-(5*4)
>>+++++[<<---->>-]<<.           ; notare '>>' e '<<', mi sposto in #4 perché #3 vale 87 e non 0
; da 13 a 10
---.

listato risultante 185 caratteri :) È interessante notare come ci siano diverse modalità per scrivere la stessa cosa, anche a meno di 185 [è chiaro che non volevo cercare e proporre quella più corta, altrimenti era pura fotocopia; ci sono dei trucchetti ma volevo tenerli alla fine :O]
CODICE
>++++++++[<+++++++++>-]<. >+++++[<++++++>-]<-. +++++++.. >>++++++++++[<+++++++++++>-]<+. >>++++++[<+++++>-]<++. >>++++++++++[<+++++++++>-]<---. <<. +++. <. --------. >>+. >>+++++[<<---->>-]<<. ---.


2. INPUT

Quello che fino ad ora non abbiamo usato è ',' che comporta la possibilità di ottenere input.
Es.C. Stampa minimale
CODICE
,.>+++++++++++++.---.

Questa inutilissima applicazione stampa *un carattere*, che ricordo essere ASCII decimale, e va a capo:
CODICE
vogy@eve:~$ beef foo.bf
v
v
vogy@eve:~$ beef foo.bf
g
g
vogy@eve:~$ beef foo.bf
34
3

...un capolavoro di programmazione...però...
Es.D. Stampa tringa in input
CODICE
; look: >,[>,]<[<]>[.>] (è una sorta di forma fissa che trovate ovunque)
>, ; mi sposto in #1 e acquisisco l'input [vogy...perché non lo prendi in #0? Aspetta... ;) ]
[>,] ; loop in cui acquisisco tutti i caratteri fino al byte 0
< ; torno al byte 'non 0'
[<] ; continuo a retrocedere fino al sucessivo byte nullo [cioè #0...ti avevo detto di aspettare :P ]
> ; passo al byte successivo (il primo non nullo)
[.>] ; stampo il byte e vado al successivo

CODICE
vogy@eve:~$ echo vogy | beef print.bf
vogy
vogy@eve:~$ echo vogy scemo | beef print.bf
vogy scemo
vogy@eve:~$ echo vogy 12345 | beef print.bf
vogy 12345

3. OPERAZIONI SUI BLOCCHI DI MEMORIA

Abbiamo visto nel Es.B l'operazione di moltiplicazione, qui qualcosa in più
Spostare blocchi:
CODICE
+++ ; impostiamo #0 al valore 3, vogliamo spostare 3 in #1 scaricando #0
[>+<-] ; con un loop ci spostiamo in #1, aggiungiamo 1, tornando indietro
      ; andiamo a ridurre #0 finché non vale 0. Alla fine #0 0 e #1 3

Copiare blocchi:
CODICE
++++ ; #0 portato a 4, vogliamo *copiare* 4 in #2 (2 posizioni a destra)
[>+>+<<-] ; sposta #0 in #1 e #2, svuota #0 [#0 0, #1 4, #2 4]
>[<+>-] ; sposta #1 in #0, svuota #1 [#0 4, #1 0, #2 4]

Sommare blocchi:
CODICE
+++++ ; pongo #0 5
>+++++++ ; pongo #1 7
[<+>-] ; sommo a #0 il valore di #1, risultato in #0
      ;(Cavolo vogy! È tipo AX/EAX/RAX! Esatto...bravo :) )

Sottrarre blocchi:
CODICE
++++++++++ ; #0 10
>++++++ ; #1 6
[<->-] ; dovreste capirlo...

Bene signori, questo è quanto.
A dire il vero forse è più bello programmare un interprete bf che non scrivere i programmi (ne trovate in tutti i linguaggi possibili e immaginabili), come vedete il linguaggio nella sua assurdità ha potenzialità non da poco.

Siccome il periodo estivo dovrebbe richiamare a cose inutili, il vostro vogy (se volete) vi ha anche lasciato il compitino per le vacanze!
Qualcuno forse avrà notato che non è stata presentata una istruzione condizionale 'if', vi faccio notare che abbiamo visto come copiare e sottrarre i blocchi, se una sottrazione riesce alora 'è vero' altrimenti 'è falso'....
 
Top
*Atwa*
view post Posted on 26/10/2014, 19:01     +1   -1




Oggi ho programmato un interprete. Mi mancano alcuni comandi(input e istruzioni condizionali), ma per il resto... funziona perfettamente :D
Devo ancora scrivere la doc però.
 
Top
1 replies since 1/8/2014, 15:26   167 views
  Share