• Non ci sono risultati.

5.3 VarTypes: aspetti realizzativi

N/A
N/A
Protected

Academic year: 2021

Condividi "5.3 VarTypes: aspetti realizzativi"

Copied!
16
0
0

Testo completo

(1)

Capitolo 5

Implementazione

5.1 Interfaccia grafica: scelte implementative

L’interfaccia grafica nella sua versione finale è realizzata sfruttando i componenti grafici di Swing e la gestione degli eventi di AWT. In realtà la sua prima versione era stata realizzata sfruttando interamente questo ultimo pacchetto. Poi una serie di considerazioni hanno portato ad un cambiamento di rotta. Come prima cosa vediamo quali aspetti differenziano i due sistemi.

L’ Abstract Window Toolkit (AWT) fa parte del JDK dalla prima release di Java, ma sin dall’inizio si dimostrò poco sofisticato per supportare complesse interfacce utente.

Il suo vero tallone d’Achille è però sempre stata la portabilità, problema mai veramente risolto in nessuna release del linguaggio. AWT, poggia infatti fortemente sul runtime della piattaforma su cui l’interfaccia viene eseguita, rendendo così impossibile un’uniformità di esecuzione.

Nell’aprile del 1997 JavaSoft annunciò le Java Foundation Classes (JFC), che includevano (e in alcuni casi rimpiazzavano) AWT. La maggior parte di JFC è costituita da componenti grafiche più complesse, flessibili, ma soprattutto portabili.

Queste nuove componenti furono (e sono ancora) chiamate Swing. In aggiunta alle nuove componenti, Swing apporta tre fondamentali miglioramenti ad AWT:

Ÿ Non si appoggia sul runtime della piattaforma (sistema operativo). E’ scritto interamente in Java e crea i propri componenti. Questo nuovo approccio risolve il problema della portabilità perché evita ai componenti di ereditare i comportamenti dell’ambiente ospitante.

(2)

47

Ÿ Fa una vera distinzione fra i dati che i componenti mostrano (modello) e l’aspetto vero e proprio (vista). Tale distinzione può sembrare troppo forzata, ma in realtà ha importanti implicazioni per gli sviluppatori. Ciò significa infatti che i componenti sono estremamente flessibili e facilmente adattabili a mostrare nuovi tipi di dati non originariamente definiti, o a cambiare il loro aspetto senza coinvolgere la struttura dell’informazione rappresentata.

Illustrato questo aspetto ritorniamo alla nostra implementazione. L’ Applet grafica si compone essenzialmente in due parti (classi): AppletBase e BRead (vedere il codice).

Le componenti grafiche sono in realtà interamente composte nella prima di queste due classi, la seconda costituisce una sorta di appoggio per la gestione degli eventi.

E’ inutile e dispersivo in queste sede discutere punto per punto il codice sorgente:

soffermiamoci sugli aspetti salienti.

La prima cosa da notare è che AppletBase extends JFrame e non come in genere accade quando si parla di Applet, JApplet. La principale differenza sta nel fatto che un’Applet realizzata come nel primo caso (JFrame) è totalmente indipendente, proprio dal punto di vista componentistico, dalla pagina HTML in cui è inserita:

l’Applet lanciata “risiede” in una nuova finestra (Frame). Nel secondo caso invece l’Applet risiede nella pagina HTML “chiamante” ed è spesso “vittima” di comportamenti poco “educati” da parte del browser (dimensionamento errato, funzionalità bloccate, ecc..). Attenzione: non è mia intenzione mettere in discussione lo stato dell’arte, parlo solo ed esclusivamente della mia esperienza.

Il secondo aspetto da notare è l’inserimento delle due JTextArea (una per inserire il codice da valutare l’altra per fornire un output) in due Vertical Box: questa scelta ha permesso la creazione degli ScrollPane ovvero le barre verticali di scorrimento che vengono aggiunte (tramite un metodo add) ai sopra-citati Box.

Terzo aspetto: bottoni ed eventi associati. La gestione degli eventi è stata delegata ad AWT ed è realizzata sfruttando i metodi addActionListener , actionPerformed ed il costruttore ActionListener(). La sequenza di costruzione è la seguente: sul bottone viene chiamato addActionListener che come argomento ha un new ActionListener()

(3)

che contiene il metodo d’invocazione actionPerformed che a sua volta scatena l’evento invocato alla pressione del bottone stesso.

Quarto ed ultimo aspetto: JEditorPane. I bottoni Inputs ed HowTo svolgono il lavoro di due mini browser sfruttando le potenzialità di JEditorPane. Questa ultima scelta è stata indotta dal fatto di aver realizzato l’Applet come un Frame indipendente e dalla necessità di mostrare in due finestre distinte una sorta di guida-esempio all’utilizzo.

La prima scelta implementativa mi vieta di usare i “tradizionali” metodi per

“navigare” nel contesto delle Appet (getAppletContext , getApplets e showDocument), proprio perché tale contesto non esiste. Allora l’unica alternativa resta quella usata. L’unica pecca di questa realizzazione è che non è possibile visualizzare file che risiedono localmente sulla macchina, ma solo riferire quelli che si trovano ad un determinato indirizzo della rete.

(4)

49

5.2 Tabelle: funzionamento e gestione

Le tabelle hanno un ruolo fondamentale nella rappresentazione di linguaggi a blocchi, siano essi blocchi, let, o, come nel nostro caso, oggetti.

I tecnicismi e gli aspetti realizzativi vengono rimandati ad un’analisi del codice:

scopo di questo paragrafo vuol essere l’illustrare come avviene la gestione dello scope (visibilità) delle variabili. Per un’analisi corretta, viene utilizzato un pezzo di codice Tiger[3] che per ragioni di chiarezza espositiva viene preferito a Sigma_X.

Abbiamo dunque:

1: function f (a:int, b:int, c:int) = 2: print_int(a + c);

3: let var j := a + b 4: var a := “hello”

5: in print(a); print(j);

6: let var a := b + 1 7: in print_int(a) 8: end

9: end;

10: print_int(b)

(5)

Alla linea 1 non ci sono identificatori in scope. Graficamente abbiamo:

Alla linea 2: print_int(a + c) i tre parametri a, b e c vengono aggiunti allo scope:

In top viene tenuto l’ultimo simbolo inserito in dict. Ogni simbolo in tabella sa qual è il simbolo che era stato inserito subito prima di lui nello scope corrente. Ciò significa che partendo da top e tornando indietro sul simbolo precedente, si possono scorrere tutti i simboli inseriti nello scope corrente e solo quelli.

(6)

51

Alla linea 3: let var j := a + b viene creato uno scope

locale:

top viene impostato a null e c viene inserito in testa alla lista marks. Questo valore dovrà essere riabilitato non appena si esce dallo scope locale.

Le linee 3: let var j := a + b e 4: var a := “hello” aggiungono due identificatori allo scope locale:

(7)

L’aggiunta di a deve invece sovrascrivere il vecchio valore di a. Al fine di poter riabilitare tale valore in futuro, lo rendiamo raggiungibile legandolo in lista al vecchio valore di a:

La linea 6: let var a := b + 1 introduce un altro scope locale. Poniamo top a null e salviamo il vecchio valore di top in cima alla lista marks:

(8)

53

L’aggiunta dell’identificatore a, sovrascrive il vecchio valore che viene comunque legato in lista:

Cosa accade alla linea 8: end quando si abbandona l’ambiente più interno?

Vorremmo tornare a:

Prima di tutto eliminiamo dalla tabella il simbolo legato a top. Se tale simbolo ha un simbolo precedente non null proseguiamo l’eliminazione con quest’ultimo e così via.

(9)

Ogni volta che eliminiamo un simbolo dalla tabella, controlliamo se ha un vecchio valore legato a lista e lo inseriamo a posto suo. Nel nostro caso sostituiamo il valore di a col suo vecchio valore. Alla fine eliminiamo la testa dalla lista marks e la copiamo in top. Il ragionamento è simile per la chiusura dello scope alla linea 9:

questa volta partiamo dal valor a in top ed eliminiamo a (appunto) e j, poiché a sa di essere stato inserito dopo j nello scope. Il vecchio valore di a viene riabilitato.

Eliminiamo a questo punto la testa di marks e copiamola in top.

5.3 VarTypes: aspetti realizzativi

Le variabili di tipo svolgono un ruolo essenziale nel funzionamento dell’algoritmo di unificazione e di inferenza. Vediamone i principali aspetti realizzativi.

Le variabili di tipo sono rappresentate da oggetti della classe VarTypes che estende la classe types ed implementa l’interfaccia Comparable. La prima scelta deriva dal fatto che una variabile di tipo deve poter essere utilizzata al posto di qualsiasi altro tipo del sistema. La seconda permette alla classe di avere un metodo compareTo necessario per ordinare lessicograficamente due variabili nella stessa sostituzione.

Tale ordinamento è richiesto per motivi di efficienza (complessità) dell’algoritmo di unificazione. La classe VarTypes possiede un contatore statico count ed un intero che identifica lo stato della variabile corrente: è importantissimo ai fini del

funzionamento del sistema che ogni variabili nuova (fresh) sia distinta dalla precedente; due costruttori uno pubblico ed uno privato: il primo costruisce una nuova variabile associando al suo stato un nuovo valore del contatore, il secondo viene chiamato all’interno del metodo instance per sostituire alla variabile il tipo calcolato dall’unificazione.

Caratterizza particolarmente la classe il metodo occurCheck (occurCheckL è solo un metodo ausiliario per scorrere un dominio composto). Tale metodo ha il compito di controllare che la variabile non occorra all’interno del secondo termine

(10)

55

volta in volta viene applicata l’operazione appropriata. Particolarmente interessante è il caso di MTypes (tipi associati ai metodi) in cui l’occurCheck viene chiamato ricorsivamente sul dominio e sul rango.

5.4 Unificazione ed inferenza: unify ed inferW

L’unificazione lavora sui tipi, l’inferenza sui termini. Per essere coerenti con questa definizione il metodo unify è stato inserito nelle classi che rappresentano i tipi trattati dal sistema, mentre il metodo inferW è stato inserito nelle classi che rappresentano i termini del linguaggio.

La parte più interessante che riguarda il codice dell’unify è quella che si occupa dell’unificazione dei tipi oggetto (OType) e dei tipi metodo (MTypes). Il metodo unify di queste due classi crea due vettori che rappresentano i due tipi coinvolti (con modalità diverse a seconda che si tratti di tipi metodo o tipo oggetto), che vengono poi passati al metodo statico unifyList contenuto in UnifyUtil. Se l’unificazione chiamata è quella che coinvolge gli oggetti la prima operazione effettuata è una sincronizzazione (metodo sync) dei due vettori passati. Tale operazione considera i metodi identificati (naturalmente nei due vettori) dagli stessi selettori e crea con essi un vettore di AuxTypes (record di due tipi generici).

Per quanto riguarda invece l’inferW il codice più articolato è sicuramente quello che riguarda la regola per l’Object e per la Select, con qualche annotazione da fare per la lessThan (relazione di sottotipo). Procediamo con ordine.

Un oggetto in Sigma_X è dato dal termine rappresentato dalla classe TreeObj. Il costruttore (pubblico) di tale classe, come prima cosa genera un nome unico per l’oggetto invocando il metodo genName() su un oggetto istanza della classe Name (tipo per i nomi di classe/oggetto). La chiamata di inferW si occupa di propagare (legare) il nome appena generato al self di ogni metodo contenuto nell’oggetto. L’

inferenza vera e propria è stata realizzata nella classe ObjBody che ne modella il corpo.

(11)

Nel nostro calcolo l’invocazione di un metodo è rappresentata dalla classe TreeInv, che modella anche l’ applicazione (Abstract[AppC]). Una Select si distingue da una Abstract (a livello di codice) per il valore del campo meth: se questo ultimo è istanza di TreeSel (è un selettore che identifica un metodo) allora ci troviamo di fronte ad una Select altrimenti se è istanza di TreeAbst (è un metodo) abbiamo un’Abstract. Da notare in particolare il metodo keys() che applicato ad una tabella (contesto) restituisce tutti gli elementi sotto forma di enumeratore: particolarmente utile quando c’è la necessità di mappare una funzione su tutti gli elementi (nel nostro caso tutti gli elementi del contesto (tipi) vengono raffinati con la sostituzione calcolata).

Parliamo brevemente adesso della relazione di sottotipo lessThan (vedere paragrafo successivo). Definiamo per cominciare, cosa intendiamo noi per sottotipo: diciamo che tb è sottotipo di ta se e solo se ta contiene tutti i selettori di tb. E’ implementata, con lo stesso nome, come metodo statico della classe Termine. In inferW tale relazione è utilizzata sia in Abstract che in Extract: per questo motivo il metodo è realizzato in switch-mode sfruttando la solita instanceof . Infatti l’Abstract si chiede soltanto se i due oggetti appartengono alla stessa classe (hanno lo stesso nome), mentre l’Extract fa il controllo completo (selettori e metodi). Notiamo che parte del codice di lessThan è molto simile a quello di sync (ricerca nei vettori, vedere paragrafo 5.6).

5.5 LessThan : implementazione della relazione di sottotipo

La relazione di sottotipo implementata in questa versione di Sigma_X è prettamente un’inclusione insiemistica la cui definizione informale ed intuitiva è la seguente:

tb è sottotipo di ta se e solo se ta contiene tutti i selettori di tb

(12)

57 Se per esempio avessimo:

• ta = [uno:Obj_1->INT , due:Obj_1->STRING]

• tb = [uno:Obj_1->var0]

potremmo concludere che tb è sottotipo di ta e che unificano sotto la sostituzione (var0 , INT).

Il metodo omonimo viene inserito come metodo statico nella classe Termine e la sua chiamata avviene all’incirca nel seguente modo:

LessT result = Termine. lessThan(t1,t2);

dove LessT è una classe appoggio (contenitore) che presenta due field: s e guard. Il primo di tipo Substitution, andrà a contenere l’eventuale sostituzione calcolata, il secondo di tipo boolean mi dirà se la relazione di sottotipo vale.

Adesso in dettaglio, andiamo a vedere come opera la lessThan(t1,t2).

I casi che va ad analizzare sono due:

1. t2 instanceof Name 2. t2 instanceof OType

Nel primo caso la cosa che fa è molto semplice: va a reperire il self di t1 e controlla se è lo stesso di quello di t2. Nel caso in cui tale self sia uguale il metodo termina restituendo un contenitore LessT con campo guard settato a true e sostituzione identica, altrimenti restituisce un LessT con campo guard settato a false e sostituzione TopSub.

(13)

Il caso più interessante è comunque il secondo. Come prima cosa i due tipi oggetto (ormai appurati) vengono trasformati in due vettori di TyObjectRecord che non sono altro che coppie ( selettore , tipo metodo ). Tornando al nostro esempio otterremmo:

per ta le coppie ( uno , Obj_1->INT ) e ( due , Obj_1->STRING ); mentre per tb l’unica coppia ( uno , Obj_1->var0 ).

Fatto questo vengono creati quattro vettori d’appoggio: due destinati a contenere i selettori rispettivamente di t1 e t2 e due a contenere le parti destre delle coppie cioè i tipi dei metodi. A questo punto comincia a scorrere i due vettori di TyObjectRecord e di riflesso a riempire i vettori di “selettori e metodi”. Nel nostro esempio v1Sel conterrebbe [uno , due] e v1Type [Obj_1->INT , Obj_1->STRING] ; mentre v2Sel [uno] e v2Type [Obj_1->var0]. Abbiamo già detto all’inizio che ta deve contenere tutte le coppie di tb: lessThan va adesso a controllarlo. Si comincia a scorrere v2Sel:

nell’esempio abbiamo un solo elemento in esso, uno. Di esso si prende ogni singolo elemento e sfruttando il metodo contains dei vettori si va a vedere se esso è presente anche in v1Sel. Se si, chiamando indexof si va a vedere dove tale selettore è situato ed una volta avuto l’indice si accede a v1Type e si prende il metodo corrispondente.

Adesso si estrae anche da v2Type il metodo corrispondente al selettore e dopo di ciò si vanno ad inserire i due metodi in un vettore di AuxTypes, ognuno dei quali altro non è se non una coppia di metodi.

Continuando sempre col solito esempio, estraiamo da v2Sel uno, controlliamo se è presente in v1Sel, e dopo di che prendiamo il metodo corrispondente e costruiamo la nostra coppia di AuxTypes: ( Obj_1->var0 , Obj_1->INT ).

Bene, come ultima cosa andiamo a scorrere questo vettore di AuxTypes e operiamo le unificazioni richieste.

Nota: se scorrendo i vettori avessimo ottenuto intersezione vuota, il metodo sarebbe subito terminato restituendo un LessT = new LessT( new TopSub() , false ).

5.6 Codice che realizza la lessThan

(14)

59 public static LessT lessThan(types o1, types o2) {

if (o2 instanceof Name) {

Name name_o1 = (Name)((OType)o1).getNext().mT.self();

if (o2.equals(name_o1))

return new LessT( new identity() , true );

else

return new LessT( new TopSub() , false );

}

if (o2 instanceof OType) {

Vector v1 = ((OType)o1).TyObjectList();

Vector v2 = ((OType)o2).TyObjectList();

int i = 0;

int j = 0;

int v1Size = v1.size();

int v2Size = v2.size();

Vector v1Sel = new Vector();

Vector v2Sel = new Vector();

Vector v1Type = new Vector();

Vector v2Type = new Vector();

while (i != v1Size) {

TyObjectRecord temp = (TyObjectRecord)v1.get(i);

String tempSel = temp.getSelector();

MTypes tempTyp = temp.geType();

v1Sel.add(tempSel);

v1Type.add(tempTyp);

i++;

}

(15)

while (j != v2Size) {

TyObjectRecord temp = (TyObjectRecord)v2.get(j);

String tempSel = temp.getSelector();

MTypes tempTyp = temp.geType();

v2Sel.add(tempSel);

v2Type.add(tempTyp);

j++;

} i = 0;

String test;

int index = 0;

boolean guard;

MTypes t1;

MTypes t2;

Vector temp = new Vector();

//o1 deve contenere tutte le coppie di o2 while(i != v2Size)

{

test = (String)v2Sel.get(i);

guard = v1Sel.contains((String)test);

if (guard) {

index = v1Sel.indexOf(test);

t2 = (MTypes)v2Type.get(i);

t1 = (MTypes)v1Type.get(index);

AuxTypes ct = new AuxTypes(t1,t2);

temp.add(ct);

} else

(16)

61 i++;

}

Substitution fin = null;

j = 0;

types e1;

types e2;

AuxTypes aux;

while (j != temp.size()) {

aux = (AuxTypes)temp.get(j);

e1 = aux.first();

e2 = aux.second();

System.out.println("e1 vale "+e1.toString());

System.out.println("e2 vale "+e2.toString());

if (fin == null) fin = e1.unify(e2);

else

fin = fin.per((e1.unify(e2)));

j++;

}

return new LessT(fin,true);

}

return new LessT(new TopSub(),false);

}

Riferimenti

Documenti correlati

Pertanto, in assenza di un vero e proprio picco visibile del fondo, si è effettuato il calcolo della MAR, considerando il numero netto di conteggi del rivelatore che possa

La politica, sia estera che interna, dei Paesi partecipanti alla Politica di Vicinato non segue invece questa tendenza visto che gli obblighi contratti da

“Prevalentemente per Attività”. In tale situazione urbanistica l’area in esame è stata oggetto di rilascio di Concessione Edilizia n. 72011 del 14.03.2003 per la realizzazione di

La designazione avverrà con apposita deliberazione del Consiglio metropolitano. In merito alla durata dell’incarico, si precisa che, a norma di legge, le Commissioni, una

Sedile sportivo di serie Cooper S PELLE MINI YOURS LOUNGE CARBON BLACK. Il

TYE1 Sedili sportivi pelle Lounge MINI Yours Carbon Black 2XD Volante sportivo in pelle Nappa con badge MINI Yours 423 Tappetini in velluto con badge MINI Yours. 4AA Padiglione

Cette question juridique sera traitée du point de vue du « retour » dans la partie suivante.. Le retour des mineurs marocains semble être pour les différentes parties à ce

Livestock production fell in B_Africa and B_NAmer in all scenarios, achieving more pronounced reduction under global scenario (-25.21% and -24.52%, respectively). B_LatinAmer