Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Appunti Compilatori, Appunti di Elementi di Informatica

Appunti Compilatori e linguaggi di programmazione

Tipologia: Appunti

2015/2016

Caricato il 02/08/2016

Syloader
Syloader 🇮🇹

5

(1)

2 documenti

1 / 8

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
Questions & Answers
I prova intercorso LPC
1. Q:% Quando%una%grammatica%context3free%è%detta%ambigua?%Dare%un%esempio%di%
grammatica%ambigua.%
A: Una grammatica che produce più di un albero di parsing per una data frase è detta ambigua, in altri termini
una grammatica è ambigua se ammette più di una derivazione destra o più di una derivazione sinistra per una stessa
frase.
Una sentenza o frase di una grammatica è una forma sentenziale che non contiene alcun non terminale (formata
solo da terminali).
Esempio:
G: E à E + E | E * E | id
Frase: id + id * id
E E + E id + E id + E * E id + id * E id + id * id
E E * E E + E * E id + E * E id + id * E id + id * id
2. Q:% Descrivere%brevemente%i%tre%concetti%lessema,%pattern,%token%
A:% %
a. Lessema:%sequenza%di%caratteri%del%programma%sorgente%che%corrisponde%al%pattern%
di%un%token%ed%è%identificata%dall’analizzatore%lessicale%come%una%specifica%istanza%di%
quel%token.%
b. Pattern:%descrizione%compatta%delle%forme%che%il%lessema%di%un%token%può%assumere.%
Nel%caso%di%una%parola%chiave,%il%pattern%è%semplicemente%la%sequenza%di%caratteri%
che%formano%quella%parola%chiave.%Per%gli%identificatori%ed%altri%tipi%di%token,%il%
pattern%è%una%struttura%più%complessa%cui%possono%corrispondere%più%stringhe.%
c. Token:%coppia%costituita%da%un%nome%e%un%valore%di%un%attributo%opzionale.%Il%nome%
del%token%è%un%simbolo%astratto%che%rappresenta%uno%specifico%tipo%di%unità%
lessicale,%per%esempio%una%particolare%parola%chiave%oppure%una%sequenza%di%
caratteri%che%denota%un%identificatore.%I%nomi%dei%token%sono%i%simboli%che%il%parser%
riceve%in%ingresso%ed%elabora.%
3. Q:% Dare%lo%schema%a%blocchi%di%un%qualsiasi%parser%LL%
A:
pf3
pf4
pf5
pf8

Anteprima parziale del testo

Scarica Appunti Compilatori e più Appunti in PDF di Elementi di Informatica solo su Docsity!

Questions & Answers

I prova intercorso LPC

1. Q: Quando una grammatica context-­‐free è detta ambigua? Dare un esempio di

grammatica ambigua.

A: Una grammatica che produce più di un albero di parsing per una data frase è detta ambigua, in altri termini una grammatica è ambigua se ammette più di una derivazione destra o più di una derivazione sinistra per una stessa frase. Una sentenza o frase di una grammatica è una forma sentenziale che non contiene alcun non terminale (formata solo da terminali). Esempio: G: E à E + E | E * E | id Frase: id + id * id E ⇒ E + E ⇒ id + E ⇒ id + E * E ⇒ id + id * E ⇒ id + id * id E ⇒ E * E ⇒ E + E * E ⇒ id + E * E ⇒ id + id * E ⇒ id + id * id

2. Q: Descrivere brevemente i tre concetti lessema , pattern , token

A:

a. Lessema: sequenza di caratteri del programma sorgente che corrisponde al pattern

di un token ed è identificata dall’analizzatore lessicale come una specifica istanza di

quel token.

b. Pattern: descrizione compatta delle forme che il lessema di un token può assumere.

Nel caso di una parola chiave, il pattern è semplicemente la sequenza di caratteri

che formano quella parola chiave. Per gli identificatori ed altri tipi di token, il

pattern è una struttura più complessa cui possono corrispondere più stringhe.

c. Token: coppia costituita da un nome e un valore di un attributo opzionale. Il nome

del token è un simbolo astratto che rappresenta uno specifico tipo di unità

lessicale, per esempio una particolare parola chiave oppure una sequenza di

caratteri che denota un identificatore. I nomi dei token sono i simboli che il parser

riceve in ingresso ed elabora.

3. Q: Dare lo schema a blocchi di un qualsiasi parser LL

A:

4. Q: Dare lo schema a blocchi di un qualsiasi parser LR

A:

5. Q: Descrivere la differenza di modello di compilazione ed esecuzione fra i linguaggi di

programmazione C e Java. Utilizzare diagrammi a blocchi. Quando interviene l'interprete

nei due linguaggi?

A:

a. C: l’interprete interviene quando il file eseguibile a.out deve essere eseguito. Viene

effettuato il linking, il loading ed in seguito può essere interpretato dal processore.

b. Java: l’interprete interviene in fase di esecuzione. Il bytecode viene dato in pasto

alla jvm(specifica per SO ed hw sottostante) che lo interpreta.

6. Q: Descrivere molto brevemente il modello generale per l'esecuzione di una

grammatica ad attributi.

  1. Q: Indicare un tipo di grammatica ad attributi con cui è possibile eseguire le azioni durante il parsing. Motivare la risposta.

8. Q: Dimostrare che se la tabella di parsing LR(1) costruita su una grammatica G non ha

conflitti shift/reduce allora anche la tabella di parsing LALR(1) sulla stessa grammatica G

non può avere conflitti shift/reduce. (Suggerimento: si dimostra per assurdo)

A: Suppongo per assurdo che nell’unione si abbia un conflitto in corrispondenza del

simbolo di lookahead a poiché si ha un item del tipo [Aàα. {a}] che richiede una

riduzione secondo la produzione Aàα e un altro item del tipo [Bàβ.aγ {b}] che richiede

un passo di shift. Dato che i core degli stati originali devono essere identici, devono

contenere entrambi gli item [Aàα. {a}] e [Bàβ.aγ {b}]. In questo caso un tale stato già

presentava originariamente un conflitto S/R e per tanto la grammatica non poteva essere

LR(1) come invece abbiamo ipotizzato. Per questo la fusione di stati con core comune non

può mai produrre un conflitto S/R che non sia già presente in uno degli stati di partenza e

C source

code CC^ a.out^

Interprete

(hw) Output

Java source

code JavaC^ Bytecode^

JVM

interprete Output

sostituzione di tale occorrenza di β con A produce la forma sentenziale destra precedente in una derivazione destra di γ. Il processo di handle pruning consiste nell’ottenere una derivazione destra al rovescio a partire dalla stringa w costituita da soli terminali, riconoscendo l’handle ad ogni passo e sostituendo il corpo della produzione associata con la testa di tale produzione.

13. Q: Quali condizioni deve verificare una grammatica per essere LL(1)?

A: Una grammatica è LL(1) quando, date due produzioni distinte A àα|β:

  • FIRST(Aà α)∧FIRST(Aà β) = Ø

o Non esiste alcun terminale a tale che sia α che β derivano stringhe che

iniziano con a.

  • NOT(α⇒ε ∧ β⇒ ε)

o Al più una tra α e β deriva la stringa nulla ε

  • Se α⇒*ε allora FIRST(β) ∧ FOLLOW(A) = Ø e viceversa

o Se β⇒*ε allora α non deriva alcuna stringa che inizia con un terminale

appartenente all’insieme FOLLOW(A); allo stesso modo, se α⇒*ε, allora β

non deriva alcuna stringa che inizia con un terminale in FOLLOW(A).

Teoria LPC

Analisi Lessicale La prima fase di compilazione e detta analisi lessicale o scanning. L'analizzatore lessicale legge il programma in ingresso come un flusso di caratteri che raggruppa in sequenze complete e dotate di significato dette lessemi. Analisi Sintattica La seconda fase della compilazione e l'analisi sintattica o parsing. L' analizzatore sintattico, detto parser, utilizza il primo elemento dei token prodotti dall'analizzatore lessicale per produrre una rappresentazione intermedia ad albero che indica la struttura grammaticale della sequenza dei token. Una tipica rappresentazione intermedia è l' albero sintattico o syntax tree in cui un nodo interno rappresenta un'operazione mentre i figli di tale nodo rappresentano gli argomenti dell'operazione. Analisi Semantica L' analizzatore semantico utilizza l'albero sintattico e le informazioni nella tabella dei simboli per verificare la consistenza del programma sorgente rispetto alla definizione del linguaggio. Inoltre raccoglie ulteriori informazioni di tipo e le memorizza nell'albero sintattico oppure nella tabella dei simboli per poterle poi usare nella fase successiva di generazione del codice intermedio. Una parte importante dell'analisi semantica e il controllo sui tipi o type checking che ha lo scopo di verificare che ogni operatore abbia operandi di tipo adeguato. Generazione del codice intermedio Dopo tali fasi molti compilatori generano esplicitamente una rappresentazione intermedia di basso livello (simile al codice macchina) che può essere vista come un programma per una macchina virtuale. Una rappresentazione intermedia dovrebbe avere due importanti proprietà: deve essere semplice da generare e semplice da tradurre nel linguaggio della macchina destinazione. Considereremo una forma di rappresentazione intermedia, nota come codice a tre indirizzi o three-address code, che consiste in una sequenza di istruzioni a tre operandi simili a quelle di un generico linguaggio assembly. Ogni operando può essere considerato un registro. Ottimizzazione del codice La fase di ottimizzazione del codice indipendente dalla macchina di destinazione cerca di migliorare il codice intermedio in modo da consentire un migliore risultato nella generazione del codice destinazione. Generatore codice macchina Il generatore di codice prende come ingresso la rappresentazione intermedia e la traduce nel programma destinazione. Se il linguaggio destinazione e un codice macchina, in primo luogo vengono selezionati registri e/o locazioni di memoria per ognuna delle variabili utilizzate dal programma. Quindi, le istruzioni del linguaggio intermedio vengono tradotte in opportune sequenze di istruzioni macchina che realizzano le operazioni desiderate. Tabella dei simboli La tabella dei simboli è appunto una struttura dati che contiene, per ogni nome di variabile, un record i cui campi indicano i vari attributi del nome. Una tale struttura dati deve essere organizzata in modo tale da rendere efficiente sia l'accesso e la modifica delle informazioni sia la ricerca di uno specifico nome. Fasi del compilatore (oltre lo schema) La fase di analisi di un compilatore suddivide un programma sorgente in elementi costitutivi di base e produce una rappresentazione interna detta codice intermedio. La fase di sintesi, quindi, traduce la rappresentazione intermedia nel programma destinazione.

Definizioni regolari Per semplificare la notazione, introduciamo alcuni nomi simbolici per indicare specifiche espressioni regolari in modo da poter usare poi tali nomi come se fossero simboli nella descrizione di espressioni più complesse. Eliminazione della ricorsione sinistra Una grammatica e detta ricorsiva a sinistra se ha un non-terminale A per cui esiste una derivazione A=>Aa della stringa a. I parser top-down non possono gestire grammatiche con ricorsione sinistra, per cui si rende necessaria un metodo di trasformazione mirato a eliminare tale ricorsione. Fattorizzazione sinistra La fattorizzazione sinistra e una trasformazione utile per ottenere una grammatica più adatta per il parsing predittivo (top- down). Grammatiche LL(1) E sempre possibile costruire un parser predittivo (cioè un parser a discesa ricorsiva senza backtracking) a partire da una grammatica della classe LL(l). La prima "L" indica che la sequenza d'ingresso viene analizzata da sinistra (left, appunto) verso destra, la seconda "L'' specifica che si costruisce una derivazione sinistra (leftmost derivation) e infine l' "1" fra parentesi indica che le decisioni durante il parsing vengono prese analizzando un solo simbolo di lookahead. Parsing bottom-up Il parsing bottom-up procede alla costruzione di un albero di parsing per una data stringa d'ingresso cominciando dalle foglie (bottom) e procedendo verso l'alto (up) fino alla radice. Front-end & back-end La parte di analisi ha lo scopo di suddividere il programma sorgente in elementi di base e di imporre loro una struttura grammaticale. Quindi, basandosi su tale struttura, costruisce una rappresentazione intermedia del programma sorgente. Se l'analisi individua nel programma sorgente dei costrutti sintatticamente scorretti o semanticamente non significativi deve fornire messaggi informativi in modo che l'utente possa provvedere a correggere gli errori. La parte di analisi raccoglie inoltre informazioni sul programma sorgente e le memorizza in una struttura dati detta tabella dei simboli o symbol table che viene poi passata alla successiva parte di sintesi insieme alla rappresentazione intermedia. La parte di sintesi, a partire dalle informazioni nella tabella dei simboli e dalla rappresentazione intermedia, costruisce il programma destinazione. La parte di analisi e anche detta front-end del compilatore, mentre la parte di sintesi è detta back end. GRADI DI LIBERTÀ

  1. Quale non terminale espandere
  2. Quale produzione scegliere
FIRST
FOLLOW