001: #include<stdio.h>
002: #include<stdlib.h>
003: #include<string.h>
004: #include<ctype.h>
005: 
006: 
007: // prototipi funzioni da definire
008: char *readstring(FILE*);
009: double convertnum(char*);
010: 
011: int main(int argc, char **argv){
012: 
013:   FILE *fp = fopen(argv[1], "r");   // per comodita' passo il nome del file sulla riga di comando, il testo non diceva nulla in merito e quindi ogni scelta ragionevole e' valida
014:   if(!fp)
015:   {
016:     perror("");
017:     exit(EXIT_FAILURE);
018:   }
019: 
020:   char *s; // la uso per memorizzare stringa restituita da readstring()
021: 
022:   int inizio = 1; // per capire se e' il primo numero o sono dopo un =
023:   double acc, operand; // dello stesso tipo restituito da convertnum()
024:   while( (s = readstring(fp)) != NULL)
025:   {
026:     if( !strcmp(s, "+") )
027:     {
028:       acc = acc + operand;
029:     }
030:     else if( !strcmp(s, "-") )
031:     {
032:       acc = acc - operand;
033:     }
034:     else if( !strcmp(s, "*") )
035:     {
036:       acc = acc * operand;
037:     }
038:     else if( !strcmp(s, "/") )
039:     {
040:       acc = acc / operand; // il testo non chiedeva di effettuare controlli  sugli operandi, quindi non controllo se divisione per 0
041:     }
042:     else if( !strcmp(s, "=") )
043:     {
044:       printf("%lf\n", acc);
045:       inizio = 1; // dopo un "=" e' come se ripartissi da 0
046:     } 
047:     else // per esclusione
048:     {
049:       double num = convertnum(s);
050:       // printf("Ho letto %s -> %lf\n", s, num); // DEBUG
051:       if(inizio) // se primo numero letto o comunque primo numero dopo un "=" devo memorizzare in acc, altrimenti in operand
052:       {
053:   inizio = 0;
054:   acc = num;
055:       }
056:       else
057:       {
058:   operand = num;
059:       }
060:     }
061: 
062:     free(s); // non mi serve piu' e disalloco
063:   }
064: 
065:   fclose(fp);
066: 
067:   
068: 
069: 
070:   return 0;
071: }
072: 
073: char *readstring(FILE *f)
074: {
075:   char tmp[10000];
076:   int letti = fscanf(f, " %s", tmp); // come visto a lezione, fscanf() restituisce il numero di specificatori di formato che e' riuscita a risolvere. Quindi se legge una stringa deve restituire 1
077:   if( letti != 1 ) 
078:     return NULL;
079: 
080:   // alloco area dinamica su cui ricopiare la stringa e tengo conto del carattere nullo di terminazione
081:   char *x = malloc(strlen(tmp) + 1);
082:   // vi copio quanto letto
083:   strcpy(x, tmp);
084:   // lo restituisco
085:   return x;
086: }
087: 
088: 
089: double convertnum(char *s)
090: {
091:   // PRIMA PARTE valutiamo se la stringa contiene realmente un numero
092: 
093:   // test 1: deve contenere solo cifre oppure ., +, -, e  o E
094:   // scorro carattere per carattere e controllo
095:   for(int i = 0; i < strlen(s); ++i)
096:   {
097:     if( !isdigit(s[i]) && s[i] != '+' && s[i] != '-' && s[i] != '.' && s[i] != 'e' && s[i] != 'E' )
098:     {
099:       printf("ERRORE: la stringa %s contiene il carattere non valido [%c] \n", s, s[i]);
100:       exit(EXIT_FAILURE);
101:     }
102:     // per semplicita' di operazioni nel seguito, trasformo eventuale e minuscola in E maiuscola
103:     if( s[i] == 'e' )
104:       s[i] = 'E';
105:   }
106: 
107:   // test 2: il primo carattere deve essere solo una cifra oppure ., +, -
108:   if( !isdigit(s[0]) && s[0] != '.' && s[0] != '+' && s[0] != '-' )
109:   {
110:     printf("ERRORE: la stringa %s ha il primo carattere non valido\n", s);
111:     exit(EXIT_FAILURE);
112:   }
113: 
114:   // nota, da qui in avanti uso le funzioni di stringa strchr() e strrchr() che mi cercano un carattere all'interno di una stringa e restituiscono il suo indirizzo se lo trovano o NULL se non c'e'.
115:   // dato che una cerca da destra e una da sinistra posso usarle per capire se ci sono almeno due caratteri di quel tipo
116:   // confrontando gli indirizzi restituiti si puo' inoltre capire se un carattere ricercato e' prima o dopo di un altro
117: 
118:   // test 3: solo un "." e prima di eventuali E o e
119:   char *firstdec = strchr(s, '.');  // cerco posizione eventuale punto partendo da sinistra
120:   char *lastdec  = strrchr(s, '.'); // cerco posizione eventuale punto partendo da destra
121:   // se c'e un punto solo, che io lo cerchi a partire da sinistra o a partire da destra devo avere lo stesso risultato
122:   if( firstdec != lastdec )
123:   {
124:     printf("ERRORE: la stringa %s ha troppi punti decimali\n", s);
125:     exit(EXIT_FAILURE);
126:   }
127:   char *firstE   = strchr(s, 'E');  // cerco posizione eventuale E partendo da sinistra
128:   // se c'e' un . e una E allora il . deve precedere la E
129:   if(firstE && firstdec && firstE < firstdec) // firstE e firstdec sono NULL se non c'e' quel carattere, quindi li confronto se e solo se tutti e due non sono NULL
130:   {
131:     printf("ERRORE: la stringa %s ha il punto decimale dopo l'indicazione dell'esponente\n", s);
132:     exit(EXIT_FAILURE);
133:   }
134: 
135:   // test 4: la E deve apparire una sola volta ed essere seguita da almeno una cifra eventualmente preceduta da + o -
136:   char *lastE    = strrchr(s, 'E');  // cerco posizione eventuale E partendo da destra (da sinistra l'avevo gia' calcolata)
137:   if( firstE != lastE )
138:   {
139:     printf("ERRORE: la stringa %s ha troppe indicazioni dell'esponente\n", s);
140:     exit(EXIT_FAILURE);
141:   }
142:   // se la E esiste guardo il carattere immediatamente successivo saltando eventuale segno
143:   if( firstE )
144:   {
145:     ++firstE; // incremento il puntatore che ora puntera' al carattere che segue la E
146:     if( *firstE == '+' || *firstE == '-' ) // se il carattere successivo e' un + o un - incremento ancora
147:       ++firstE;
148:     if( !isdigit(*firstE) )
149:     {
150:       printf("ERRORE: la stringa %s non contiene un numero dopo l'indicazione dell'esponente\n", s);
151:       exit(EXIT_FAILURE);
152:     }
153:   }
154: 
155:   // SECONDA PARTE converto stringa in numero (se arrivo qui, sicuramente il numero e' corretto) e lo restituisco
156: 
157:   return atof(s); 
158: 
159: 
160: }
161: