/* Esercizio di preparazione prova 30-5-2005 */ #include #include #include #include #include #include /* Istruzioni: Preparare un file con un programma pascal. Es: ProgrammaTest.pas Creare l'eseguibile per questo scanner. Es: Scanner.exe Salvare entrambi in una directory. Esempio: c: Dal prompt di Ms-Dos lanciare "c:\> Scanner ProgrammaTest.pas" */ #define MAX_LUNG_STRINGA 40 /* definisce la lunghezza massima di una stringa di caratteri */ typedef char String[MAX_LUNG_STRINGA+1]; typedef enum { Identificatore, ParolaChiave, Commento, ParentesiAperta, ParentesiChiusa, Piu, Meno, Asterisco, Slash, Uguale, PuntoVirgola, Punto, Virgola, Maggiore, MaggioreUguale, Minore, MinoreUguale, Diverso, DuePunti, Assegnamento, Intero, Reale, Stringa, Errore} TipoToken; /* definisce le classi di token */ typedef struct { TipoToken classe; String valore; } Token; /* definisce il token come una coppia (classe,valore) */ char* ParoleChiave[] = {"begin","boolean","char","do","else","end","false", "for","if","integer","program","real","repeat","then","to","true","until", "var","while",NULL}; /* definisce un vettore contenente le parole chiave */ int e_ParolaChiave(String s) { /* stabilisce se la stringa s è una parola chiave */ char* key = ParoleChiave[0]; int i = 0; while (ParoleChiave[i]) if (strcmpi(ParoleChiave[i++],s) == 0) return !0; return 0; } int getToken(FILE* f, Token* t) { /* legge un token e lo classifica */ static char c; static int lookahead = 0; /* sono statiche in modo che non perdano il loro valore all'uscita della funzione. lookahead serve per sapere se il carattere letto c, è stato consumato (lookahead = 0), o se indicava l'inizio di un nuovo token e dunque non è stato consumato (lookahead = !0) */ enum {Inizio, Identificatore_ParolaChiave, Parentesi_Commento, InCommento, AsteriscoInCommento, Maggiore_MaggioreUguale, Minore_MinoreUguale_Diverso, DuePunti_Assegnamento, NumIntero_NumReale, NumReale, InStringa, ApiceInStringa} stato_corrente = Inizio; /* stati dell'automa riconoscitore */ int i = 0; int eot = 0; /* variabile che indica la fine di un token. 0 = falso, !0 = vero */ t->classe = Errore; /* inizializziamo la classe del token a Errore */ while (!eot && i < MAX_LUNG_STRINGA) { /* leggo una stringa finchè non è finito il token o si è raggiunta la dimensione massima */ if (!lookahead) c = fgetc(f); /* non avevamo caratteri in sospeso, dunque leggiamo un nuovo carattere dal file */ else lookahead = 0; /* consumiamo il carattere che avevamo e dunque lookahead varrà 0 */ if (c == EOF) break; else switch(stato_corrente) { case Inizio: /* accettiamo tutti i caratteri validi e transitiamo nello stato opportuno */ if (isalpha(c) || c == '_') { stato_corrente = Identificatore_ParolaChiave; t->valore[i++] = c; t->classe = Identificatore; /* Siamo all'inizio di un identificatore o di una parola chiave. Passiamo nello stato opportuno, inseriamo c nel token e assegniamo al token la sua classe di appartenenza */ } else if (isspace(c)) stato_corrente = Inizio; /* gli spazi vengono ignorati */ else if (isdigit(c)) { stato_corrente = NumIntero_NumReale; t->valore[i++] = c; t->classe = Intero; /* la lettura di una cifra porta ad associare il token alla classe intero */ } else switch(c) { case '+': t->valore[i++] = c; t->classe = Piu; eot = !0; break; case '-': t->valore[i++] = c; t->classe = Meno; eot = !0; break; case '*': t->valore[i++] = c; t->classe = Asterisco; eot = !0; break; case '/': t->valore[i++] = c; t->classe = Slash; eot = !0; break; case '=': t->valore[i++] = c; t->classe = Uguale; eot = !0; break; case ';': t->valore[i++] = c; t->classe = PuntoVirgola; eot = !0; break; case '.': t->valore[i++] = c; t->classe = Punto; eot = !0; break; case ',': t->valore[i++] = c; t->classe = Virgola; eot = !0; break; case '(': stato_corrente = Parentesi_Commento; t->valore[i++] = c; break; case ')': t->valore[i++] = c; t->classe = ParentesiChiusa; eot = !0; break; /* + - * / = ; . , ) rappresentano token a se stanti */ case '>': stato_corrente = Maggiore_MaggioreUguale; t->valore[i++] = c; /* non è sicuro che il token sia finito, poichè potrebbe essere > o >=. Dunque eot non viene modificato */ break; case '<': stato_corrente = Minore_MinoreUguale_Diverso; t->valore[i++] = c; /* non è sicuro che il token sia finito, poichè potrebbe essere < <= <>. Dunque eot non viene modificato */ break; case ':': stato_corrente = DuePunti_Assegnamento; t->valore[i++] = c; /* non è sicuro che il token sia finito, poichè potrebbe essere : :=. Dunque eot non viene modificato */ break; case '\'': stato_corrente = InStringa; break; default: eot = !0; t->valore[i++] = c; } /* end switch(c) */ break; case Identificatore_ParolaChiave: if (isalnum(c) || c == '_') { stato_corrente = Identificatore_ParolaChiave; t->valore[i++] = c; /* continuo a leggere un identificatore o parola chiave */ } else { lookahead = !0; eot = !0; /* l'identificatore era finito con il carattere letto precedentemente, dunque il token è finito e mi ritrovo con un carattere in più */ } break; case Parentesi_Commento: if (c == '*') { stato_corrente = InCommento; i--; /* se dopo la ( trovo * sono dentro un commento */ } else { lookahead = !0; eot = !0; t->classe = ParentesiAperta; /* non ho trovato un *, dunque la parentesi era un token a se stante e mi ritrovo con un carattere in più da sfruttare */ } break; case InCommento: if (c == '*') stato_corrente = AsteriscoInCommento; /* sono in un commento e trovo *. Potrebbe essere l'inizio della sequenza di fine commento "*)" */ break; case AsteriscoInCommento: if (c == ')') stato_corrente = Inizio; /* il commento si è concluso */ else stato_corrente = InCommento; /* l'asterisco era una simbolo all'interno del commento */ break; case Maggiore_MaggioreUguale: if (c == '=') { t->classe = MaggioreUguale; t->valore[i++] = c; eot = !0; } /* il > è seguito da = quindi il token è >= */ else { t->classe = Maggiore; eot = lookahead = !0; } /* il token letto + >. Ho un carattere da sfruttare */ break; case Minore_MinoreUguale_Diverso: if (c == '=') { t->classe = MinoreUguale; t->valore[i++] = c; eot = !0; } /* ho letto il token <= */ else if (c == '>') { t->classe = Diverso; t->valore[i++] = c; eot = !0; } /* ho letto il token <> */ else { t->classe = Minore; eot = lookahead = !0; } /* ho letto il token <. Ho un carattere da sfruttare */ break; case DuePunti_Assegnamento: if (c == '=') { t->classe = Assegnamento; t->valore[i++] = c; eot = !0; } /* Ho letto il token := */ else { t->classe = DuePunti; eot = lookahead = !0; } /* ho letto il token : */ break; case NumIntero_NumReale: if (isdigit(c)) t->valore[i++] = c; /* continuo a leggere cifre */ else if (c == '.') { stato_corrente = NumReale; t->valore[i++] = c; /* letto un . che segna l'inizio di una parte decimale */ } else { t->classe = Intero; eot = lookahead = !0; /* ho letto un intero. Ho un carattere da sfruttare */ } break; case NumReale: if (isdigit(c)) t->valore[i++] = c; /* continuo a leggere la parte decimale di un reale */ else { t->classe = Reale; eot = lookahead = !0; /* ho letto un numero reale. Ho un carattere da sfruttare */ } break; case InStringa: if (c == '\'') stato_corrente = ApiceInStringa; /* ho letto un apice in una stringa */ else t->valore[i++] = c; /* continuo a leggere una stringa */ break; case ApiceInStringa: if (c == '\'') { stato_corrente = InStringa; t->valore[i++] = c; } /* ho letto un apice in una stringa */ else { t->classe = Stringa; eot = lookahead = !0; } /* l'ultimo apice letto segnava la fine della stringa. Ho un carattere da sfruttare */ break; } /* end switch(stato_corrente) */ } /* end while */ t->valore[i++] = '\0'; /* assegno il fine stringa al token letto */ if (e_ParolaChiave(t->valore)) t->classe = ParolaChiave; /* controllo se il token è una parola chiave */ return (c != EOF); }