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;
}