2.34        Linguaggi rompicapo

In questo paragrafo c’è una raccolta di linguaggi nati per divertimento, e per lo più con lo scopo di sfidare la bravura dei programmatori. Una tecnica per affrontarli, e semplificarli, è quella di dotarsi di macro, eventualmente interpretate da un linguaggio di alto livello che ne genera il sorgente finale.

2.34.1 Brainf***

E' un linguaggio di una "macchina" con 8 istruzioni, un registro che punta ad un indirizzo della memoria (MP), un registro che segue il programma (PC), un dispositivo di input, ed uno di output:

E' stato dimostrato che Brainf*** è Touring completo, quindi può calcolare qualsiasi algoritmo algoritmo, nella tabella sottostante sono alcuni "programmi" per dei semplici problemi:

Programma

Funzione

Risultato[1]

,>,[<+>-]<.

Somma due numeri

> brainf -3 17

,>,[<+>-]<.

^Z

 

Input: -3

Input: 17

Output: 14

,[>+++<-]>.

Moltiplica per 3

> brainf 21

,[>+++<-]>.

^Z

 

Input: 21

Output: 63

,[>[-.]+.<-]

Flip flop

> brainf 2

,[>[-.]+.<-]

^Z

 

Input: 2

Output: 1

Output: 0

Output: 1

>,                   input var in pos 1

[

  [-<]               var meno 1 vai a pos 0

  +[

    >[>+>+<<-]       var in posizioni 2 e 3

    >>[-<<+>>]       ripristina var da pos 2

    <<[              se var non zero

        >-             in copia var meno 1

        >>+            risultato più 1

        <<<[-]]        azzero var

    >[-<+>]          ripristino var (meno 1)

    <<               ritorna in pos 0

  -]

 >                   ritorna in pos 1

]

>>>.                 Risultato

Divide per 2

> brainf 0

...(omesso)...

Input: 0

Output: 0

> brainf 1000

 

Input: 1000

Output: 500

> brainf 999

 

Input: 999

Output: 499

> brainf 2

 

Input: 2

Output: 1

Gli esempi sono stati provati con un interprete generato tramite Bison (v. par.1.7 ); l'interprete accetta anche commenti purché non contengano i caratteri che corrispondono ai codici delle istruzioni.

Brainf*** è una sfida per i programmatori, come si può intuire dagli esempi riportati, tuttavia è una sfida che alcuni hanno affrontato per programmi non banali, quali un interprete di Brainf*** e un programma che stampa se stesso (quine). Per affrontare Brainf*** adeguatamente occorre dotarsi di macrofunzioni, un esempio è stato sviluppato con GNU AWK (v.par. 2.8 ), per generare un programma che verifica il segno di un numero. Le macrofunzioni lasciano il puntatore alla memoria nella posizione in cui è stata eseguita l'operazione, nel caso di COPY e MOVE, alla posizione in cui è stato copiato o mosso il primo operando .

Macroprogramma (file abs.src)

Programma generato (file brnabs.src)

INPUT NUMBER

[

  INCR COUNT

  COPY COUNT COMODO

  INCR SW2

  GOTO SW1

  [

    GOTO COUNT

    [

      INCR NUMBER

      DECR COUNT

    ]

    DECR SW2

    DECR SW1

  ]

  GOTO SW2 

  [

    GOTO COUNT

    [

      DECR NUMBER

      DECR COUNT

    ]

    INCR SW1

    DECR SW2

  ]

  MOVE COMODO COUNT

  GOTO NUMBER

]

GOTO SW1

OUTPUT

prova

>  brainf -300 < brnabs.src

 

Input: -300

Output: 0

>  brainf 300 < brnabs.src

 

Input: 300

Output: 1

>,

[ 

>+

>[-]<<<[-]>>[-<<+>>>+<]<<[->>+<<]>>>

>+

>

[ 

<<<

[ 

<+

>-

] 

>>-

>-

] 

<

[  

<<

[ 

<-

>-

] 

>>>+

<-

] 

<<[-]>[-<+>]<

<

] 

>>>>

.

 

Variabile COMODO a 3

Variabile COUNT   a 2

Variabile NUMBER a 1

Variabile SW1     a 5

Variabile SW2     a 4

 

2.34.2            BEFUNGE

Befunge é un linguaggio inventato da Chris Pressey nel 1993 con lo scopo di essere complesso da utilizzare (Twisted, Deranged Programming Language in the Tradition of BrainF*** and False). Befunge opera su una  memoria stack ed una memoria bidimensionale con un insieme di istruzioni che comprendono:

·         spostamenti del Program Counter nelle 4 direzioni, salto di una posizione (PC+2), spostamenti orizzontali o verticali in funzione del contenuto dello stack.

·         operazioni sullo stack: duplicazione, scambio, eliminazione, POP in memoria, Push da memoria, stampa, operazioni aritmetiche, immissione di numeri, ecc…

Nel Befunge originale le dimensioni della memoria erano 80x24 ma Befunge-97 permette una griglia di capacità illimitata ed un’insieme di direttive per aiutare la stesura dei programmi. Fra le estensioni proposte, ma non sono a conoscenza se siano state realizzate: Funge-98 con un numero arbitrario di dimensioni, multithreaded e con più di un Program Counter.

La tabella che segue contiene le istruzioni di Befunge.

COMMAND         INITIAL STACK(bot->top)RESULT (STACK)

 

+ (add)         <value1> <value2>       <value1 + value2>

- (subtract)    <value1> <value2>       <value1 - value2>

* (multiply)    <value1> <value2>       <value1 * value2>

/ (divide)      <value1> <value2>       <value1 / value2> (nb. integer)

% (modulo)      <value1> <value2>       <value1 mod value2>

! (not)         <value>                 <0 if value non-zero, 1 otherwise>

` (greater)     <value1> <value2>       <1 if value1 > value2, 0 otherwise>

> (right)                               PC -> right

< (left)                                PC -> left

^ (up)                                  PC -> up

v (down)                                PC -> down

? (random)                              PC -> right? left? up? down? ???

_ (horizontal if) <boolean value>       PC->left if <value>, else PC->right

| (vertical if)   <boolean value>       PC->up if <value>, else PC->down

" (stringmode)                          Toggles 'stringmode'

: (dup)         <value>                 <value> <value>

\ (swap)        <value1> <value2>       <value2> <value1>

$ (pop)         <value>                 pops <value> but does nothing

. (pop)         <value>                 outputs <value> as integer

, (pop)         <value>                 outputs <value> as ASCII

# (bridge)                              'jumps' PC one farther; skips

                                        over next command

g (get)         <x> <y>                 <value at (x,y)>

p (put)         <value> <x> <y>         puts <value> at (x,y)

& (input value)                         <value user entered>

~ (input character)                     <character user entered>

@ (end)                                 ends program

Figura 21 (da Befunge-93 Documentation di Chris Pressey)

Nella Figura 22 alcuni esempi di istruzioni con la relativa esecuzione, le funzioni sono precedute dalla richiesta di immissione di un numero (&) e sono seguite dalla stampa del risultato (.) e dall’istruzione di fine programma (@).

Programma

Funzione

Risultato

&&+.@

Somma due numeri

Immetti numero 180

Immetti numero 19

199

&&04p:04g\04g....@

Duplica due numeri sullo stack

Immetti numero 17

Immetti numero 13

17 13 17 13

v                                     >v

013p123p&33p23g23g+23p113g+13p13g33g-!|23g.@

            ^<<<<<<<<<<<<<<<<<<<<<<<<<<

Potenza

n-esima di due

Immetti numero 7

128

Figura 22

Infine il codice seguente, (Figura 23 ), genera la serie di Fibonacci. L’interprete (incompleto) di Befunge è stato scritto con BCPL (v. BCPL par. 2.13.1.2 ), la generazione del codice sorgente è stata prodotta con un macrolinguaggio interpretato da un programma in MUMPS (v. MUMPS par.  2.41 ).

REM SERIE DI FIBONACCI

      MOVE 0 COUNT

      MOVE 1 A1

      MOVE 0 A2

      ASK LIMITE

LABEL LOOP

      MOVE 0 COMODO

      ADD A1 COMODO

      ADD A2 COMODO

      MOVE A2 A1

      MOVE COMODO A2

      PRINT COMODO

      ADD 1 COUNT

      COMPARE COUNT LIMITE

      IFNONEQUAL GOTO LOOP

      END

v                                                                       >v

013p123p033p&43p053p23g53g+53p33g53g+53p33g23p53g33p53g.113g+13p13g43g-!|@

                ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Immetti numero 13

1 1 2 3 5 8 13 21 34 55 89 144 233

Figura 23



[1] L'interprete accetta il programma dallo standard input.