Relazione Progetto
Informatica Teorica 2006/2007
Progetto Svolto da
Filippo De Santis
mat. 0000245081
Indice Argomenti :
- Linguaggio
- Scanner
- Parser
- Inteprete
Linguaggio
Linguaggio :
Introduzione
Il linguaggio scelto per implementare questo progetto, comprende
costrutti molto simili al C e ad altri linguaggi della stessa famiglia.
Si è utilizzata la stessa forma del linguaggio C per le
dichiarazioni delle variabili, per l'assegnamento e le operazioni
aritmetiche. Per le operazioni condizionali invece si è
preferito utilizzare dei costrutti simili al C ma che definissero in
maniera più specifica l'inizio e la fine di tali operazioni
("if - endif" e "while - endwhile").
Per il costrutto che specifica la parallelizzazione di una o
più istruzioni si è creata la parola chiave
"parallelo". Le operazioni da eseguire tramite il costrutto "parallelo"
sono racchiuse tra parentesi graffe.
Infine, per quel che riguarda l'ordine in cui verranno valutate le
operazioni aritmetiche, esse saranno valutate da destra a sinistra
senza la possibilità di inserire parentesi o altri simboli
per la modifica dell'ordine di valutazione delle operazioni.
Linguaggio : La
Grammatica (Forma BNF)
Insieme dei Simboli Terminali : T = {begin, var, real, [(a..z)+(A..Z)+(0..9)*]+,
(0..9)+.(0..9)+
, end, parallelo, endp, while, endwhile, (, ), ;, = =,
>, <, >=, <=, <>, AND, OR, =,
+, *, /, -, if, else, endif, return};
Insieme dei Simboli Non Terminali : N = {INI, DIC, A, R, Ob, Oc, COD, CONDS, VARS, TYPE, NAME, OP_COND, OP_LOGIC, Vn,
Tn, VTn};
Insieme delle Produzioni : P
- <INI> ::= begin
<DIC> <A> <R> end
- <DIC> ::= <DIC>
<DIC>
- <DIC> ::= var
<TYPE> <NAME> ;
- <A>
::= <A> <A> | <Vn> =
<Ob> | <Oc>
| parallelo {COD}
- <COD> ::= <COD>
<COD> | <A> endp
- <Ob> ::= <Vn> ; |
<Tn> ;
- <Ob> ::= <Vn> +
<Ob> | <Tn> + <Ob>
- <Ob> ::= <Vn> *
<Ob> | <Tn> * <Ob>
- <Ob> ::= <Vn> /
<Ob> | <Tn> / <Ob>
- <Ob> ::= <Vn> -
<Ob> | <Tn> - <Ob>
- <Oc> ::= while (
<CONDS> ) <A> endwhile
- <Oc> ::= if ( <CONDS> ) <A>
endif
- <Oc> ::= if ( <CONDS> ) <A>
else <A> endif
- <CONDS> ::= <VTn>
<OP_COND> <VTn>
| <VTn> <OP_COND> <VTn>
<OP_LOGIC> <CONDS>
- <VTn> ::= <Vn> |
<Tn>
- <R> ::= return (
<VARS> ) ;
- <VARS> ::= <Vn> ; <VARS> |
<Vn>
- <TYPE> ::= real
- <NAME> ::= [(a..z)+(A..Z)+(0..9)*]+
- <Vn> ::= [(a..z)+(A..Z)+(0..9)*]+
- <Tn> ::= (0..9)+.(0..9)+
- <OP_COND> ::= = = | >= | <= |
> | < | <>
- <OP_LOGIC> ::= AND | OR
Le Produzioni più siognificative presenti in questa
grammatica sono quelle derivabili direttamente dal simbolo non
terminale "A".
Queste produzioni comprendono tutte le attività che si
possono sviluppare con questo linguaggio : operazioni di assegnamento
di un valore ad una variabile, operazioni condizionali e cicli,
operazioni di parallelizzazione del codice.
- Assegnamento: Da "A" derivano tutte le produzioni di terminali della
forma " <variabile> = <variabili o numero>
;" o "<variabile> = <variabile o numero>
<+,*,-, / > <variabile o nuemro> ... ;" .
Per la scrittura di queste espressioni di terminali viene coinvolto
anche il simbolo non terminale "Ob" da cui derivano le "operazioni base
o aritmentiche" presenti nel mio linguaggio.
- Condizioni e Cicli : Da "A" si possono derivare direttamente il
costrutto "IF" e il costrutto "WHILE". Il primo prevede due possibili
scritture, una con la presenza della keyword "else" al siuo interno e
una senza la keyword "else". Una particolarità di questo
costrutto è che se l' "if" prevede la presenza dell' "else"
allora ne può contenere solamente una occorrenza (per
eseguire degli "else if" cosecutivi l'unico modo è prevedere
una derivazione "if" da "A" successiva alla keyword "else"). Il secondo
costrutto, invece, ci dà la possibilità di
implementare dei cicli rispetto a una o più condizioni che
vengono valutate una prima volta prima di svolgere il costrutto "while"
e poi ad ogni ciclo di operazioni contenute tra le keyword "while" ed
"endwhile".
- Parallelizzazione : Infine da "A" si può derivare
l'operazione di parallelizzazione. Questa operazioni prevede la
presenza di una (banale) o più sezioni di codice da svolgere
in parallelo. In particolare, per come si è implementato
l'interprete, per ogni porzioni di codice presente nel costrutto
"parallelo" si svolgeranno le operazioni della sezione e poi tutte
quelle successive restituendo un risultato per ogni porzione di
istruzioni "parallelizzata".
Linguaggio :
Esempi
1) Calcolare il fattoriale di un numero e verificare se
è superiore o meno a "b"
L'esempio sotto riportato esegue queste operazioni:
- Simbolo di inizio del programma (begin)
- Dichiarazione variabili
- Assegnamento di un valore alle variabili appena dichiarate
- Parallelizzazione delle operazioni di assegnamento per la
variabile "b"
- Ciclo "while" per il calcolo del fattoriale del valore
della variabile "a"
- Verifica della condizione "a > b" e memorizzazione
di "1" o "0" in c
- Trono il valore di "c" che mi indica se "a > b" o
meno
Codice esempio:
begin
var real a;
var real b;
var real c;
a = 5;
c = a - 1;
parallelo{
b = 30;
endp
b = 200;
endp
}
while(c > 0)
a = a * c;
c = c - 1;
endwhile
if(a > b)
c = 1;
else
c = 0;
endif
return (c);
end
Il risultato dell'applicazione dell'interprete a questo listato di
operazioni è la produzione di due vettori di variabili:
V1(b = 30) : [a, 120.0] [b, 30.0] [c, 1.0]
V2 (b = 200): [a, 120.0] [b, 200.0] [c, 0.0]
Si nota che nel primo vettore la variabile "c" è ad 1,
quindi la condizione è verificata, mentre nel secondo
vettore la variabile "c" è 0 e la condizione a > b
non è verificata.
2) Calcolare la divisione di un numero per i primi 4 numero primi a
partire da 2:
Operazioni eseguite:
- Dichiarazioni delle variabili
- Valorizzazione delle variabili
- Istruzione di parallelizzazione e assegnamento a "b" di un
valore diverso ogni volta
- Calcolo della divisione tra "a" e "b"
- Restituzione del valore calcolato
Codice:
begin
var real a;
var real b;
var real c;
a = 100;
c = 0;
parallelo{
b = 2;
endp
b = 3;
endp
b = 5;
endp
b = 7;
endp
}
c = a / b;
return (c);
end
Questo secondo esempio prevede l'utilizzo solamente dei costrutti "if"
e "parallelo". I vettori delle variabili risultanti
dall'applicazione dell'interprete a questo listato sono:
V1 : [a, 100.0] [b, 2.0] [c, 50.0]
V2 : [a, 100.0] [b, 3.0] [c, 33.333333333333336]
V3 : [a, 100.0] [b, 5.0] [c, 20.0]
V4 : [a, 100.0] [b, 7.0] [c, 14.285714285714286]
3) Calcolo della potenza n-esima di un numero:
Operazioni eseguite:
- Dichiarazioni delle variabili
- Assegnamento della base della potenza
- Parallelo per i valori dell'esponente
- calcolo potenza di "a" rispetto ad ogni esponente "pot"
- Restituzione del valore della potenza
Codice:
begin
var real pot;
var real a;
var real b;
var real c;
a = 2;
c = 1;
parallelo{
pot = 4;
endp
pot = 8;
endp
pot = 12;
endp
pot = 16;
endp
}
b = pot;
while(b > 0)
c = c*a;
b = b - 1;
endwhile
return (c);
end
In questo ultimo esempio abbiamo utilizzato il costrutto parallelo e il
costrutto while per il calcolo della potenza di un numero rispetto a
diversi valori dell'esponente. I vettori delle variabili per ogni
sezione di codice parallelo risulteranno:
V1 : [pot, 4.0] [a, 2.0] [b, 0.0] [c, 16.0]
V2 : [pot, 8.0] [a, 2.0] [b, 0.0] [c, 256.0]
V3 : [pot, 12.0] [a, 2.0] [b, 0.0] [c, 4096.0]
V4 : [pot, 16.0] [a, 2.0] [b, 0.0] [c, 65536.0]
Scanner
Scanner :
Introduzione
Lo scanner implementato esegue le operazioni di lettura dei token dal
file e di memorizzazione di questi token su uno stack.
Lo stack di token creato dallo scanner sarà poi utile al
parser per la valutazione delle produzioni e conterrà degli
oggetti identificati da un valore e un tipo ([abc Vn], [= OP_AS], [
> OP_COND], [OR OP_LOGIC], etc.).
All'interno dello scanner viene anche creato il vettore delle
variabili diachirate. Questo vettore è fondamentale per lo
svolgimento delle operazioni dell'interprete in quanto è
utilizzato per contenere il valore delle variabili del mio listato.
Inoltre lo scanner sfrutta questo vettore per verificare se una stringa
del listato è una variabile o meno, e se non lo è
viene segnalato un'errore.
Scanner : I Token
Per il riconoscimento dei token presenti nel mio listato ho utilizzato
diversi automi. In particolare questi automi andavano a valutare le
stringhe che incontravo e in base al loro primo carattere le
categorizzavano come numeri, stringhe, condizioni, keyword, etc.
1) Riconoscimento numeri: Se il priomo carattere della mia stringa
è un numero, continuo a leggere gli altri carattere fintanto
che sono numeri o contengono una virgola seguita da almeno un numero.
2) Operazione "=" e "= =" : Se leggo il primo carattere ed è
un "=" vado a verificare se anche il successivo è un uguale.
Se non lo è ho un'operazine di assegnamento e memorizzo
l'oggetto corrispondete nello stack, altrimenti ho una operazione
condizionale e memorizzo sullo stack un simbolo "condizione"
con valore corrispondente alla stringa trovata.
3) Operazione ">" e "> =" : Analogamente al caso
precedente se leggo ">" ho sicuramente una operazione
condizionale ma devo verificare se ci sia o meno la presenza del
simbolo "=". Una volta verificata la presenza di "=" posso
memorizzare sullo stack un simbolo "condizione" con valore
corrispondente alla stringa trovata.
4) Operazione "<" e "< =" e "<> :
Analogamente al caso "3" valuto la presenza , dopo il simbolo
"<", dei simboli "=" o ">". Queste tre stringhe
rappresentano tutte una condizione, quindi verificata quale delle
stringhe mi trovo a leggere memorizzo sullo stack un simbolo
"condizione" con valore corrispondente alla stringa trovata.
5) Riconoscimento variabili e keyword: Se trovo un carattere
dell'alfabeto dia minuscolo che maiuscolo posso applicare l'utoma per
il riconoscimento delle stringhe. Una volta ottenuta la stringa vado a
valutare se questa è una variabile, controllando nel vettore
delle variabili dichiarate, o se è una keyword
confrontandola con le keyword utilizzabili nel mio linguaggio. Se la
stringa non corrisponde a nessuna di queste due tiipologie di strnghe
il programma viene bloccato e viene segnalato un errore.
Scanner :
Descrizione Classi
Scanner.java
All'interno di questa classe viene letto il file e vengono implementati
gli automi per il riconoscimento delle stringhe, dei terminali numerici
e dei terminali come parentesi, operazioni algebriche e operazioni
condizionali.
Attributi della classe:
- SchemaListato
schema : Oggetto utilizzato per la memorizzazione dei
token e la verifica che questi appartengano al linguaggio
La classe contiene i seguenti metodi:
- public
Scanner() : E' il costruttore della classe;
- public
boolean cercaToken(String percorso) : Metodo per far
partire la ricerca dei token; la stringa "percorso" viene utilizzata
per indicare il path del file da cui ricavare i token. All'interno di
questo metodo vengono applicati gli automi per il riconoscimento dei
token che poi vengono passati alla classe SchemaListato.java;
- Input : String percorso; il
parametro indica il path del file su cui eseguire la scansione alla
ricerca dei token del linguaggio.
- Output : "true" se la scansione va a buon fine, "false" altrimenti.
- public
Stack<StackObject> get_stack() : Metodo che
restituisce lo stack dei token;
- Input : Nessuno input;
- Output : Ritorna lo stack dei token utilizzato poi dal parser.
- public VarID
getVarIDList() : metodo che restituisce il vettore delle
variabili dichiarate;
- Input : Nessun input.
- Output : Rotorna il vattore delle variabili dichiarate.
SchemaListato.java
All'interno della classe SchemaListato.java vengono eseguiti i
controlli sulle dichiarazioni delle variabili e vengono memorizzati i
token nello stack che poi verrà passato al parser. La classe
contiene come attributi l'array delle keyword e l'array delle
operazioni condizionali del linguaggio.
Attributi della classe:
- String
parole_chiave[] : array delle keyword del mio linguaggio
- String
op_logic[] : array delle operazioni logiche del mio
linguaggio
- VarID
variabili : array delle variabili dichiarate
- MemorizzaElementoListato
memo : Oggetto a cui viene delegata la memorizzazione dei
token su file
- Stack<StackObject>
stack : Oggetto Stack in cui memorizzare i token
La classe contiene i seguinti metodi:
- SchemaListato()
: Costruttore;
- public void
putElemento(String tipo, String value, int riga) : Metodo
per l'inserimento di un token nell'array dei token; Il nome del token
è dato dalla variabile "name", il valore dalla variabile
"value" ed infine il parametro "riga" indica la riga in cui
è stato trovato il token ed è utilizzata per
indicare, se si verificano errori, che token si stava valutando e in
che riga si trovava. All'interno di questo metodo viene anche valutato
quando si sta leggendo una dichiarazione di una variabile (serie di
token "var", "real" "<name>" e ";"), se la verifica va a
buon fine la variabile viene memorizzata nel vettore delle variabili
dichiarate. Infine per ogni stringa che non corrisponde ad una keyword
del mio linguaggio si valuta se è una variabile dichiarata e
se non lo è si segnala un errore.
- Input : String tipo, String value,
Int riga; i parametri identifica l'elemento da inserire e la riga in
cui è stato trovato.
- Output : Nessun output.
- public VarID
getVarIDList() : Restituisce il vettore delle variabili
dichiarate creato nel metodo precedente;
- Input : Nessun input.
- Output : VarID; Ritorna l'elenco delle variabili dichiarate;
- public
Stack<StackObject> get_stack() : Restituisce
lo stack dei token creato nel metodo "putElemento";
- Input : Nessun input.
- Output : Stack; Ritorna lo stack degli elementi da far valutare al parser.
VarID.java
Classe utilizzata all'interno di SchemaListato.java. Questa classe
descrive un oggettro tramite cui memorizzare le variabili dichiarate in
un vettore.
Attributi della classe:
- Variabili[]
variabili_listato : Array dove memorizzo il vettore delle
variabili dichiarate
- int ind_var
: numero di variabili memorizzate
La classe contiene i seguenti metodi:
- public
VarID() : Metodo costruttore, inizializza l'array delle
variabili.
- public
boolean put_varID_stringa(String var_name, String var_type, String
value, boolean output) : Inserisce una variabili di nome
"var_name", di tipo "var_type" con valore "value" nell'array delle
variabili dichiarate. Inoltre il parametro "output" indica se questa
variabile dovrà essere stampata a video o meno al momento
dell'interpretazione dell'istruzione "return".
- Input : String var_name, String var_type, String
value, boolean output; parametri per la creazione di una nuova variabile.
- Output : boolean; restituisco "true" se l'inserimento va a buon fine, "false" altrimenti
- public
boolean setValueVar(String var_name, String value) :
Aggiornamento del valore della variabile "var_name" con il valore
"value";
- Input : String var_name, String value; parametri di aggiornamento per la variabiel "var_name";
- Output : boolean; restituisco "true" se l'aggiornamento va a buon fine, "false" altrimenti
- public
String getVarValue(String name) : Restituisce il valore
della variabile "name";
- Input : String name; parametro che indica la variabile di cui restituire il valore.
- Output : String; valore della variabile "name".
- public
Variabili getVar(String var_name) : Restituisce un
oggetto di tipo "Variabili" descritto dalla classe "Variabili.java".
Questo oggetto contiene tutte le caratteristiche di una variabile
dichiarata;
- Input : String var_name; parametro che indica la variabile da restituire.
- Output : Variabili; oggetto che cnotiene le caratteristiche della variabile "var_name".
- public
boolean isSet(String var_name) : Verifico se esiste una
variabile di nome "var_name";
- Input : String var_name; parametro che indica la variabile da cercare.
- Output : "true" se trovo la variabile, "false" se non la trovo
- public
boolean isOutput(String var_name) : Verifico se la
variabile "var_name" è indicata come una variabile di output;
- Input : String var_name; parametro che indica la variabile da cercare.
- Output : valore "output" della variabile "var_name".
- public void
setOutput(String var_name) : Setto la variabile "var_name"
come una variabile di output;
- Input : String var_name; parametro che indica la variabile da cercare.
- Output : Nessun valore.
- public int
getNumeroVariabili() : Restituisco il numero di variabili
dichiarate;
- Input : Nessun valore.
- Output : int; numero di variabili memorizzate.
- public void
printVar() : Metodo utilizzato dall'interprete per
stampare le variabili di output e i loro valori alla fine
dell'esecuzione del listato;
- Input : Nessun valore.
- Output : Nessun valore.
- public VarID
clone() : Override del metodo "Object.clone()". Necessario
per l'implementazione del costrutto parallelo all'interno del quale si
devono "moltiplicare" i vettori delle variabili dichiarate per tenere
traccia di ogni opperazione parallela.
- Input : Nessun valore.
- Output : VarID; oggetto che restituisce una copia dell'oggetto su cui richiamo questo metodo.
MemorizzaElementoListato.java
Classe attraverso la quale andiamo a memorizzare su file ogni
oggetto memorizzato poi sullo stack.
Attributi della classe:
- File f
- FileOutputStream fos
- PrintStream
ps
La classe contiene i seguenti metodi :
- public
MemorizzaElementoListato() : Costruttore, inizializza il
file su cui andremo a tenere traccia dei token trovati;
- public
boolean inserisciElemento(String tipo, String value) :
Inserisce l'elemento di tipo "tipo" e valore "value" nello stack che
poi utilizzarà il parser e scrive l'elemento nel file di
riferimento dei token.
- Input : String tipo, String value; parametri da memorizzare su file;
- Output : boolean; "true" se la scrittura va a buon fine, "false" altrimenti.
Variabili.java
Classe utilizzata per descrivere gli oggetti che compongono il vettore
delle variabili dichiarate.
Attributi della Classe :
- String name
- String type
- String valore
- boolean
output : parametro che indica se una variabile va stampata
o meno alla conclusione dell'interpretazione del codice
La classe continene i seguenti metodi:
- public
Variabili() : Costruttore
- public
boolean setVar(String identificativo, String tipo) :
Utilizzato per modificare il nome e il tipo dell'oggetto;
- public
String getNameVar() : Restituisce il parametro "name";
- public
String getTypeVar() : Restituisce il parametro "type";
- public void
valorizza(String val) : Modifica il parametro "valore"
dell'oggetto;
- public
String getVal() : Restituisce il parametro "valore";
- public void
setOutput() : Setta a "true" il valore del parametro
"output";
- public
boolean isOutput() : Resistuisce il valore del parametro
"output";
Stack.java
Classe utilizzata per implementare uno stack i cui elementi possono
essere oggetti diversi. Questa classe infatti per mette di istanziare
uno stack formato da una tipologia di elementi qualsiasi. All'atto
della crazione dell'oggetto Stack sarà quindi necessario
esplicitare a quale classe apparterrano gli elementi di Stack. In
particolare ho crato una classe StackObject.java che descrive gli
oggetti che utilizzo.
Attributi della classe :
- ArrayList<E>
stack_token : Array di oggetti "E" che viene manipolato
come fosse una pila
La classe continene i seguenti metodi:
- public
Stack() : Costruttore
- public void
push(E item) : Inserimento di un elemento in cima alla
pila;
- public E
pop() : Rimuovo un elemento dalla cima della pila
- public int
nchild() : Restituisce il numero di elementi della pila
- public
Stack<E> clone() : Override del metodo
"Object.clone" utilizzato per la gestione del costrutto "parallelo" nel
parsing.
StackObject.java
La classe descrive gli oggetti che andrò ad utilizzare come
elementi dello stack dei token.
Attributi della classe:
- String type
: Descrizine del tipo di elemento memorizzato
- String value
: Valore dell'elemento memorizzato
La classe contiene i seguenti metodi:
- public
StackObject(String tipo, String valore) : Costruttore;
- public
String get_type() : Restituisce il parametro "type"
dell'elemento;
- public
String get_value() : Restituisce il parametro "value"
dell'elemento;
- public
StackObject get_object() : Restituisce l'elemento stesso;
Scanner :
Osservazione
E' importante osservare che la pila di token creata durante la fase di
scansionamento dei token del codice avrà come primo elemento
il corrispondente dell'ultimo token letto ( la parola chiave "end") e
come ultimo elemento il corrispondente del primo token letto (la parola
chiave "begin"). Andremo quindi a derivare le nostre produzione dai
terminali partendo dal "fondo" del codice.
Parser
Parser :
Introduzione
Il parser implementato è un parser Botto-Up. In particolare
l'implementazione è effettuata leggendo l'input da sinistra
a destra e andando a creare delle derivazione rightmost.
Ho deciso di implementare un parser con il metodo Bottom-Up prendendo
spunto dalla teoria dei parser LR che utilizzano uno stack in cui sono
memorizzati i token del mio linguaggio e da questi, per passi
successivi, si creano delle derivazioni rightmost che riducono lo stack
fino ad avere il solo nodo iniziale della grammatica analizzata. Per la
verifica dell'esattezza del linguaggio ho implementato degli automi che
riconoscessero i prefissi delle produzioni della grammatica e che sono
descritti al punto "Parser : La Grammatica".
Infine il parser produrrà, oltre alla verifica della
sintassi del cosdice, anche un albero di parsing che verrà
utilizzato dell'interprete.
Parser : La
Grammatica
La Grammatica utilizzata è la stessa descritta al punto
"Linguaggio : La Grammatica". All'interno del parsing ho sviluppato
degli automi per il riconoscimento delle varie porzioni del codice
tramite la lettura dei token memorizzati sulla pila creata durante la
fase di "scansione" del codice.
Descrivo ora gli automi implementati per il riconoscimento delle
produzioni della grammatica.
- Produzione : <R> ::= return (
<VARS> ) ;
- Estraggo dalla pila il token "end", altrimenti segnalo un errore;
- Stato R1 : Se estraggo il token ";" passo allo
stato R2, altrimenti segnalo un errore;
- Stato R2 : Se estraggo il token ")" passo allo stato R3, altrimenti segnalo un errore;
- Stato R3 : Se estraggo il token "Vn" passo allo stato R4, altrimenti segnalo un errore;
- Stato R4 : (Eseguo una riduzione a VARS )
- Se estraggo il token ";" passo allo stato R3, altrimenti segnalo un errore;
- Se estraggo il token "(" passo allo stato R5, altrimenti segnalo un errore;
- Stato R5 : Se estraggo il token "return" passo
allo stato accettante R6, altrimenti segnalo un errore;
- Stato R6 (accettante) : Eseguo una riduzione a "R" e
accetto l'input;
- Produzione : <DIC> ::= <DIC>
<DIC> | var
<TYPE> <NAME> ;
- Estraggo dalla pila il token ";", altrimenti segnalo un errore;
- Stato D1 : Se estraggo il token "NAME" passo allo stato D2, altrimenti segnalo un errore;
- Stato D2 : Se estraggo il token "TYPE" passo allo stato D3, altrimenti segnalo un errore;
- Stato D3 : Se estraggo il token "var" passo allo stato
accettante D4, altrimenti segnalo un errore;
- Stato D4 : Eseguo una riduzione a "DIC" e
accetto l'input;
- Produzione : <A>
::= <Vn> =
<Ob> | <Oc>
| parallelo {COD}
- Produzione
: <A>
::= <Vn> =
<Ob>
- Produzione : <Ob> ::= <Vn> ; |
<Tn> ; | <Vn> +
<Ob> | <Tn> + <Ob> | <Vn> *
<Ob> | <Tn> * <Ob> | <Vn> /
<Ob> | <Tn> / <Ob> | <Vn> -
<Ob> | <Tn> - <Ob>
- Estraggo dalla pila il token ";", altrimenti segnalo un errore;
- Stato AS1 : Se estraggo il token "Vn" o "Tn"
passo allo stato AS2, se estragggo "NAME" passo il controllo
all'automa per il riconoscimento delle dichiarazioni, altrimenti
segnalo un errore;
- Stato AS2 : (Eseguo una riduzione a Ob)
- Se estraggo uno dei token {+, *, /, - } passo
allo stato AS3, o
- Se estraggo il token "=" passo allo stato AS4, altrimenti segnalo un errore;
- Stato AS3 : Se estraggo il token "Vn" o "Tn" passo
allo stato AS2, altrimenti segnalo un errore;
- Stato AS4 : Se estaggo il token "Vn" passo allo stato
AS5 accettante, altrimenti segnalo un errore;
- Stato AS5 : Eseguo una riduzione ad A e accetto l'input;
- Produzione : <A>
::= <Oc>
- Produzione
: <Oc> ::= while (
<CONDS> ) <A> endwhile | if ( <CONDS> ) <A>
endif | if ( <CONDS> ) <A>
else <A> endif
- Estraggo il token "endwhile"
- Stato W1 : Estraggo il token
successivo e richiamo l'automa per le produzioni di A
- Se l'automa per le produzioni di A accetta ed
estraggo il token il token ")" passo allostato C1, altrimenti segnalo un errore;
- Prosuzioni : <CONDS> ::= <VTn>
<OP_COND> <VTn>
| <VTn> <OP_COND> <VTn>
<OP_LOGIC> <CONDS>
- Stato C1 : Se estraggo il token "Vn" o "Tn"
passo allo stato C2, altrimenti segnalo un errore;
- Staco C2 : Eseguo una riduzione a "VTn", se
estraggo un token {= =, >=, <=,
>, <, <>} passo allo stato C3, altrimenti segnalo un errore;
- Stato C3 : Se estraggo il token "Vn" o "Tn"
passo allo stato C4, altrimenti segnalo un errore;
- Stato C4 : (Eseguo prima una
riduzione a VTn e poi una riduzione a CONDS)
- Se estraggo un token {AND , OR} passo allo
stato C1 o
- Se estraggo il token "(" passo allo stato W2, altrimenti segnalo un errore;
- Stato W2 : Se estraggo il token "while" passo allo
stato W3 accettante, altrimenti segnalo un errore;
- Stato W3 : Eseguo una riduzione a Oc e accetto
l'input
- Estraggo il token "endif"
- Stato I1 : Estraggo il token successivo e richiamo
l'automa per le produzioni di A
- Se l'automa per le produzioni di A accetta ed
estraggo il token "else" passo allo stato I2, se estraggo ")" passo
allo stato C1, altrimenti segnalo un errore;
- Stato I2 : Estraggo il token successivo e richiamo
l'automa per le produzioni di A
- Se l'automa per le produzioni di A accetta ed
estraggo il token ")" passo allo stato C1, altrimenti segnalo un errore;
- Produzioni : <CONDS> ::= <VTn>
<OP_COND> <VTn>
| <VTn> <OP_COND> <VTn>
<OP_LOGIC> <CONDS>
- Stato C1 : Se estraggo il token "Vn" o "Tn"
passo allo stato C2, altrimenti segnalo un errore;
- Staco C2 : Eseguo una riduzione a "VTn", se
estraggo un token {= =, >=, <=,
>, <, <>} passo allo stato C3, altrimenti segnalo un errore;
- Stato C3 : Se estraggo il token "Vn" o "Tn"
passo allo stato C4, altrimenti segnalo un errore;
- Stato C4 : (Eseguo prima una
riduzione a VTn e poi una riduzione a CONDS)
- Se estraggo un token {AND , OR} passo allo
stato C1 o
- Se estraggo il token "(" passo allo stato I3, altrimenti segnalo un errore;
- Stato I3 : Estraggo il token "if" e passo allo
stato I4 accettante, altrimenti segnalo un errore;
- Stato I4 : Eseguo una riduzione a "Oc" e accetto
l'input;
- Produzione : <A>
::= parallelo
{COD}
- Stato
P1 : Estraggo il token "}" e passo allo stato P2, altrimenti segnalo un errore;
- Stato
P2 : Estraggo il token "endp" e passo allo stato COD1, altrimenti segnalo un errore;
- Prosuzione
: <COD> ::= <COD>
<COD> | <A> endp
- Stato COD1 : Estraggo il token successivo e
richiamo l'automa per le produzioni di A
- Se l'automa per le produzioni di A accetta passo
allo stato COD2, altrimenti segnalo un errore;
- Stato COD2 : (Eseguo una riduzione ad "A")
- Estraggo il token "endp" e passo allo stato COD1
o
- Estraggo il token "{" e passo allo stato P3, altrimenti segnalo un errore;
- Stato P3 : Estraggo il token "parallelo" e passo allo
stato P4 accettante, altrimenti segnalo un errore;
- Stato P4 : Eseguo una riduzione ad "A" e accetto l'input
Nella fase di implementazione del parser gli automi sono stati
"adattati" alla programmazione e al riconoscimento di istruzioni
successive. Ad esempio l'automa che riconosce le produzioni di
assegnamento ("<A>
::= <Vn> =
<Ob>")
viene impiegato fintanto che si ha un token ";" come primmo token e non
si è arrivati alla dichiarazione delle variabili. Anche gli
altri automi saranno richiamati tante volte quanti sono i token trovati
che portano al loro stato iniziale.
Come dicevo nella paragrafo di introduzione, durante il parsing dei
token viene creato l'albero di parsing (descrizione dettagliata nel
paragrafo "Parsing"). In particolare all'albero vengono aggiunti dei
nodi ogni qualvolta si effettua una riduzione da un terminale ad una
variabile o da una produzione ad una variabile della mia
grammatica.
Paerser : Le
Classi
Per implementare il parser abbiamo utilizzato la classe Parser.java.
All'interno di questa classe abbiamo implementato diversi metodi per il
parsing delle diverse produzioni.
Parser.java
Attributi della classe:
- Stack<StackObject>
stack_parsing : Stack dei token da ridurre alle produzioni
della grammatica;
- NodoAlbero
ini : Nodo "root" dell'albero di parsing;
- NodoAlbero
ret
: (figlio di ini) Radice del sottoalbero di parsing del
costrutto "return";
- NodoAlbero a
: (figlio di ini) Radice del sottoalbero di parsing delle produzioni di
"A";
- VarID
variabiliDaControllare : Vettore delle variabili
dichiarate, è utilizzato nel parsing della produzione
"return" per settare le variabili da stampare una volta interpretato il
codice;
La classe contiene i seguenti metodi:
- public
Parser() : Costruttore
- public
boolean go_parsing(Stack<StackObject> stack, VarID
variabili) : Metodo richiamato per far partire il parsing.
In input deve ricevere lo stack da cui estrarre i token e il vettore
delle variabili a cui, durante il parsing della sezione di codice
"return", potrebbero essere modificato il campo "output".
- Input : Stack<StackObject> , VarID
- Output : true se il parsing non ha trovato errori nella
riduzione delle produzioni, false se il codice non rispetta la
grammatica
- private
boolean parsing() : Metodo in cui viene
implementato il riconoscimento della produzione "<INI> ::= begin
<DIC> <A> <R> end";
viene valutato se esiste il token "begin" all'inizio del listato, se
esistono le dichiarazioni e sono corrette, se esistono e sono corrette
le attività, se esiste ed è corretto il costrutto
"return" e infine se esiste il token "end".
- Input : Nessun input
- Output : true se il parsing della prosuzione "INI" si
è svolto senza errori, false altrimenti
- private
boolean return_automaton() : Metodo per il parsing delle
produzioni della variabile "R".
- Input : Nessun input
- Output : true se il parsing della produzione "R" si
è svolto senza errori, false altrimenti
- private
boolean attivita_automaton() : Metodo per il parsing
delle produzioni di della variabile "A".
- Input : Nessun input
- Output : true se il parsing delle produzioni di "A" si
è svolto senza errori, false altrimenti
- private
boolean dichiarazioni_automaton() : Metodo per il parsing
delle produzioni della variabile "DIC"
- Input : Nessun input
- Output : true se il parsing della produzione "DIC" si
è svolto senza errori, false altrimenti
- private
NodoAlbero assegnamento(Stack<StackObject> stackOp)
: Implementazione dell'automa per la produzione "<A>
::= <Vn> =
<Ob>";
all'interno di questo metodo vengono implementati i controlli per
verificare se si è in presenza di una dichiarazione
piuttosto che di una operazione di assegnamento e viene implementata la
creazione dei nodi che rappresentano operazioni di assegnamento da
inserire nell'albero di parsing che verrà utilizzato
dall'inteprete.
- Input: Stack<StackObject> , il
parametro indica su che stack dovremmo andare ad estrarre i
token che valutiamo. Si effettua il passaggio di questo stack per
mantenere un corretto funzionamento del nostro programma anche se
l'operazione è all'interno o successiva all'applicazione del
costrutto "parallelo".
- Output : NodoAlbero , restituiamo il nodo dell'operazione
di assegnamento che abbiamo verificato essere corretta, o il nodo
radice di un sottoalbero formato da una serie di operazioni di
assegnamento adiacenti.
- private
NodoAlbero parallelo(Stack<StackObject> stackOp)
: Metodo per l'implementazione dell'automa che riconosce le produzioni
di "<A>
::= parallelo
{COD}"; oltre alla verifica della correttezza delle
produzioni contenute all'interno del costrutto parallelo si crea anche
il nodo "parallelo" per l'albero di parsing utilizzato poi
dall'interprete.
- Input: Input: Stack<StackObject> ,
il parametro indica su che stack
dovremmo andare ad estrarre i token che valutiamo. Si effettua
il
passaggio di questo stack per mantenere un corretto funzionamento del
nostro programma anche se l'operazione è successiva
all'applicazione del costrutto "parallelo".
- Output : NodoAlbero , restituiamo il nodo della
produzione "<A>
::= parallelo
{COD}" che
abbiamo verificato essere corretta. Questo sarà il nodo
radice dei sottoalberi che rappresentano le varie istruzioni messe in
parallelo.
- private
NodoAlbero se(Stack<StackObject> stackOp) :
Metodo per l'implementazione dell'automa che riconosce le produzioni
"<A>
::= <Oc>"
ed in particolari per la produzione "<Oc> ::= if (
<CONDS> ) <A> else <A>
endif | if ( <CONDS> ) <A>
endif".
- Input : Stack<StackObject> , il parametro
indica su che stack
dovremmo andare ad estrarre i token che valutiamo. Si effettua
il
passaggio di questo stack per mantenere un corretto funzionamento del
nostro programma anche se l'operazione è successiva
all'applicazione del costrutto "parallelo".
- Output : NodoAlbero , restituiamo il nodo della
produzione "<Oc> ::= if (
<CONDS> ) <A> else <A>
endif | if ( <CONDS> ) <A>
endif"
che
abbiamo verificato essere corretta.
- private
NodoAlbero mentre(Stack<StackObject> stackOp)
: Metodo per l'implementazione dell'automa che riconosce le
produzioni "<A>
::= <Oc>"
ed in particolari per la produzione "<Oc> ::= while (
<CONDS> ) <A> endwhile".
- Input : Stack<StackObject> , il parametro
indica su che stack
dovremmo andare ad estrarre i token che valutiamo. Si effettua
il
passaggio di questo stack per mantenere un corretto funzionamento del
nostro programma anche se l'operazione è successiva
all'applicazione del costrutto "parallelo".
- Output : NodoAlbero , restituiamo il nodo della
produzione "<Oc> ::= while (
<CONDS> ) <A> endwhile"
che
abbiamo verificato essere corretta.
- private
NodoAlbero conds(Stack<StackObject> stackOp)
: Metodo per l'implementazione dell'automa che riconosce le produzioni "<CONDS> ::= <VTn>
<OP_COND> <VTn>
| <VTn> <OP_COND> <VTn>
<OP_LOGIC> <CONDS>". Questo
particolare metodo è richiamato all'interno delle operazioni
condizionali "if" e "while" per valutare la correttezza delle
condizioni espresse in quei costrutti.
- Input : Stack<StackObject> , il parametro
indica su che stack
dovremmo andare ad estrarre i token che valutiamo. Si effettua
il
passaggio di questo stack per mantenere un corretto funzionamento del
nostro programma anche se l'operazione è successiva
all'applicazione del costrutto "parallelo".
- Output : NodoAlbero , restituiamo il nodo della
produzione "<CONDS> ::= <VTn>
<OP_COND> <VTn>
| <VTn> <OP_COND> <VTn>
<OP_LOGIC> <CONDS>"
che
abbiamo verificato essere corretta.
- public
NodoAlbero get_albero() : Metodo utilizzato
dall'interprete per ottenere l'albero di parsing da utilizzare.
- Input : Nessun input.
- Output : Radice dell'albero di parsing.
- public VarID
getVariabili() : Metodo che restituisce il vettore delle
variabili dichiarate aggiornate dopo la valutazione delle produzioni di
"R".
- Input : Nessun input.
- Output : Vettore variabili dichiarate.
Interprete
Inteprete :
Introduzione
L'interprete implementato sfrutta la navigazione dell'albero di parsing
per interpretare il codice inserito. In particolare questo interprete
parte dal nodo radice dell'albero di parsing (nodo corrispondente alla
produzione "<INI> ::= begin
<DIC> <A> <R> end")
e ne naviga ricorsivamente tutti i figli: scorre l'albero fino alle
produzioni che riguardano dei terminali e poi si risale aggiornando il
valore delle variabili.
Una particolarità del nostro Albero di parsing è
che i figli dei nodi vengono letti da destra a sinistra.
Interprete :
Architettura e Strutture Dati
La struttura dati che utilizzo per
l'interpretazione del codice è un albero di nodi che
rappresentano le operazioni che devo interpretare. Questo albero
è stato creado durante la fase di parsing. L'elemento generico
del mio albero è un elemento di tipo NodoAlbero di seguito
descritto:
NodoAlbero Generale
| Nome : Stringa |
Tipo : Stringa |
Figli : Array
NodoAlbero |
HasChild : boolean |
Descrivio ora le strutture dati generali che creo durante la fase di
parsing (assemblando l'albero), e navigo durante l'interpretazione:
NodoAlbero INI :
| Nome: ini |
Tipo : root |
Figli : {"end", RET, A, "begin"} |
HasChild : true |
Descrive il nome e il tipo del nodo che fa da radice al mio albero di
parsing. I suoi figli sono 4 : (da destra) il nodo che descrive l'inzio
del codice da interpretare "begin", il nodo "A" che indica la radice di
tutte le attività presenti nel listato, il nodo "RET" che
rappresenta l'operazione di "return" delle variabili ed infine il nodo
"end" che descrive la fine del mio codice da interpretare.
NodoAlbero RET :
| Nome : return |
Tipo : return |
Figli : {VARS} |
HasChild : true |
Nodo che viene interpretato quando si sono interpretate tutte le attività e che contiene un solo figlio "VARS".
NodoAlbero VARS:
| Nome : vars |
Tipo : VARS |
Figli : {Vn}* |
HasChild : {true, false} |
Nodo che contiene una serie di figli che indicano le variabili
diachiarate che devono essere riportate in output, nel nostro caso
stampate a video.
NodoAlbero A :
| Nome : A |
Tipo : attivita |
Figli : {Assegnamento, Parallelo, IF, IF-ELSE, WHILE}+ |
HasChild : true |
Nodo radice del sottoalbero delle attività da interpretare.
Questo nodo ha una serie non ordinata e un numero non fissato di figli.
Questi figli saranno nodi di tipo "assegnamento", "Oc" o "Parallelo".
Da questo no parte lì'interpretazione vera e propria dell'albero
di parsing, visto che sono queste le operazioni più complesse da
interpretare.
NodoAlbero Assegnamento :
| Nome : AS |
Tipo : Assegnamento |
Figli : {Assegnamento?, Ob,
OP_AS,Vn} |
HasChild : true |
Nodo che può contenere al suo interno una operezione di
assegnamento ed eventualmente un nuovo nodo "assegnamento". Questo nodo
quindi descrive o la radice di un albero di nodi "assegnamento" o uno
dei nodi interni o una foglia di questo sottoalbero.
NodoAlbero Ob:
| Nome: Ob |
Tipo : Ob |
Figli : {{Ob, {+, -, *, /}}?,
{Vn,Tn}} |
HasChild : true |
Il nodo in questione è analogo al nodo assegnamento, solo che
viene descritto in modo diverso. I suoi figli possono essere o una
coppia formata da un nodo Ob e una delle operazioni aritmetiche, o da
un valore terminale "Vn" o "Tn". L'interpretazione di una serie di
questi nodi porta a svolgere una serie di operazioni aritmetiche su
variabili e/o su numeri.
NodoAlbero Parallelo:
| Nome : parallelo |
Tipo : COD |
Figli : {As, Ap}* |
HasChild : true |
Il nodo parallelo creato nel momento del parsing è formato da
una serie di coppie di nodi. Entrambi sono dei nodi attività
(quindi insiemi di operazioni come assegnameto, while, etc.) ma quello
indicato con Ap è la radice delle operazioni di una delle porzioni di codice da "parallelizzare", mentre As
è la radice delle operazioni da svolgere a partire dalla prima
operazione che si incontra superando il costrutto "parallelo".
In pratica si replica in As quello che è l'albero restante di
operazioni successive al nodo "parallelo" e lo si fa tante volte quante
sono le porzioni di codice da parallelizzare.
NodoAlbero IF:
| Nome : IF |
Tipo : Oc |
Figli : {A, CONDS} |
HasChild: true |
Nodo radice del sottoalbero i cui figli descrivo le operazioni da
interpretare (nodo attività) all'interno dell' "if" se,
interpretato il nodo "CONDS", le condizioni risultano verificate.
NodoAlbero IF-ELSE :
| Nome : IF-ELSE |
Tipo: Oc |
Figli : {A, else, A, CONDS} |
HasChild : true |
Nodo radice del sottoalbero i cui figli descrivo le operazioni da interpretare subito dopo l' "if" se, interpretato il
nodo "CONDS", le condizioni risultano verificate, altrimenti si interpretano le attività successive al nodo "else".
NodoAlbero WHILE:
| Nome: while |
Tipo : Oc |
Figli : {A, CONDS} |
HasChild : true |
Nodo radice del sottoalbero i cui figli descrivono le operazioni da
ninterpretare se e fino a che le condizioni del nodo "CONDS" verngono
verificate.
NodoAlbero CONDS:
| Nome : conds |
Tipo: conds |
Figli : {{CONDS, OP_LOGIC, CONDS}, {VTn, OP_COND, VTn}} |
HasChild : ture |
Nodo che descrive le condizioni da valutare in ogni operazione
condizionale "if" o "while". Queste condizioni possono essere formate o
da tre nodi che indicano 3 simboli terminali VTn, OP_COND,
VTn, oppure da tre nodi che indicano rispettivamente : un nodo CONDS,
un simbolo terminale (operatore logico) e un secondo nodo CONDS. In
questo secondo caso si dovranno sviluppare i nodi CONDS trovati e
interpretare le condizioni al loro interno per poi poter tornare a
valutare il nodo precedente.
Interprete : Esecuzione
Istruzioni per avviare il programma:
- Assicurarsi di aver compilato senza errori il package "Scanner" o comunuque tutti i file della cartella "Scanner"
- Aprire il prompt dei comandi;
- Entra nella cartella dove è contenuto il file "Main.java"
- Scrivi il comando "java Main <percorso_file>", sostituendo
a <percorso_fiile> il path del file con il codice da sperimentare
- Lancia il comando e il programma è in esecuzione
Se non fosse possibile compilare i sorgenti, viene
fornito anche un file “scanner.jar” che contiene l’intera applicazione. Tramite
il comando “java –jar scanner.jar” si avrà comunque il mio interprete in
esecuzione.
Una volta che il programma termina la sua esecuzione vengono stampati a
video i risultati richiesti, se si vuole avere una visione completa
delle variabili in uso e del loro risultato finale, si deve aprire il
file di nome "reportVariabili" creato nella cartella di esecuzione del
programma.
Interprete : Esempi
Un primo semplice esempio di codice che sfrutta il costrutto
"parallelo" e da cui si ottengono risultati diversi è il
seguente:
Codice esempio 1:
begin
var real a;
var real b;
var real c;
a = 5;
c = a - 1;
parallelo{
b = 30;
endp
b = 200;
endp
}
while(c > 0)
a = a * c;
c = c - 1;
endwhile
if(a > b)
c = 1;
else
c = 0;
endif
return (c);
end
Il risultato dell'applicazione dell'interprete a questo listato di
operazioni è la produzione di due vettori di variabili:
V1(b = 30) : [a, 120.0] [b, 30.0] [c, 1.0]
V2 (b = 200): [a, 120.0] [b, 200.0] [c, 0.0]
Questo primo esempio è stato già descritto in precedenza
(Linguaggio : Esempi), ma è comunque un buon punto i partenza
per la comprensione di come il costrutto "parallelo" possa essere
applicato all'interno del nostro codice per esaminare più
scenari ogniuno legato ad una o più modifiche di una o
più variabili.
Gli altri due esempi presentati sotto sono esempi in cui il costrutto
"parallelo" viene utilizzato proprio per valutare, rispetto al
cambiamento di una variabile, quali sono gli effetti su i risultati
finali dell'interpretazione del nostro codice. Nel terzo esempio
inoltre ci sono due operazioni di aprallelizzazione successive, ed
è importante notare come la seconda operazioni di
parallelizzazione venga applicata ad ogni ramo della prima per ottenere
un aumento esponenziale delle possibili soluzioni.
Codice Esempio 2:
begin
var real a;
var real b;
var real c;
a = 100;
c = 0;
parallelo{
b = 2;
endp
b = 3;
endp
b = 5;
endp
b = 7;
endp
}
c = a / b;
return (c);
end
I vettori delle variabili risultanti
dall'applicazione dell'interprete a questo listato sono:
V1 : [a, 100.0] [b, 2.0] [c, 50.0]
V2 : [a, 100.0] [b, 3.0] [c, 33.333333333333336]
V3 : [a, 100.0] [b, 5.0] [c, 20.0]
V4 : [a, 100.0] [b, 7.0] [c, 14.285714285714286]
Codice Esempio 3:
begin
var real pot;
var real a;
var real b;
var real c;
a = 2;
c = 1;
parallelo{
pot = 4;
endp
pot = 8;
endp
pot = 12;
endp
pot = 16;
endp
}
b = pot;
while(b > 0)
c = c*a;
b = b - 1;
endwhile
parallelo{
pot = 2;
endp
pot = 3;
endp
}
b = pot;
while(b > 0)
c = c*a;
b = b - 1;
endwhile
return (c);
end
In questo ultimo esempio abbiamo utilizzato il costrutto parallelo e il
costrutto while per il calcolo della potenza di un numero rispetto a
diversi valori dell'esponente. I vettori delle variabili per ogni
sezione di codice parallelo risulteranno:
V1 : [pot, 2.0] [a, 2.0] [b, 0.0] [c, 64.0]
V2 : [pot, 3.0] [a, 2.0] [b, 0.0] [c, 128.0]
V3 : [pot, 2.0] [a, 2.0] [b, 0.0] [c, 1024.0]
V4 : [pot, 3.0] [a, 2.0] [b, 0.0] [c, 2048.0]
V5 : [pot, 2.0] [a, 2.0] [b, 0.0] [c, 16384.0]
V6 : [pot, 3.0] [a, 2.0] [b, 0.0] [c, 32768.0]
V7 : [pot, 2.0] [a, 2.0] [b, 0.0] [c, 262144.0]
V8 : [pot, 3.0] [a, 2.0] [b, 0.0] [c, 524288.0]
Interprete : Le Classi
Interprete.java
La classe Interprete.java è la classe principale dove
viene implementato il metodo di navigazione dell'albero di
parsing.
Atributi della classe :
- VarID VariabiliDichiarate : Vettore dove sono memorizzate le
variabili dichiarate; questo vettore è utilizzato sia per
ottenere il valore delle variabili che per modificarlo (ad esempio in
seguito ad operazioni di assegnamento)
- ReportInterprete report
: Oggetto che viene utilizzato per la scrittura su file del vettore
delle variabili una volta eseguite tutte le operazioni richieste.
- int MaxCicli : Intero
che indica il numero massimo di cicli che un'istruzione while
può eseguire, serve per prevenire i cicli infiniti.
- int livelloParallelo :
intero che indica quante istruzioni
"parallelo" sono state seguite; utilizzato per capire a che
profondità si è con l'interpretazione di una istruzione
parallelo.
La classe contiene i seguenti metodi
:
- public Interprete() : Costruttore;
- public boolean interpreta(NodoAlbero radice, VarID schemaVaribili)
: Metodo che valuta la validità del nodo "INI"; verifica la
presenza di "begin", delle dichiarazione delle varibili, lancia
l'interpretazione delle attività e se sono corrette verifica la
presenza del parametro "return" e dell' "end" finale.
- Input : NodoAlbero radice, VarID schemaVaribili; i
parametri indicano rispettivamente la radice dell'albero di parsing e
il vettore delle variabili dichiarate da valorizzare e eventualmente
modificare.
- Output : boolean ; scorrendo l'albero di parsing vado a
verificare se sono presenti degli errori e se non vi sono e
l'interpretazione del codice va a buon fine restituiso il valore
"true", "false" altrimenti.
- private boolean intepretaAttivita(NodoAlbero nodoRadice) : Metodo per l'interpretazione dei nodi "attivita".
- Input : NodoAlbero nodoRadice; il parametro in input
indica il nodo di tipo "attivita" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se le operazioni svolte dai nodi figli del nodo "attivita" vengono eseguite senza errori.
- private boolean interpretaAssegnamento(NodoAlbero nodoIni) : Metodo per l'interpretazione dei nodi "assegnamento".
- Input : NodoAlbero nodoIni; il parametro in input indica
il nodo di tipo "assegnamento" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se le operazioni svolte
dai nodi figli del nodo "assegnamento" vengono eseguite senza errori.
- private double interpretaOb(NodoAlbero Ob)
: Metodo per l'intepretazione delle operazioni basi; Viene passato al
metodo il primo nodo di tipo "Ob" che trovo e tramite quello calcolo il
valore dell'operazione base scorrendo il sottoalbero con radice "Ob".
- Input : NodoAlbero Ob; il parametro in input indica il nodo di tipo "Ob" da cui deve partire l'interpretazione.
- Output : double ; valore numerico derivato dall'applicazione di una o più delle operazioni base da interpretare;
- private boolean interpretaIf(NodoAlbero nodoIni) : Metodo per l'interpretazione del nodo "IF";
- Input : NodoAlbero nodoIni; il parametro in input indica il nodo "IF" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se le operazioni svolte dai nodi figli del nodo "IF" vengono eseguite senza errori.
- private boolean condizioni(NodoAlbero nodoCondizioni) : Metodo per l'interpretazione delle condizioni di una delle operazioni condizionali "while" o "if".
- Input : NodoAlbero nodoCondizioni; il parametro in input indica il nodo "CONDS" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se le condizioni sono verificate, false altrimenti.
- private boolean trueFalseCombination(boolean v1, boolean v2, String OP_LOGIC)
: Metodo per la combinazione dei risultati di un di più
condizioni legate fra loro da un operatore logico come "AND" o "OR".
- Input : boolean v1, boolean v2, String OP_LOGIC; i
parametri che vengono passati in input rappresentano rispettivamente i
risultati di due condizioni e l'operatore logico.
- Output : boolean; restituisco "true" se è vera l'affermazione "v1 OP_LOGIC v2", e "false" se è falsa.
- private boolean interpretaIfElse(NodoAlbero nodoIni) : Metodo per l'interpretazione del nodo che indica un'operazione condizionale "IF-ELSE".
- Input : NodoAlbero nodoIni; il parametro in input indica il nodo "IF-ELSE" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se l'interpretazione dei nodi figli di "IF-ELSE" va a buon fine, "false" altrimenti.
- private boolean interpretaWhile(NodoAlbero nodoIni) : Metodo per l'interpretazione del nodo che indica un'operazione condizionale "WHILE".
- Input : NodoAlbero nodoIni; il parametro in input indica il nodo "WHILE" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se l'interpretazione dei nodi figli di "WHILE" va a buon fine, "false" altrimenti.
- private boolean interpretaParallelo(NodoAlbero nodoIni) : Metodo per l'interpretazione del nodo che indica un'operazione "parallelo".
- Input : NodoAlbero nodoIni; il parametro in input indica il nodo "parallelo" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se l'interpretazione dei nodi figli di "parallelo" va a buon fine, "false" altrimenti.
- private boolean interpretaReturn(NodoAlbero nodoRadice) : Metodo per l'interpretazione del nodo "return".
- Input : NodoAlbero nodoRadice; il parametro in input indica il nodo "return" da cui deve partire l'interpretazione.
- Output : boolean; restituisco "true" se l'interpretazione dei nodi figli del nodo "return" va a buon fine, "false" altrimenti.
ReportInterprete.java
La classe ReportInterprete.java viene
utilizzata per la memorizzazione del vettore delle variabili dichiarate
quando si è conclusa l'interpretazione dell'albero di parsing.
Attributi della classe:
- File f
- FileOutputStream fos
- PrintStream ps
La classe contiene i seguenti metodi
:
- public ReportInterprete()
: Costruttore; si occupa anche di inizializzare il file su cui poi
andrà a scrivere il vettore delle variabili dichiarate.
- public boolean writeReport(VarID variabili) : Metodo che scrive su file il vettore delle varabili passatogli come parametro.
- Input : VarID variabili; il parametro indica il vettore delle variabili che deve essere scritto su file.
- Output : boolean; "true" se la scrittura è avvenuta senza errore, "false" altrimenti.