Traduzione guidata dalla sintassi

Documenti analoghi
Fasi di un Compilatore

Linguaggi e Ambienti di Programmazione

Semantica e traduzione guidata dalla sintassi

Grammatiche. Grammatiche libere da contesto Grammatiche regolari Potenza delle grammatiche libere e regolari Struttura di frase: Alberi di derivazione

Analizzatore lessicale o scanner. Lo scanner rappresenta un'interfaccia fra il programma sorgente e l'analizzatore sintattico o parser.

Alberi ed Alberi Binari

Algoritmi e Strutture Dati

Espressioni aritmetiche

Grammatiche Parse trees Lezione del 17/10/2012

Dispensa 2. Data una grammatica context free esistono tre metodi diversi per costruirne la parsing table per un parser LR:

Automi e Linguaggi Formali

Alberi e alberi binari I Un albero è un caso particolare di grafo

Pumping lemma per i linguaggi Context-free

Yet Another Compiler-Compiler. Generazione automatica di analizzatori sintattici

Problemi, istanze, soluzioni

Espressività e limitazioni delle grammatiche regolari

Variabili. Unità 2. Domenico Daniele Bloisi. Corso di Programmazione e Metodi Numerici Ingegneria Aerospaziale BAER

Alberi binari e alberi binari di ricerca

Linguaggi di programmazione - Principi e paradigmi 2/ed Maurizio Gabbrielli, Simone Martini Copyright The McGraw-Hill Companies srl

Fondamenti d Informatica: linguaggi formali. Barbara Re, Phd

Elementi lessicali. Lezione 4. La parole chiave. Elementi lessicali. Elementi lessicali e espressioni logiche. Linguaggi di Programmazione I

AUTOMA A STATI FINITI

Costanti e Variabili

Laboratorio di Algoritmi e Strutture Dati. Code con Priorità

Note per la Lezione 4 Ugo Vaccaro

Il Modello di un Compilatore. La costruzione di un compilatore per un particolare linguaggio di programmazione e' abbastanza complessa.

GRAFI. Cosa sono Grafi non orientati Grafi orientati Grafi pesati Alberi Automi!

Heap e code di priorità

Cos è un algoritmo. Si dice algoritmo la descrizione di un metodo di soluzione di un problema che sia

Appunti del corso di Informatica 1 (IN110 Fondamenti) 7 Grafi e alberi: introduzione

Introduzione alla programmazione

Funzioni, Stack e Visibilità delle Variabili in C

Cosa si intende con stato

INDICI PER FILE. Accesso secondario. Strutture ausiliarie di accesso

Linguaggi e Grammatiche Liberi da Contesto

Espressioni regolari

Algoritmi e Strutture Dati. HeapSort

Grammatiche context-free

Definire tramite una grammatica ad attributi il

Il tipo astratto coda con priorità: specifiche sintattiche e semantiche. Realizzazioni.

I Linguaggi di Programmazione

PROLOG E ANALISI SINTATTICA DEI LINGUAGGI Quando si vuole definire in modo preciso la sintassi di un linguaggio si ricorre a una grammatica G=(V n,v t

«Sciente e Tecnologie dei Beni Culturali»

Linguaggi di Programmazione Corso C. Parte n.3 Linguaggi Liberi da Contesto e Linguaggi Contestuali. Nicola Fanizzi

INTRODUZIONE ALLA PROGRAMMAZIONE AD ALTO LIVELLO IL LINGUAGGIO JAVA. Fondamenti di Informatica - D. Talia - UNICAL 1. Fondamenti di Informatica

Sviluppo di programmi

Linguaggi di Programmazione

Linguaggi, Traduttori e le Basi della Programmazione

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

Il linguaggio di programmazione Python

Programmazione in Java (I modulo)

Algoritmi e Strutture Dati

Verificare se una grammatica e LL(1) e costruirne la tabella di parsing. Verificare se una grammatica e LR(0) e costruirne la tabele ACTION e GOTO

Programmazione Orientata agli Oggetti. Emilio Di Giacomo e Walter Didimo

Cammini minimi in grafi:

Esercizi su alberi binari

Introduction. The Structure of a Compiler

Dispensa YACC. 1.1 YACC: generalità

Dispensa YACC: generalità

Dispensa 1. Da un punto di vista logico l architettura di un compilatore si può suddividere in due parti: Analisi e Sintesi.

Esercizi proposti 10

Linguaggi Regolari e Linguaggi Liberi

Logica proposizionale

Rappresentazione dell informazione

6 - Blocchi e cicli. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Definizione di metodi in Java

Esercitazione 6. Alberi binari di ricerca

Programmazione ad oggetti

Macchine sequenziali. Automa a Stati Finiti (ASF)

La codifica di sorgente

Definizione di classi. Walter Didimo

Rappresentazioni numeriche

UD 3.2b: Programmazione in Pascal (1)

Analizzatore Lessicale Parte I Scanner

Il linguaggio C. Puntatori e Array

Transcript:

Traduzione guidata dalla sintassi Attributi e definizioni guidate dalla sintassi Dipartimento di Matematica e Informatica mariarita.diberardini@unicam.it

Analisi Semantica Analisi sintattica - output: il flusso di token (fase di analisi lessicale) viene raggruppato in frasi grammaticali rappresentate tramite alberi di derivazione di una CFG Questo tipo di rappresentazione intermedia consente di identificare operatori ed operandi delle espressioni e statements Costituisce l input della fase di analisi semantica: verifica dell esistenza di eventuali errori semantici acquisizione di informazioni sui tipi utilizzate nelle fasi successive del processo di compilazione type checking Problema: come possiamo definire la semantica ai costrutti dei linguaggi di programmazione?

Un semplice esempio Consideriamo la grammatica per le espressioni sui naturali: E E + T E T T T T F T /F F F (E) number Definire la semantica di questo linguaggio significa definire il valore di ogni espressione costruita applicando le produzioni della grammatica

Un semplice esempio Consideriamo la grammatica per le espressioni sui naturali: E E + T E T T T T F T /F F F (E) number Definire la semantica di questo linguaggio significa definire il valore di ogni espressione costruita applicando le produzioni della grammatica Associamo ad ogni non terminale della grammatica (e quindi ad ogni nodo del albero di derivazione per una data stringa in input) un attributo val che rappresenta appunto il suo valore: E.val, T.val, F.val A questo punto non ci resta che associare a ciascuna produzione una regola, detta regola semantica, che dice come calcolare il valore di una espressione a partire da quello delle sue sottoespressioni

Un semplice esempio PRODUZIONI E E 1 + T E E 1 T E T T T 1 F T T 1 /F T F F (E) F num REGOLE SEMANTICHE E.val = E 1.val + T.val E.val = E 1.val T.val E.val = T.val T.val = T 1.val F.val T.val = T 1.val/F.val T.val = F.val F.val = E.val F.val = num.lexval

Albero di derivazione della stringa 3 5 + 1 E E + T E * T F T F num F num num

Albero di derivazione (annotato) della stringa 3 5 + 1 E.val =16 E.val =15 + T.val =1 E.val =3 * T.val =5 F.val =1 T.val =3 F.val =3 num F.val =5 num num

Valutazione delle regole Fornire le regole semantiche non è però sufficiente Bisogna anche fornire un ordine di valutazione: dobbiamo specificare quali regole applicare (e, soprattutto, in quale ordine) per calcolare il valore desiderato L ordine di valutazione può essere ricostruito a partire dall albero di derivazione per la stringa input, non è altro che un qualche schema di visita dell albero di derivazione

Obiettivi Vediamo, in breve, una tecnica che permette di effettuare analisi semantica e traduzione usando la struttura sintattica data dalla grammatica di un linguaggio L idea di base è quella di associare ad ogni costrutto del nostro linguaggio delle informazioni utili allo scopo Queste informazioni utili vengono rappresentate mediante degli attributi associati a simboli (terminali e non) della grammatica Il valore di ciascun attributo è calcolato tramite delle regole semantiche associate alle produzioni della grammatica È anche indispensabile determinare il giusto ordine di valutazione (grafo delle dipendenze e ordinamento topologico)

(SDD) Sono generalizzazioni delle grammatiche context-free in cui ad ogni simbolo della grammatica è associato un insieme di attributi Se pensiamo ad ogni nodo del parse tree come ad una struttura (un record, un oggetto), allora gli attributi per un simbolo grammaticale X sono i campi della struttura che rappresenta il nodo X Scriveremo X.b per indicare il fatto che b è un attributo di X Un attributo può rappresentare qualsiasi cosa: stringhe (anche frammenti di codice), numeri, tipi, locazioni di memoria, entry di tabelle, etc. Il valore di ogni attributo ad ogni nodo è calcolato applicando la regola semantica associata alla produzione che si usa nel nodo

Attributi Sintetizzati ed Ereditati Attributi Sintetizzati: il loro valore in un dato nodo N può essere calcolato a partire dai valori degli attributi dei nodi figli di N Se A XYZ è una produzione di una SDD e b è un attributo sintetizzato per il non terminale A, il valore di b può dipendere solo dal valore di attributi dei simboli X, Y e Z Esempio: PRODUZIONI E E 1 + T E E 1 T E T T T 1 F REGOLE SEMANTICHE E.val = E 1.val + T.val E.val = E 1.val T.val E.val = T.val T.val = T 1.val F.val

Attributi Sintetizzati ed Ereditati Attributi Ereditati: il loro valore in un dato nodo N può essere calcolato a partire dai valori degli attributi dei nodi fratelli e del nodo padre di N Se A XYZ è una produzione di una SDD e b è un attributo ereditato del simbolo Y, il valore di b dipende dal valore di attributi dei simboli A (padre), X e Z (fratelli) Un terminale può avere solo attributi ereditati tipicamente restituiti dall analizzatore lessicale (valori lessicali) Esempio: PRODUZIONI F num REGOLE SEMANTICHE F.val = num.lexval

Formalmente... Una definizione guidata dalla sintassi è una grammatica libera da contesto in cui associamo ad ogni produzione A α un insieme di regole semantiche della forma b ::= f (c 1, c 2,..., c k ) dove f è una funzione arbitraria (di solito espressa con delle espressioni) e b, c 1, c 2,..., c k sono degli attributi Se b è un attributo sintetizzato di A allora c 1, c 2,..., c k sono attributi dei simboli in α (figli di A) Se b è un attributo ereditato di un di simbolo di α allora c 1, c 2,..., c k sono attributi di simboli di α oppure di A (fratelli e padre di A) In ogni caso l attributo b dipende dagli attributi c 1, c 2,..., c k

Una SDD per un semplice desk calculator PRODUZIONI L En E E 1 + T E T T T 1 F T F F (E) F digit REGOLE SEMANTICHE print(e.val) E.val = E 1.val T.val E.val = T.val T.val = T 1.val F.val T.val = F.val F.val = E.val F.val = digit.lexval Ha solo attributi sintetizzati, è una definizione S-attributed

Una SDD per un semplice desk calculator La grammatica genera le espressioni aritmetiche tra cifre seguite dal newline (n) Ogni non terminale ha un attributo sintetizzato val Il terminale num ha un attributo sintetizzato lexval il cui valore è fornito dall analizzatore lessicale (è il valore della particolare sequenza di caratteri che corrisponde al token num) La regola associata al simbolo iniziale L è una chiamata di procedura che stampa un valore intero (side-effect) mentre tutte le altre regole servono per il calcolo del valore degli attributi Assunzioni e convenzioni: L uso di non terminali con pedici (es, E 1 e T 1) serve per distinguere due diverse occorrenze dello stesso non terminale in una data produzione

Una SDD per delle semplici dichiarazioni di tipo PRODUZIONI D T L T int T real L L 1, id L id REGOLE SEMANTICHE L.inh := T.type T.type = int T.type = real L 1.inh = L.inh addtype(id.entry, L.inh) addtype(id.entry, L.inh)

Una SDD per delle semplici dichiarazioni di tipo Gli attributi ereditati vengono usati per distribuire informazioni sul tipo degli identificatori in una dichiarazione Una dichiarazione è costituita (in molti linguaggi di programmazione come C, Java,... ) da un identificare di tipo T seguito da una lista L di identificatori type è un attributo sintetizzato di T, mentre inh è un attributo ereditato per L Inizialmente il valore dell attributo type viene passato all attributo inh di L (mediante la regola L.type := T.type) Durante la costruzione della lista, ogni elemento passa al successivo il valore di inh (mediante la regola L 1.inh = L.inh) La procedura addtype, prende in input l entrata nella tabella dei simboli per un qualche identificare (id.entry) ed un tipo (L.inh) ed aggiunge al record dell identificare informazioni riguardanti il tipo

Le regole semantiche inducono delle dipendenze tra il valore degli attributi rappresentate mediante i cosidetti grafi delle dipendenze La valutazione, nel giusto ordine, delle regole semantiche determina il valore di tutti gli attributi dei nodi del parse tree di una stringa data La valutazione può avere anche side-effects (effetti collaterali) come la stampa di valori o l aggiornamento di una variabile globale Un parse tree che mostri i valori degli attributi ad ogni nodo è detto parse tree annotato Il processo di calcolo di questi valori prende il nome di annotazione o decorazione del parse tree

Grafo delle dipendenze Input: parse tree per una data stringa Ha un nodo per ogni attributo di ogni nodo n del parse tree Gli archi tengono conto delle dipendenze tra gli attributi indotte dalle regole semantiche Es: Sia A X Y una produzione con regola semantica associata A.a := f (X.x, Y.y) A a X x y Y

Grafo delle dipendenze Input: parse tree per una data stringa Ha un nodo per ogni attributo di ogni nodo n del parse tree Gli archi tengono conto delle dipendenze tra gli attributi indotte dalle regole semantiche Es: Sia A X Y una produzione con regola semantica associata X.x := g(a.a, Y.y) A a X x y Y

Grafo delle dipendenze: un esempio D T inh type L 1 inh L int 2 entry id 1 inh L 3 id entry 2 id 3 entry

Un algoritmo per la costruzione del grafo delle dipendenze for each nodo n nel parse tree do for each attributo a del nodo n do costruisci un nodo per a nel grafo; for each nodo n nel parse tree do for each regola semantica b := f (c 1, c 2,..., c k ) associata con una produzione usata in n do for i := 1 to k do aggiungi un arco dal nodo per c i al nodo per b Attenzione: per le chiamate di procedure (Es: addtype(id.entry, L.type)) aggiungiamo un nodo (e, quindi, un attributo) fittizio

D T 4 inh type 5 L 1 int inh 7 inh 6 L 3 2 id entry 1 L 2 3 id 8 entry 2 9 10 id 3 1 entry

Un ordinamento topologico di un grafo diretto aciclico è un qualsiasi ordinamento dei nodi n 1 n 2... n k tale che se esiste un arco nel grafo dal nodo n i al nodo n j (se la coppia (n i, n j ) A) allora n i precedete n j nell ordinamento (n i n j ) ogni coppia i, j Un qualsiasi ordinamento topologico del grafo delle dipendenze dà un ordine valido in cui le regole semantiche possono essere valutate Nell ordinamento topologico i valori degli attributi c 1, c 2,..., c k di una regola b := f (c 1, c 2,..., c k ) sono disponibili sempre prima che f sia valutata

La traduzione specificata da una qualsiasi definizione guidata dalla sintassi può essere sempre e comunque implementata nel seguente modo: 1 si costruisce il parse tree 2 si costruisce il grafo delle dipendenze 3 si trova un ordinamento topologico del grafo 4 si valutano le regole semantiche dei nodi secondo l ordinamento topologico

Consideriamo, di nuovo, la valutazione di int d 1, d 2, d 3. Abbiamo: (1) costruito il parse tree dalla stringa, (2) costruito il grafo delle dipendenze e (3) identificato un ordinamento topologico del grafo. Ora valutiamo le regole in base all ordinamento, ottenendo il seguente frammento di codice: T.type := int; L 1.inh := T.type; L 2.inh := L 1.inh; L 3.inh := L 2.inh; addtype(id 3.entry, L 3.inh); addtype(id 2.entry, L 2.inh); addtype(id 1.entry, L 1.inh);

Ordinamenti topologici e cicli La presenza di un ciclo in in grafo delle dipendenze rende impossibile definire un ordinamento topologico e, di conseguenza, uno schema di valutazione per le regole semantiche. Ad esempio, il seguente ciclo sta ad indicare che per calcolare il valore dell attributo a abbiamo bisogno del valore dell attributo b, che a sua volta dipende dal valore di a... a b

Una SDD che usa solo attributi sintetizzati è detta S-attributed Un parse tree di una defizione S-attributed può sempre essere annotato valutando le regole semantiche per gli attributi in maniera bottom-up (dalle foglie alla radice) In genere, basta una semplice visita in postordine del parse tree secondo il seguente schema (dove N è la radice del parse tree): postorder (N) for each (figlio C di N, da sinistra a destra) postorder(c) valuta gli attributi di N Possono quindi essere implementate facilmente durante il parsing LR Generatori automatici di LR-parser possono essere modificati per implementare una definizione S-attributed basata su una grammatica LR

Valutazione degli attributi in una definizione S-attributed Grafo delle dipendenze per 3 4n L 8 E 7 val n T 6 val T 3 val * F 5 val 2 4 F lexval digit lexval digit 1 lexval

Valutazione degli attributi in una definizione S-attributed Albero annotato per 3 4n L E.val=12 n T.val=12 T.val=3 * F.val=4 F.val=3 digit.lexval=4 digit.lexval=3

Definizioni L-Attributed È una sottoclasse di SDD per la quale riusciamo ad identificare un ordine di valutazione; impone dei vincoli sugli attributi In particolare ogni attributo deve essere: 1 sintettizzato oppure 2 ereditato, ma... se X i.a è un attributo ereditato il cui valore è calcolato tramite una regola associata alla produzione A X 1 X 2... X n, tale regola può usare (a) attributi ereditati di A (b) attributi (ereditati e sintetizzati) dei simboli X 1, X 2,..., X i 1 (c) attributi (ereditati e sintetizzati) di X i solo se in questo modo non si formano cicli nel grafo delle dipendenze (d) se X i è un terminale, la regola può anche dipendere da valori lessicali per X i

Una Definizione L-Attributed PRODUZIONI D T L T int T real L L 1, id L id REGOLE SEMANTICHE L.inh := T.type T.type = int T.type = real L 1.inh = L.inh addtype(id.entry, L.inh) addtype(id.entry, L.inh)

Una definizione non L-attributed e non S-attributed PRODUZIONI A B C REGOLE SEMANTICHE A.s = B.a B.i = f (C.b, A.s) s è un attributo sintetizzato di A (dipende da un attributo a di un nodi figlio) i è un attributo ereditato di B, ma tale attributo dipende da : un attributo sintetizzato (e non ereditato) di A un attributo di C, fratello destro (e non sinistro) di B

Syntax Tree Vediamo ora come sia possibile usare le definizioni guidate dalla sintassi per specificare (implementare) la costruzione di syntax tree per espressioni Abbiamo parlato di syntax tree all inizio del corso: un albero sintattico (astratto) è una forma condensata di un parse tree che è utile per rappresentare i costrutti dei linguaggi Operatori e le parole chiave non appaiono come foglie, ma sono associati a nodi interni; sulle foglie troviamo invece gli operandi Un altra semplificazione è che le catene di applicazione di una singola produzione vengono collassate

Costruzione di un Syntax Tree Problema: definire delle regole semantiche per le produzioni della seguente grammatica che producano in output il syntax-tree per la stringa in input E E 1 + T E E 1 T E T T (E) T num T id

Vediamo ora in dettaglio come costruire gli alberi sintattici per le espressioni aritmetiche; Costruiamo, inanzitutto, i sottoalberi per le sottoespressioni creando un nodo per ogni operatore ed operando I figli di un nodo operatore altro non sono che le radici dei sottoalberi che rappresentano le sottoespressioni che definiscono lespressione principale Ogni nodo di un syntree può essere implementato mediante un record con diversi campi

In un nodo operatore un campo identifica l operatore stesso e i campi rimanenti son i puntatori ai nodi operandi; l operatore è spesso detto etichetta del nodo I nodi in un syntree possono avere campi addizionali per gli attributi che sono stati definiti In un nodo operando un campo identifica l operando che può essere un identificatore o un numero I campi rimanenti possono rappresentare un entrata alla symbol table (nel caso in cui l operando sia un identificatore) o un valore (nel caso in cui l operando sia un numero)

Usiamo le seguenti funzioni per costruire i nodi dei syntree per espressioni con operatori binari: 1 mknode(op, left, right) crea un nodo operatore con etichetta op e due campi puntatore all operando destro e sinistro 2 mkleaf (id, entry) crea un nodo identificatore con etichetta id ed un puntatore entry alla tabella dei simboli 3 mkleaf (num, val) crea un nodo numero con etichetta num e un campo val contentente il valore

Il seguente frammento di programma crea (in maniera bottom-up) un syntax tree per lespressione a 4 + c 1. p 1 = mkleaf (id, entry a ); 2. p 2 = mkleaf (num, 4); 3. p 3 = mknode(, p 1, p 2 ); 4. p 4 = mkleaf (id, entry c ); 5. p 3 = mknode( +, p 3, p 5 );

SDD per i Syntax Trees Diamo una definizione guidata dalla sintassi S-attributed per la costruzione dell albero sintattico di una espressione contenente gli operatori + e - Introduciamo un attributo nptr per ogni simbolo non terminale per tener traccia dei puntatori ritornati dalle funzioni di creazione dei nodi Produzioni E E 1 + T E E 1 T E T T (E) T id T num Regole Semantiche E.nptr := mknode( +, E 1.nptr, T.nptr) E.nptr := mknode(, E 1.nptr, T.nptr) E.nptr := T.nptr T.nptr := E.nptr E.nptr := mkleaf(id, id.entry) E.nptr := mkleaf(num, num.val)

Albero annotato