Linguaggi e Ambienti di Programmazione Principi e tecniche diffuse che si incontrano spesso nelle applicazioni dell’informatica. Compilatori Editor di struttura: struttura riceve in input una sequenza di comandi per costruire un programma sorgente. Non solo esegue i compiti di un text editor normale, ma analizza il testo imponendo una struttura gerarchica, aggiunge automaticamente parole chiave,... Pretty printer: printer analizza un programma e lo stampa in modo che la sua struttura diventi visibile usando font speciali e indentazioni. Verificatori statici: statici cercano di scoprire errori nei programmi senza mandarli in esecuzione. Puo’ scoprire che parti del codice non vengono mai eseguite o che alcune variabili non sono definite o che una variabile di tipo reale e’ usata come puntatore. Interpreti di interrogazioni: interrogazioni traducono un predicato che contiene operatori booleani e relazionali in comandi per cercare in un database i record che soddisfano il predicato Formattatori di testo, testo ... Programma scritto in linguaggio sorgente compilatore messaggi d’errore Programma scritto in linguaggio oggetto (target) Modello di compilazione analisi - sintesi analisi: spezza il programma in parti costituenti e crea una rappresentazione intermedia sintesi: partendo dalla rappresentazione intermedia costruisce il programma nel linguaggio target La fase di analisi può essere suddivisa a sua volta in tre fasi: • analisi lineare: lineare raggruppa i caratteri della stringa di input in token, ossia identifica sequenze che hanno un significato. • analisi gerarchica: gerarchica raggruppa i token in una struttura gerarchica. • analisi semantica: semantica verifica che le componenti di un programma stiano insieme in modo significativo. Esempio: 1 analisi lineare usata anche nei linguaggi di interrogazione e nei sistemi di information retrieval, in un compilatore viene chiamata analisi lessicale o scanning. position := initial + rate * 60 identificatore numero simbolo segno di d’assegnazione moltiplicazione 2 analisi gerarchica chiamata analisi sintattica o parsing: raggruppa i token in frasi di una grammatica. Di solito le frasi vengono rappresentate da un albero di parsificazione. := position + initial * rate 60 La struttura gerarchica è di solito espressa da regole ricorsive Un identificatore è un’espressione Un numero è un’espressione Se espressione1 e espressione2 sono espressioni, sono espressioni anche espressione1 + espressione2 espressione1 * espressione2 (espressione1) se id è un identificatore e expr è un’espressione, allora id := expr è un’istruzione se expr è un’espressione e st è un’istruzione, allora while (expr) do st if (expr) then st sono istruzioni Le grammatiche sono una formalizzazione delle regole ricorsive che possono essere usate per guidare l’analisi sintattica Un identificatore è un’espressione Un numero è un’espressione Se espressione1 e espressione2 sono espressioni, sono espressioni anche espressione1 + espressione2 espressione1 * espressione2 se id è un identificatore e expr è un’espressione, allora id := expr è un’istruzione istruzione identificatore position espressione := espressione + espressione identificatore espressione initial identificatore numero rate 60 * espressione 3 analisi semantica Verifica l’esistenza di errori semantici e raccoglie informazioni per la fase di generazione del codice. Usa la struttura gerarchica della fase 2 (analisi sintattica) per identificare operatori e operandi di espressione e istruzioni. Una parte importante è la verifica dei tipi. Type checking: ogni operatore deve avere operandi del tipo corretto. La rappresentazione interna di un numero intero è diversa da quella di un numero reale. Se position, rate e initial sono stati dichiarati reali e 60 è assunto intero, il type checker rileva che il prodotto è tra un intero e un reale. Di solito l’intero viene convertito in un reale. programma sorgente analisi lessicale front end analisi sintattica analisi semantica symbol table manager generazione codice intermedio trattamento degli errori ottimizzazione generazione codice programma finale back end Symbol table management Funzione essenziale di un compilatore è quella di ricordare gli identificatori e raccogliere informazioni sui loro vari attributi: • memoria allocata • tipo • scope • nome e tipo degli argomenti per le procedure • ... È una struttura dati con un record per ogni identificatore. Quando l’analisi lessicale riconosce nel programma sorgente un identificatore, esso viene inserito nella symbol table. Gli attributi vengono di solito determinati nelle fasi successive. var position, initial, rate : real quando l’analizzatore incontra position il tipo real non è ancora noto Error Detection and Reporting Quando si incontra un errore, bisogna trattarlo in modo che la compilazione possa procedere. Un compilatore che si ferma quando trova il primo errore non è utile. Errori si possono trovare in tutte le fasi, ad esempio: nell’analisi lessicale: caratteri che non formano token del linguaggio nell’analisi sintattica: una sequenza di token che viola le regole strutturali nell’analisi semantica: la somma tra due identificatori di cui uno è il nome di un array oppure il nome di una procedura position := initial + rate * 60 analizzatore lessicale id1 := id2 + id3 * 60 analizzatore sintattico := id1 + id2 * id3 60 analizzatore semantico analizzatore semantico := id1 + id2 * id3 intoreal 60 generatore codice intermedio temp1 := intoreal (60) temp2 := id3 * temp1 temp3 := id2 + temp2 id1 := temp3 temp1 := intoreal (60) temp2 := id3 * temp1 temp3 := id2 + temp2 id1 := temp3 Symbol Table 1 position 2 initial 3 rate 4 ……. ……. ……. ottimizzatore temp1 := id3 * 60.0 id1 := id2 + temp1 generatore codice MOVF MULF MOVF ADDF MOVF id3, R2 #60.0, R2 id2, R1 R2, R1 R1, id1 Linguaggi artificiali e formali Nei linguaggi formali la forma delle frasi, cioè la sintassi, e il loro significato, la semantica, sono definiti in modo algoritmico, cioè è possibile scrivere una procedura per verificare la correttezza grammaticale delle frasi e per calcolarne il significato. Struttura matematica costruita a partire da un alfabeto per mezzo di regole assiomatiche o definita per mezzo di macchine chiamate automi. Teoria dei linguaggi formali: formali sintassi delle frasi. Impalcatura su cui si può costruire la semantica. Come specificare un numero infinito di frasi mediante una descrizione finita? Algoritmi di enumerazione: insieme finito di regole grammatica generativa Per progettare un compilatore, cioè un algoritmo che verifica la correttezza delle frasi e ne calcola il significato, è necessario un approccio più analitico, riconoscitivo. I due approcci, generativo e riconoscitivo sono duali ed equivalenti: si può passare da un algoritmo di enumerazione ad uno di riconoscimento in modo meccanico.