-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmacchina-astratta.cc
471 lines (420 loc) · 15.8 KB
/
macchina-astratta.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/*!
\mainpage Macchina Astratta
\section intro_sec Introduzione
La macchina astratta implementata e` basata sulla JVM sia per quanto riguarda
la sua struttura, sia per l'instruction-set. Per quest'ultimo e` stato preso
un sottoinsieme di quello della JVM, percui un programma funzionante su questa
macchina astratta funziona anche sulla JVM, traducendo pero` gli mnemonici in
bytecode con un assembler come, per esempio, l'Oolong scritto da Joshua Engel.
La struttura, in modo simile alla JVM, e` composta da uno stack di sistema,
in cui vengono messi i record di attivazione (RdA) di ogni funzione che viene
eseguita, e da una area-programma dove vengono memorizzate le istruzioni da
eseguire. Ogni RdA e` quindi composto da un program counter (PC) che punta
all'istruzione da eseguire, da uno stack degli operandi e da un array delle
variabili locali.
\section install_sec Compilazione e installazione
Per compilare e creare l'eseguibile della macchina astratta, o per creare la
documentazione attraverso doxygen, e` possibile dare uno dei seguenti comandi
all'interno della directory "macchina-astratta" dove sono presenti i file
sorgente:
- <tt>make</tt>: crea l'eseguibile <tt>macchina-astratta</tt> dentro la
directory "bin".
- <tt>make doc</tt>: crea la documentazione del codice in formato html e
pdf dentro la directory "doc".
L'eseguibile <tt>macchina-astratta</tt> si aspetta come argomento un file,
all'interno del quale ci dovra` essere il codice del programma da eseguire.
*/
/*!
\file macchina-astratta.cc
\brief Implementazione della macchina astratta
\author Andrea Zanelli
\date 18-12-2008
*/
#include <fstream>
#include <iostream>
#include <string>
#include <stdio.h>
#include "ProgramArea.h"
#include "GlobalVariablesArea.h"
#include "SystemStack.h"
using std::string;
// Variabili globali
//! Contiene l'insieme delle istruzioni da eseguire suddivise per funzione
ProgramArea programma;
//! Contiene le variabili globali del programma
GlobalVariablesArea variabili_globali;
//! Stack di sistema del programma da eseguire, contiene i record di attivazione
SystemStack stack_di_sistema;
//! Se "true" e` stata trovata la funzione "clinit"
bool funzione_clinit = false;
//! Numero di funzioni trovate all'interno del programma
unsigned int n_funzioni = 0;
// Dichiarazione di funzioni
void leggi_file(std::ifstream& in_file);
bool leggi_istruzione(std::ifstream& file, string& str);
void cotrolla_etichetta(string& str);
void gestisci_direttiva(const string& direttiva);
void inserisci_variabile_globale(const string& direttiva);
void pulisci_stringa(string& str);
void elimina_spazi_iniziali_e_finali(string& str);
void cancella_carattere(string& str, char c = ' ');
extern void esecutore();
/*!
\fn int main(int argc, char **argv)
\brief Funzione iniziale della macchina astratta
\param argv array con gli argomenti
\param argc numero di argomenti
\return 0: Ok
\return 1: Errore
Prende come argomento il nome del file da eseguire (contenente il programma).
Dopodiche` esegue i seguenti passi:
- (1) Inizializza l'oggetto globale <tt>programma</tt> e carica le
istruzioni del programma al suo interno.
- (2) Inizializza l'oggetto globale <tt>variabili_globali</tt> e ci
mette dentro le variabili globali del programma.
- (3) Se esiste la funzione "\<clinit\> ()V" (per l'inizializzazione
delle variabili globali) crea un record di attivazione vuoto in
<tt>stack_di_sistema</tt> impostando il PC alla prima istruzione di
"<clinit> ()V" e chiama la funzione <tt>esecutore()</tt>.
- (4) Crea un record di attivazione nell'oggetto globale
<tt>stack_di_sistema</tt> e imposta il PC alla prima istruzione della
funzione main.
- (5) Passa il controllo alla funzione <tt>esecutore()</tt> che si occupa
di eseguire le istruzioni contenute in <tt>programma</tt>.
*/
int main(int argc, char **argv) {
/* Gestisce gli argomenti e apre il file */
std::ifstream in_file;
if(argc < 2) {
std::cerr <<"Errore: specificare il nome del file" <<std::endl;
return 1;
}
if(argc > 2) {
std::cerr <<"Attenzione: i seguenti argomenti sono stati ignorati:";
for(int i=2; i<argc; ++i)
std::cerr <<" " <<argv[i];
std::cerr <<std::endl;
} // end if(argc > 2)
in_file.open(argv[1]);
if(!in_file) {
std::cerr <<"Errore: impossibile aprire il file " <<argv[1] <<std::endl;
return 1;
}
try{
/* (1) e (2): legge e gestisce le istruzioni nel file */
leggi_file(in_file);
/* (3) e (4): mette un RDA vuoto sullo stack di sistema e chiama la funzione
esecutore() */
if(funzione_clinit) {
// esegue la funzione <clinit>()V
stack_di_sistema.push_ar();
stack_di_sistema.pc_set(programma.get_function_index("<clinit>()V"));
esecutore();
}
stack_di_sistema.push_ar();
stack_di_sistema.pc_set(
programma.get_function_index("main([Ljava/lang/String;)V") );
/* (5): passa il controllo alla funzione esecutore() */
esecutore();
} // end try
/* Gestione eccezioni */
catch(string e) {
std::cerr <<"Errore: " <<e <<std::endl;
in_file.close();
return 1;
}
catch(...) {
std::cerr <<"Errore: l'esecuzione e` stata interrotta da "
<<"un errore sconosciuto" <<std::endl;
in_file.close();
return 1;
}
in_file.close();
return 0;
} // end main(int argc, char **argv)
/*!
\fn void leggi_file(const std::ifstream& in_file)
\brief Legge il file passato e gestisce le istruzioni lette
\param in_file file-stream di input, aperto sul file da leggere
Legge il file passato come file-stream e mette le istruzioni in
<tt>programma</tt> e le variabili globali in <tt>variabili_globali</tt>.
Ogni istruzione inserita in <tt>programma</tt> e` senza spazi all'inizio o
alla fine ed ha un solo spazio tra istruzione e argomenti. Allo stesso modo il
nome delle variabili globali dentro alla struttura <tt>variabili_globali</tt>
e` senza spazi.
*/
void leggi_file(std::ifstream& in_file) {
string riga;
while(leggi_istruzione(in_file,riga)) {
cotrolla_etichetta(riga);
if(riga == "") {
// Riga vuota
}
else if(riga[0] == '.') {
// Direttiva
gestisci_direttiva(riga);
}
else{
// Istruzione
programma.add_instruction(riga);
}
} // end while(std::getline(infile,riga))
return;
} // end leggi_file(std::ifstream& in_file)
/*!
\fn bool leggi_istruzione(std::ifstream& file, string& str)
\brief Legge le istruzioni da file e le mette in str
\param file file-stream di input, aperto sul file da leggere
\param str stringa in cui mettere le istruzioni lette
\return <tt>false</tt>: errore di lettura o fine del file
\return <tt>true</tt>: letta e inserita una istruzione in <tt>str</tt>
Legge le istruzioni dal file-stream <tt>file</tt> e le inserisce nella stringa
<tt>str</tt>.\\
Ogni istruzione dev'essere separata da un "a-capo". Da ogni istruzione vengono
eliminati gli "spazi inutili", ovvero gli spazi iniziali e finali e gli spazi
doppi (lasciando ovviamente inalterate le stringhe all'interno delle
istruzioni).\\
Se si vuole cambiare il metodo di lettura delle istruzioni (ad esempio si
vuole permettere che stiano tutte su una riga, separate da spazi) e`
sufficente modificare solo questa funzione.
*/
bool leggi_istruzione(std::ifstream& file, string& str) {
// azzera str
str.clear();
char ch; // carattere letto da file
bool istr_stringa = false; // dentro o fuori a una stringa nell'istruzione
// legge l'istruzione da file
while(file.get(ch)) {
if(!istr_stringa && ch == '\t') {
// sostituisce le tabulazioni con spazi
ch = ' ';
}
if(ch == ' ') {
// spazio: elimina gli spazi inutili
if(!str.empty() && !istr_stringa && str[str.size()-1]==' ')
str.erase(str.size()-1);
}
else if(ch == '"') {
// virgolette: valuta se nell'istruzione vi e` una stringa
if(istr_stringa && str[str.size()-1]=='\\') {
// dentro una stringa c'e` la sequenza \"
// allora si rimane all'interno della stringa (istr_stringa=true)
}
else {
// dentro o fuori una stringa dell'istruzione
istr_stringa?istr_stringa=false:istr_stringa=true;
}
}
else if(ch == '\n') {
// a-capo: se e` fuori da una stringa e` la fine dell'istruzione
if(!istr_stringa) {
// fine istruzione
elimina_spazi_iniziali_e_finali(str);
return true;
}
} // end else if(ch=='\n')
// inserisce il carattere letto nella stringa
str.push_back(ch);
} // end while(file.get(ch))
// fine del file
return false;
} // end leggi_istruzione(std::ifstream&, string&)
/*!
\fn void cotrolla_etichetta(string& str)
\brief Controlla se nell'istruzione c'e` un'ettichetta e la rende unica
\param str istruzione da controllare
Controlla se nell'istruzione e` presente un'ettichetta, in tal caso la rende
unica all'interno del programma aggiungendoci un numero relativo alla funzione
nella quale e` contenuta.\\
L'istruzione passata dev'essere senza spazi iniziali e finali e con un solo
spazio per separare gli argomenti.
*/
void cotrolla_etichetta(string& str) {
if(!str.empty()) {
// L'etichetta dev'essere sempre all'inizio dell'istruzione, siccome non ci
// sono spazi iniziali e finali, se c'e` uno spazio prima dei due-punti
// significa che non c'e` un'etichetta.
string::size_type pos_label = str.find(':');
string::size_type pos_space = str.find(' ');
bool etichetta = false;
char nf[3];
if(pos_label != string::npos &&
(pos_space == string::npos || pos_label < pos_space)) {
// c'e` un'etichetta
etichetta = true;
sprintf(nf, "_%d", n_funzioni);
str.insert(pos_label, nf);
}
string::size_type pos_istr = 0;
if(etichetta) {
pos_label = str.find(':');
++pos_label;
while(str[pos_label] == ' ')
++pos_label;
pos_istr = pos_label;
}
string::size_type pos_arg = str.find(' ',pos_istr);
if(pos_arg != string::npos) {
string istruzione;
istruzione = str.substr(pos_istr,pos_arg-pos_istr);
// istruzioni che hanno come argomento una etichetta
if( istruzione == "goto" ||
istruzione == "if_icmpeq" ||
istruzione == "if_icmpge" ||
istruzione == "if_icmpgt" ||
istruzione == "if_icmple" ||
istruzione == "if_icmplt" ||
istruzione == "if_icmpne" ||
istruzione == "ifeq" ||
istruzione == "ifge" ||
istruzione == "ifgt" ||
istruzione == "ifle" ||
istruzione == "iflt" ||
istruzione == "ifne"
) {
// sostituisce l'etichetta: siccome queste istruzioni hanno come
// argomento solo l'etichetta, e non ci sono spazi finali, allora si
// aggiunge il numero alla ifne dell'istruzione
sprintf(nf, "_%d", n_funzioni);
str.insert(str.size(), nf);
}
}
} // end if(!str.empty())
return;
}
/*!
\fn void gestisci_direttiva(const string& direttiva)
\brief In base alla direttiva passata esegue l'operazione appropriata
\param direttiva direttiva da gestire
Le direttive possono essere:
- <tt>.method public static <em>methodname</em> <em>descriptor</em></tt>
- <tt>.end method</tt>
- <tt>.field public static <em>fieldname</em> <em>descriptor</em></tt>
- <tt>.class public Main</tt>
- <tt>.super java/lang/Object</tt>
- <tt>.end class</tt>
devono essere senza spazi iniziali e finali e con un solo sapzio tra le
parole, in caso di errore viene lanciata un'eccezione di tipo std::string
con la descrizione dell'errore.
*/
void gestisci_direttiva(const string& direttiva) {
if(direttiva.substr(0,22) == ".method public static ") {
// Dichiarazione funzione
string nome_funzione = direttiva.substr(22);
cancella_carattere(nome_funzione,' ');
if(nome_funzione == "<clinit>()V")
funzione_clinit = true;
programma.add_instruction(direttiva);
++n_funzioni;
}
else if(direttiva == ".end method") {
// Fine funzione
programma.add_instruction(direttiva);
}
else if(direttiva.substr(0,21) == ".field public static ") {
// Variabile globale
inserisci_variabile_globale(direttiva);
}
else if(direttiva == ".class public Main") {
// Prima direttiva d'inizio del programma
}
else if(direttiva == ".super java/lang/Object") {
// Seconda direttiva d'inizio del programma
}
else if(direttiva == ".end class") {
// Fine del programma
}
else{
throw string("direttiva sconosciuta: " + direttiva);
}
return;
} // end gestisci_direttiva(const string& direttiva)
/*!
\fn void inserisci_variabile_globale(const string& direttiva)
\brief Inserisce la variabile globale in <tt>variabili_globali</tt>
\param direttiva direttiva contenente la variabile globale
Estrapola la variabile globale dalla direttiva passata e la inserisce in
<tt>variabili_globali</tt>. La direttiva dev'essere nella forma
<tt>.field public static <em>fieldname</em> <em>descriptor</em></tt>, con un
solo spazio tra gli argomenti e senza spazi iniziali o finali. In caso di
errore lancia un'eccezione di tipo std::string con la descrizione dell'errore.
*/
void inserisci_variabile_globale(const string& direttiva) {
string fieldname, descriptor;
// ricava il nome della variabile
string::size_type pos_name = direttiva.find(' ', 21);
if(pos_name != string::npos) {
fieldname = direttiva.substr(21,pos_name-21);
// ricava il nome del descrittore
descriptor = direttiva.substr(pos_name+1);
if(descriptor == "S")
variabili_globali.add_variable_S(fieldname);
else if(descriptor == "C")
variabili_globali.add_variable_C(fieldname);
else if(descriptor == "I")
variabili_globali.add_variable_I(fieldname);
else if(descriptor == "J")
variabili_globali.add_variable_J(fieldname);
else {
throw string("tipo sconosciuto nella variabile globale: " + direttiva);
}
} // end if(pos_name != string::npos)
else {
throw string("manca il tipo nella variabile globale: " + direttiva);
}
return;
} // end inserisci_variabile_globale(const string& direttiva)
/*!
\fn void pulisci_stringa(string& str)
\brief Elimina gli spazi inutili alla stringa passata
\param str stringa da "ripulire"
Elimina gli spazi iniziali e finali (trim) dalla stringa <tt>str</tt> e
rimuove tutti gli spazi consecutivi all'interno della stringa lasciando solo
spazi singoli.
*/
void pulisci_stringa(string& str) {
// eliminazione spazi iniziali e finali (trim)
elimina_spazi_iniziali_e_finali(str);
// eliminazione spazi doppi
string::size_type pos = str.find(" ");
while(pos != string::npos) {
str.erase(pos, 1);
pos = str.find(" ", pos);
}
return;
} // end pulisci_stringa(string& str)
/*!
\fn void elimina_spazi_iniziali_e_finali(string& str)
\brief Elimina gli spazi iniziali e finali nella stringa (trim)
\param str stringa da "ripulire"
Elimina gli spazi iniziali e finali dalla stringa <tt>str</tt> (funzione
trim).
*/
void elimina_spazi_iniziali_e_finali(string& str) {
string::size_type pos = str.find_last_not_of(' ');
if(pos != string::npos) {
str.erase(pos + 1);
pos = str.find_first_not_of(' ');
if(pos != string::npos) str.erase(0, pos);
}
else {
str.erase(str.begin(), str.end());
}
return;
}
/*!
\fn void cancella_carattere(string& str, char c)
\brief Elimina tutte le occorrenze del carattere passato dalla stringa
\param str stringa da "ripulire"
\param c carattere da eliminare dalla stringa
Elimina dalla stringa <tt>str</tt> tutte le occorrenze del carattere passato
<tt>c</tt>, di default ' ' (spazio bianco).
*/
void cancella_carattere(string& str, char c) {
string::size_type pos = str.find(c);
while(pos != string::npos) {
str.erase(pos, 1);
pos = str.find(c, pos);
}
return;
}