Semantica di linguaggi di programmazione Ne esistono differenti stili a seconda di paradigma di programmazione uso (validazione, prototyping, verifica di complessità...) gusto personale dellinventore Ne vedremo due, già introdotti a LP Denotazionale Ogni termine del linguaggio denota un valore in un opportuno dominio Operazionale Ogni termine del linguaggio si semplifica fino ad un termine che rappresenta un valore Servono informazioni aggiuntive (contestuali) Astrazione di una macchina Le informazioni contestuali sono rappresentate nel dominio, usando valori funzionali Buon supporto per interpretazioni di logiche
È logicamente sbagliato, cioè non fa quello che dovrebbe. Questo sarà largomento della parte di specifiche (Reggio + Zucca) Dominio della semantica =? Programma = termine su una qualche grammatica CF (materiale propedeutico: dispense di LP, sezione 1 e appendici A, B,C) Vogliamo eliminare i termini sbagliati non appena possibile Per semplificare la definizione di semantica, fattorizzando i problemi che si possono incontrare (=le motivazioni per cui un programma è sbagliato) ed affrontandoli separatamente Per facilitare la comprensione da parte dellutente degli errori commessi, presentando le nozioni minime alla comprensione Un programma può essere sbagliato a 3 livelli Non corrisponde alla sintassi descritta dalla grammatica (formalmente: non appartiene a L(G)) Non soddisfa i vincoli contestuali (formalmente la semantica statica dà errore) La sua semantica non è definita (eg non terminazione) (formalmente la semantica dinamica non produce risultato accettabile) 4
Percorso Parser Stringhe Alberi (termini dellalgebra della sintassi astratta) Semantica statica Termini contestualmente corretti Semantica Valori I problemi di ambiguità vengono risolti a questo livello I problemi di nomi sconosciuti e tipaggio vengono risolti a questo livello
Semantica statica Controlla sostanzialmente due cose: uso di identificatori (costanti, funzioni, metodi..) e tipaggio Tipaggio Con un insiemi di tipi finito e in assenza di identificatori (o con identificatori lessicalmente divisi per tipi) può essere catturato dalla grammatica. Ma non sono casi interessanti. Identificatori Presenti in tutti i linguaggi di alto livello, perché permettono di astrarre, semplificando anche lesecuzione (su questo punto ci torneremo). Devono essere usati coerentemente dal punto di vista del tipo e della natura (con la loro dichiarazione/definizione, se cè) Impossibile in particolare catturarlo a livello della grammatica se il linguaggio permette la definizione di tipi da parte dellutente
Exp Num::=….. ExpNum::=Num | ExpNum + ExpNum | ExpNum ExpNum Tutte le stringhe sono contestualmente corrette Id::=…. Dec::=Id = ExpNum; Prog::=Dec* eval ExpNum | Id Vincolo contestuale: una costante deve essere dichiarata prima di poter essere usata Idea intuitiva: introduco la nozione di ambiente che mi memorizza le costanti dichiarate. Allinizio del programma lambiente è vuoto (non ho ancora esaminato nessuna dichiarazione) Ogni dichiarazione (corretta) amplia lambiente. Unespressione è corretta se usa solo costanti già nellambiente.
Formalizzazione Voglio definire una funzione a valori booleani (che interpreterò come corretto/scorretto) sul linguaggio di tipo Prog [_] s P :L Prog (Exp) B Per poterlo fare ho bisogno di funzioni ausiliarie che verificano la correttezza dei sottoprogrammi. Queste hanno bisogno di e/o costruiscono lambiente [_] s E :L ExpNum (Exp) Env s B [_] s D :L Dec (Exp) Env s Env s (Ci sarebbero anche funzioni per Num e Id ma sono sempre vere) La informazioni da memorizzare nellambiente in questo caso così facile sono solo i nomi delle costanti già introdotte Env s = Fin (L Id (Exp) )
Definizione delle funzioni Le funzioni di semantica statica si definiscono per (mutua) induzione. Le presentiamo in stile top-down (solo presentazione, perché stiamo dando un insieme di metaregole). Un programma è corretto se la sua parte dichiarazioni costruisce un ambiente in cui è corretta lespressione da valutare: [dl eval e] s P = [e] s E ( [dl] s D ) ( ) Per valutare le dichiarazioni ho bisogno di un ambiente, allinizio sarà quello vuoto Per esattezza qui ci andrebbe la funzione sulle sequenze di dichiarazioni Notazione equivalente: [dl] s D ( ) = [e] s E ( ) = b [dl eval e] s P = b
Dichiarazioni Una dichiarazione incrementa lambiente [id = e] s D ( ) = {id} Questo va bene solo se lespressione è corretta Se [e] s E ( ) = true, allora altrimenti Non abbiamo previsto la possibilità che la valutazione delle dichiarazioni generasse un errore. Per tenerne conto aggiungiamo un elemento speciale agli ambienti statici. Env s = Fin (L Id (Exp) ) {err} Lerrore andrà, naturalmente propagato [d] s D (err ) = err [e] s E (err) = false Esercizio proposto (facile) che cosa va modificato per impedire doppie dichiarazioni della stessa costante? err
Espressioni Per le espressioni abbiamo vari casi, seguiremo la struttura induttiva del linguaggio nel definire la semantica statica I numeri sono sempre corretti [n] s E ( ) = true, se err e n L Num (Exp) Somma e prodotto propagano correttezza ed errori [e + e] s E ( ) = [e] s E ( ) [e] s E ( ) [e e] s E ( ) = [e] s E ( ) [e] s E ( ) Una costante è corretta se e solo se è nota nellambiente [id] s E ( ) = id
Esercizio Verificare che il seguente programma è staticamente corretto X =7; Y=X+8; eval X+Y Calcolo lambiente statico creato dalle dichiarazioni [X =7; Y=X+8;] s D ( ) = Per le regole sulle sequenze ( che non abbiamo mai dato ) [Y=X+8;] s D ([X =7; ] s D ( ) ) = [Y=X+8;] s D ({X})= {X,Y} Perché ([7] s E ( )=true, essendo err e 7 L Num (Exp) Perché ([ X+8 ] s E ( {X} )= ([ X ] s E ( {X} ) ([ 8 ] s E ( {X} )= true true Essendo X {X} essendo {X} err e 8 L Num (Exp) {X} Valuto lespressione nellambiente statico creato dalle dichiarazioni [X + Y] s E ({X,Y} )= ([ X ] s E ( {X,Y} ) ([ Y ] s E ( {X,Y} ) = true true = true
Esercizi Proposti Verificare che il seguente programma è staticamente corretto X =7 2; Z=X+X; X = 2; eval Z+X Verificare che il seguente programma non è staticamente corretto X =2; Z=Y+X; eval Z
Espressioni tipate Che cosa dobbiamo cambiare per gestire espressioni di vari tipi? Idea intuitiva: dovremo tener conto del tipo delle espressioni per sapere quali operazioni sono lecite su di esse. Vogliamo quindi che Un ambiente statico corretto associ agli identificatori noti (che sono un insieme finito) il loro tipo. La semantica delle espressioni dia come risultato il tipo (o un errore) Formalmente Env s = [L Id (Exp) P Types] Fin {err} Funzioni parziali a dominio finito Num::=….. Bool ::=tt | ff Exp::= Id | Num | Bool | Exp + Exp | Exp Exp| Exp &Exp …. Non vale la pena di distinguere lapplicazione degli operatori a livello di grammatica, perché gli stessi problemi ci sono in ogni caso per via degli identificatori che possono avere il tipo che vogliono [_] s E :L Exp (Exp) Env s Types {err} Types = {int, bool}
Se [e] s E ( )=t Types, allora [id = e] s D ( ) = [t/id], altrimenti err Notazione per le funzioni parziali a dominio finito: [] funzione con dominio vuoto [a 1 /x 1,…, a n /x n ] con tutte le x i distinte funzione f con dominio {x 1,…,x n } definita da f(x i )=(a i ) [t/id] funzione con dominio dom( ) id} definita da [t/id](x)= (x) se x id e [t/id](id)=t I numeri sono sempre corretti [n] s E ( ) = int, se err e n L Num (Exp), altrimenti err Idem per le costanti booleane [b] s E ( ) = bool, se err e b L Bool (Exp), altrimenti err Somma e prodotto producono interi se applicati ad interi, errori altrimenti [e + e] s E ( ) = int, se [e] s E ( ) = int [e] s E ( ) = int, err altrimenti Analogamente per gli operatori boleani Una costante ha il tipo che risulta nellambiente oppure è ignota (err) [id] s E ( ) = (id) se id Dom( ), err altrimenti
Notazione equivalente e : t ________________________ t Types id = e [t/id] _ _ _ Env s L Dec (Exp) Env s {err} _ _:_ Env s L Exp (Exp) Types {err} e : err ________________________ id = e err ___________ err,n L Num (Exp) n:int e:int e:int __________________________ e + e:int _________________ id Dom( ), id : (id) ______________ err e: err e: err _________________ e + e: err e: err _________________ e + e: err Spesso le regole per trattare gli errori si omettono, e analogamente nellaltro stile si definiscono funzioni parziali senza err nel codominio _ _:_ Env s L Exp (Exp) Types {err} [_] s E : L Exp (Exp) Env s Types {err}
Ambienti locali Che cosa dobbiamo cambiare per gestire costrutti con ambienti locali? Exp::= ….let dec* in Exp Vogliamo che le costanti dichiarate localmente non siano più visibili dopo la fine del costrutto ds e: t ______________________________ let ds in e: t Questo si ottiene automaticamente, perché lambiente non compare nella conseguenza Complicazione ulteriore: vogliamo permettere di ridichiarare nellambiente locale le costanti globali, ma non doppie dichiarazioni allo stesso livello. Idea intuitiva: in tutte le regole avremo un ambiente locale ed uno globale, da usare per tenere traccia degli identificatori globali ma non modificare. Problema: e nei casi tipo let d in let d in let d in e ci basteranno due ambienti? Sì, perché analizzeremo i costrutti uno alla volta
Due livelli di ambiente Dove serve distinguere fra ambiente locale e globale? Solo al momento di decidere se una dichiarazione è lecita. Basta quindi cambiare [_] s D :L Dec (Exp) Env s Env s Env s Ambiente globale usato ma non modificato Se [e] s E ( g l )=t Types, e id Dom( l ), allora [id = e] s D ( g, l ) = l [t/id], altrimenti err Notazione ulteriore per le funzioni parziali a dominio finito: [ l ] funzione con dominio dom( ) dom( l ) definita da [ l ](x)= l (x) se x dom( l ), e [ l ](x)= (x) altrimenti [ds] s D ( ) = l [e] s E ( l )=t __________________________________________ [let ds in e ] s E ( ) = t La dichiarazione locale copre (temporaneamente) quella globale, perché lidentificatore viene cercato prima nellambiente locale e solo se non lo si trova in quello globale
Riassunto delle regole di dichiarazione [id = e;] s D ( l ) = loc [ds] s D ( loc ) = 0 ________________________________________________________________ [id = e;ds] s D ( l ) = 0 [e] s E ( l )=t _________________________________ id Dom( l ) t Types [id = e;] s D ( l ) = l [t/id] [e] s E ( l ) = err _________________________________ [id = e;] s D ( l ) = err ___________________________ id Dom( l ) [id = e;] s D ( l ) = err
Esercizio In quali ambienti statici è corretta la seguente espressione? let x=2; y=z+1; in let x=x+y; in x*y Partiamo con un generico ambiente 0 e vediamo quali restrizioni si deducono per avere la correttezza (intuitivamente: 0 (z) = int). [let x=2; y=z+1; in let x=x+y; in x*y ] s E ( 0 ) = t __________________________________________________________________________________________________________ [x=2; y=z+1; ] s D ( 0 ) = l [let x=x+y; in x*y ] s E ( 0 l ) = t _____________________________________________ ______________________________________________ T1T1 T2T2 0 err 0 (z) = int l = [int/x,int/y ]
[x=2; y=z+1; ] s D ( 0 ) = l ______________________________________________________________________________________________ [x=2;] s D ( 0 ) = [int/x] [y=z+1; ] s D ( 0,[int/x]) = [int/x,t/y ]= l __________________________________ x = Dom([]) ___________________________________ y {x} = Dom([int/x]) [2] s E ( 0 ) = int [z+1] s E ( 0 int/x ) = t _______________________________ 0 err ______________________________________________________ t=int [z] s E ( 0 int/x ) = int [1] s E ( 0 int/x ) = int ____________________________ ____________________________ 0 int/x]](z) = int 0 (z) = int ____________________________ Perché 0 int/x]](z) = 0 (z), essendo z x Regole di correttezza dei numeri
[let x=x+y; in x*y ] s E ( 0 [int/x,int/y] ) = t _____________________________________________________________________________________________ [x=x+y;] s D ( 0 [int/x,int/y] )= t 1 /x][x*y ] s E ( 0 [int/x,int/y] t 1 /x )=t ________________________________________________ x = Dom([]) ____________________________________________ [x+y] s E ( 0 int/x,int/y ) = t 1 stesso albero di sinistra ___________________________________________ t 1 =int nello stesso ambiente, perché [x] s E ( 0 int/x,int/y ) = int [y] s E ( 0 int/x,int/y ) = int 0 [int/x,int/y] t 1 /x )= __________________________________ ____________________________ 0 [t 1 /x,int/y] 0 int/x,int/y (x)=int 0 int/x,int/y (y) = int 0 [int/x,int/y] __________________________________ ______________________________