Anche questa verifica riguarda BF-, un subset del linguaggio di programmazione esoterico Brainfuck. Rimandiamo alla pagina della verifica precedente per una descrizione dei comandi supportati da BF-.
Il programma fb contenuto nell’archivio (link) ripete per un numero casuale di volte le seguenti operazioni:
- invia su una pipe un programma BF- terminato dal carattere #;
- legge da una seconda pipe l’output prodotto dal programma inviato e lo confronta con quello atteso.
I nomi delle pipe da cui leggere e scrivere sono passati al programma fb come argomenti da riga di comando usando rispettivamente le opzioni -i e -o. Se l’opzione -o non viene specificata, il programma scrive su standard output; se l’opzione -i non viene specificata, il programma legge da standard input.
L’obiettivo della verifica è di realizzare un programma che legge dalla prima pipe il programma inviato da fb, lo interpreta e scrive sulla seconda pipe l’output del programma. Terminata l’esecuzione di un programma, dovete inviare sulla pipe il carattere # e riportare l’interprete allo stato iniziale (tutta la memoria deve essere inizializzata a 0 e il data pointer deve puntare alla prima variabile della memoria). Questa sequenza di operazioni deve essere ripetuta per tutti i programmi inviati sulla pipe da fb.
Il programma fb supporta altre opzioni:
- -b attiva la modalità avanzata che produce programmi contenenti i comandi < e >;
- -v attiva la modalità verbosa;
- -h mostra le opzioni supportate.
Per usare le pipe, il programma può essere invocato come segue:
> ./fb -i /tmp/pipeIn -o /tmp/pipeOut
Soluzione
Ecco una possibile soluzione commentata della verifica:
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #define PIPEIN "/tmp/pipeIn" #define PIPEOUT "/tmp/pipeOut" #define MEMSIZE 16 /* * La verifica consiste nell'implementazione di un programma in C che interagisca * tramite pipe con il programma 'fb' che ci è stato fornito. Il programma 'fb' * invia su PIPEOUT un programma nel linguaggio BF- terminato dal carattere '#' e * si aspetta di ricevere su PIPEIN l'output prodotto dal programma inviato. * Questa interazione deve essere ripetuta per tutti i programmi ricevuti su * PIPEOUT. * * Il programma che è stato realizzato supporta tutti i comandi previsti dal * linguaggio BF- ed utilizza una memoria di dimensione pari a 16 byte. Il * programma legge un carattere per volta del programma BF- da interpretare * ed esegue l'operazione corrispondente al comando letto. Quando viene letto * il carattere '#', viene a sua volta inviato sulla pipe per segnalare che * l'esecuzione del programma BF- è terminato: la memoria e il data pointer * vengono azzerati per preparare l'interprete all'esecuzione del programma * successivo. * * Dopo aver avviato questo programma, il programma di test va eseguito come * segue: * * ./fb -i /tmp/pipeIn -o /tmp/pipeOut -b */ int main() { /* La memoria è modellata come un vettore di byte di dimensione 16. */ unsigned char memory[MEMSIZE] = {0}; int fr, fw, dp; char c; /* Crea le due pipe se non esistono già. */ mkfifo(PIPEIN, 0666); mkfifo(PIPEOUT, 0666); /* Apri PIPEIN in scrittura e PIPEOUT in lettura. Stampa un messaggio e * termina se si verifica un errore durante l'apertura. */ fw = open(PIPEIN, O_WRONLY); if (fw < 0) { fprintf(stderr, "Errore nell'apertura della pipe '%s' in scrittura.\n", PIPEIN); return EXIT_FAILURE; } fr = open(PIPEOUT, O_RDONLY); if (fr < 0) { fprintf(stderr, "Errore nell'apertura della pipe '%s' in lettura.\n", PIPEOUT); return EXIT_FAILURE; } /* Inizialmente il data pointer punta alla prima cella della memoria. */ dp = 0; /* Leggi ed interpreta un comando per volta. Il ciclo termina quando la * funzione 'read' restituisce zero, ovvero PIPEOUT è vuota ed è stata chiusa * in scrittura dal programma 'fb'. */ while(read(fr, &c, sizeof(char))) { if (c == '>') { /* Incrementa il data pointer modulo MEMSIZE. */ dp = (dp+1) % MEMSIZE; } else if (c == '<') { /* Decrementa il data pointer modulo MEMSIZE. */ dp = (dp-1+MEMSIZE) % MEMSIZE; } else if (c == '+') { /* Incrementa la cella di memoria indicata dal data pointer. */ memory[dp]++; } else if (c == '-') { /* Decrementa la cella di memoria indicata dal data pointer. */ memory[dp]--; } else if (c == '.') { /* Invia sulla pipe il valore della cella di memoria indicata dal * data pointer sotto forma di carattere. */ write(fw, memory+dp, sizeof(char)); } else if (c == '#') { /* Invia il carattere '#' per segnalare la terminazione dell'esecuzione * del programma. */ write(fw, "#", sizeof(char)); /* Azzera la memoria e riporta il data pointer alla posizione * iniziale. */ memset(memory, 0, MEMSIZE); dp = 0; } else { fprintf(stderr, "Carattere '%c' non riconosciuto.\n", c); return EXIT_FAILURE; } } /* Chiudi le pipe e rimuovile dal filesystem. */ close(fr); close(fw); unlink(PIPEIN); unlink(PIPEOUT); return EXIT_SUCCESS; }