Escolar Documentos
Profissional Documentos
Cultura Documentos
G. Michele Pinna
Univ. Cagliari
1 / 64
Parsing
2 / 64
Analisi sintattica
Quando abbiamo un programma scritto in un linguaggio di program` scritto correttamazione dobbiamo prima controllare se il programma e mente e, contemporaneamente, tradurlo in modo che sia eseguibile un esempio: consideriamo unespressione regolare (il programma), ` il linguaggio associato il risultato dellesecuzione del programma e allespressione regolare cosa facciamo? ` corretta - analizziamo lespressione per scoprire che e - contemporaneamente associamo allespressione o una nuova espressione (sintassi astratta) oppure opportune direttive per eseguirla ` un esecutore (che a noi ora non interessa) - ci sara
3 / 64
Lo schema
token
programma sorgente
analizzatore lessicale
parser
next
parse tree
rappresentazione intermedia
` tutto deterministico! Analizzatore lessicale: linguaggi regolare, e Parser: piu ` complesso: vediamo come fare
4 / 64
Analisi sintattica
Un compilatore deve produrre codice oggetto e deve anche controllare che il programma in input sia scritto secondo le regole della sua sintassi Lanalisi lessicale controlla che i token siano scritti bene, e genera una stringa di token (i token sono, nel linguaggio di programmazione, tutte le sequenze di caratteri delimitate da spazi o a capo) Lanalisi sintattica prende la stringa di token - non tutte le stringhe di token sono programmi validi ` derivabili dalla gramma- dobbiamo riconoscere quelle buone, cioe tica che descrive le regole della sintassi del linguaggio ` valida, creiamo un albero di parsing (sintassi Se la stringa di token e astratta) Ovviamente partiamo da grammatiche non ambigue (abbiamo visto ` quando possibile) come eliminare le ambiguita,
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 5 / 64
C if B then C else C
` ambigua: nella frase La grammatica e if E1 then if E2 then S1 else S2 non sappiamo a quale if si riferisce lelse, ed abbiamo due derivazioni leftmost diverse. La trasformiamo in C C1 | C2 C 1 if B then C 2 else C 2 C 2 if B then C 1 else C 2, C 2 if B then C
6 / 64
Parsing top-down
Costruisce un albero di parsing per una stringa, iniziando dalla radice e creando i nodi in ordine depth-rst (dato un nodo, prima lui e poi i gli da destra a sinistra, ricorsivamente) Trova la derivazione leftmost per una stringa Ad ogni passo, una produzione viene usata per trasformare una variabile Una volta scelta la produzione, bisogna individuare i simboli terminali della parte destra della produzione nella stringa in input if E1 then if E2 then S1 else S2 la derivo come C C 1 if B then C 2 else C 2 if B then if B then C else C 2 e poi i vari C , C 1 e C 2, ed i B , diventeranno espressioni booleane e comandi...
7 / 64
si sceglie una produzione per il simbolo di variabile: A X1 X2 Xk per ogni 1 i k ` una variabile chiamo la procedura (una delle) associata (e) - se Xi e ` un terminale ed e ` uguale al simbolo corrente, passa al prossimo - se Xi e simbolo della stringa - altrimenti errore
la produzione (procedura) scelta potrebbe non essere quella giusta - bisogna permettere il backtracking - errore vuol solo dire che bisogna tornare al passo 1 per scegliere unaltra produzione (procedura) per A, - se non ce ne siano piu ` fallisce - anche il puntatore al simbolo corrente deve tornare indietro
8 / 64
Esempio
Consideriamo la grammatica S cAd , A ab | a e la parola cad : c a b S ha solo una produzione (procedura), quindi usiamo quella, e il puntatore scorre anche c : Creo un albero con la sola radice S ed un puntatore allinizio della stringa: S c a d
a b ` diverso dal prossimo simbolo della stringa (d ). Torniamo indietro e proviamo la seconda il puntatore scorre anche a, ma b e produzione per A: S c A d
a ` uguale al possimo simbolo nella stringa. Abbiamo (il puntatore torna indietro alla c e poi scorre a). Il puntatore legge d che e creato un albero di parsing G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 9 / 64
Problemi
La grammatica potrebbe avere delle produzioni A A , quindi richiamerebbe sempre se stessa e potrebbe dare luogo ad una catena innita ` leftmost)... (ricordiamo che la derivazione e Usiamo quanto visto per la forma normale di Greibach:
Lemma 2 consideriamo una grammatica G = (V, , P , S ) in cui tutti i simboli sono utili. Consideriamo tutte le produzioni A A1 | A2 | . . . | Ak e siano tutte le altre produzioni A 1 | 2 | . . . | n che riscrivono A. Deniamo G = (V {B }, , P , S ), con B V e P ottenuto rimpiazzando tutte le produzioni che riscrivono A con le seguenti: A 1 | 2 | . . . | n e A 1 B | 2 B | . . . | n B e per la nuova variabile B si introducono le produzioni B 1 | 2 | . . . | k e B 1 B | 2 B | . . . | k B . Allora L(G ) = L(G )
10 / 64
Grammatiche LL(k )
` detta LL(k ) se nel parsing di una parola visita Una grammatica G e linput da sinistra a destra (la prima L) e simula una derivazione leftmost (la seconda). Inne il k sta per far avanzare di k posizioni il puntatore (chiamato di lookahead) per capire quale produzione usare (e lo si capisce deterministicamente)
11 / 64
Grammatiche LL(k )
` detta LL(k ) se nel parsing di una parola visita Una grammatica G e linput da sinistra a destra (la prima L) e simula una derivazione leftmost (la seconda). Inne il k sta per far avanzare di k posizioni il puntatore (chiamato di lookahead) per capire quale produzione usare (e lo si capisce deterministicamente) Il puntatore di lookahead lo uso per predirre...
11 / 64
11 / 64
12 / 64
FIRST
` linsieme di terminali che stanno allinizio di stringhe deriFIRST() e ` una qualunque stringa di simboli terminali e non vabili da , dove e Se allora
FIRST ()
Come la uso? Assumiamo di avere A | . Se FIRST () FIRST ( ) = allora basta guardare il prossimo simbolo in input. Se sta in FIRST () allora prendo A , se sta in FIRST ( ) prendo A
13 / 64
FIRST
e FOLLOW
` linsieme di terminali che stanno allinizio di stringhe deriFIRST() e ` una qualunque stringa di simboli terminali e non vabili da , dove e Se allora
FIRST ()
Come la uso? Assumiamo di avere A | . Se FIRST () FIRST ( ) = allora basta guardare il prossimo simbolo in input. Se sta in FIRST () allora prendo A , se sta in FIRST ( ) prendo A ` linsieme di terminali che possono apparire alla destra di FOLLOW (A) e A in una forma sentenziale (una derivazione da S ) Quindi esiste una derivazione S Aa se a
FOLLOW (A)
13 / 64
` una produzione allora ` una variabile e X Y1 Y2 Yk e se X e - se a FIRST (Yi ) e FIRST (Y1 ) FIRST (Yi 1 ) allora a FIRST (X ) - se FIRST (Y1 ) FIRST (Yk ) allora FIRST (X ) ` che sta in FIRST (Y1 ) sta anche in FIRST (X ), - tutto cio quindi se da Y1 non si deriva la parola vuota, allora FIRST (X ) = FIRST (Y1 ), altrimento aggiungiamo FIRST (Y2 ) e cos` via
14 / 64
mettiamo in
FIRST (X2 )
FIRST (X1 )
se FIRST (X1 ) FIRST (X2 ) contiene allora mettiamo anche tutti i simboli di FIRST (X3 ) tranne .... iteriamo no a n, se necessario se FIRST (X1 ) FIRST (Xn ) contiene , allora mettiamo anche in FIRST (X1 . . . Xn )
15 / 64
Calcolare FOLLOW
FOLLOW (S )
contiene $
se abbiamo la produzione A B allora mettiamo in FOLLOW (B ) tutto ` che sta in FIRST ( ) tranne cio se abbiamo le produzione A B o A B e ` che sta in FOLLOW (A) mettiamo in FOLLOW (B ) tutto cio
FIRST ( )
allora
16 / 64
Esempio: FIRST
Consideriamo la seguente grammatica: E TA, A +TA | , T FB , B FB | e F (E ) | id Allora: = {+, } (ho le produzioni A +TA | e chiaramente + FIRST(+TA))
FIRST (A) FIRST (B )
FIRST (FB ))
= {(, id } (ho le produzioni F (E ) | id e chiaramente ( FIRST ((E )), mentre id lo metto dato che FIRST(id ) = {id })
FIRST (F ) FIRST (T )
FIRST (T )
FIRST (E )
17 / 64
Esempio: FOLLOW
Consideriamo la seguente grammatica: E TA, A +TA | , T FB , B FB | e F (E ) | id Allora:
FOLLOW(E )
il simbolo iniziale)
FOLLOW(A) = {), $} (ho le produzioni A +TA e E TA, quindi devo ` che sta in FOLLOW(E ), dato che mettere in FOLLOW(A) tutto cio FIRST (A) contiene ) FOLLOW(T ) = {+, ), $} (ho le produzioni E TA, A +TA, quindi devo ` il simbolo mettere tutto quello che sta in FIRST(A) = {+, } e $ perche ` coinvolto) iniziale e FOLLOW(B ) = {+, ), $} (ho le produzioni T FB , B FB , quindi devo mettere $ e tutto quello che sta in FOLLOW(T ) = {+, ), }) FOLLOW(F ) = {+, , ), $} (ho le produzioni T FB , B FB , quindi devo ` {}, poi devo mettere mettere anche FIRST (B ) meno , cioe FOLLOW(T ) = {+, ), $} dato che sta in FIRST (B ) )
18 / 64
Grammatiche LL(1)
Sono grammatiche tali che: Se G ha due produzioni A e A allora = (da e non si possono derivare ` uno tra e stringhe che iniziano con lo stesso terminale e al piu ` derivare la stringa vuota) puo se FIRST () allora FIRST ( ) e FOLLOW ( ) sono disgiunti: , non deriva alcuna stringa che inizia con un terminale in FOLLOW ( ) se FIRST ( ) allora FIRST () e FOLLOW () sono disgiunti: , non deriva alcuna stringa che inizia con un terminale in FOLLOW ()
FIRST () FIRST ( )
19 / 64
e$
FOLLOW (A)
20 / 64
Esempio
E TA, A +TA | , T FB , B FB | e F (E ) | id vediamo la riga per E : M[E , id ] contiene E TA dato che id
FIRST (TA) FIRST (TA)
= {(, id } e
M[E , ] non contiene nulla, dato che FIRST (TA) = {(, id } e non contiene neanche M[E , (] contiene E TA, dato che (
FIRST (TA) FIRST (TA)
= {(, id } e non
M[E , $] non contiene nulla, dato che $ FIRST (TA) = {(, id } e non contiene neanche
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 21 / 64
Esempio: completiamo la M
E TA, A +TA | , T FB , B FB | e F (E ) | id
E A T B F
id E TA T FB
+ A +TA
( E TA T FB
A A B FB F (E ) B B
B F id
e usiamola per id + id
E TA FBA id BA guardando la colonna di id , e consumiamo id ` il simbolo successivo B guardando M[B , +], che e A +TA guardando M[A, +], e consumiamo +
22 / 64
Esempio: continuazione
E TA, A +TA | , T FB , B FB | e F (E ) | id id E TA T FB B F id B FB F (E ) + A +TA T FB B B ( E TA ) $
E A T B F
A A
e usiamola per id + id
T FB guardando la colonna di id , F id guardando M[F , id ], e lo consumiamo, e, visto che siamo in fondo, guardiamo la colonna $. Troviamo B eA mettiamo tutto assieme:
E TA FBA idBA idA id + TA id + FBA id + idBA id + idA id + id
23 / 64
24 / 64
Parsing Bottom-Up
25 / 64
26 / 64
int
int
int
27 / 64
int
int
27 / 64
int
int
27 / 64
int T + int int T + int T T +T applicando applicando applicando T int T int T int T T
int
int
T T
27 / 64
int T + int int T + int T T +T T +E applicando applicando applicando applicando T int T int T int T T T T
int
int
T T E
27 / 64
int T + int int T + int T T +T T +E E applicando applicando applicando applicando applicando T int T int T int T T T T int T
int
int
T T E E
27 / 64
Un algoritmo
Un algoritmo per il parsing bottom-up Sia w la stringa in input
1 2 3 4
prendi una sottostringa non vuota di w cerca una produzione X rimpiazza con X in w ` una produzione, torna se non ci fosse alcun tale che X e indietro ripeti no a che non si raggiunge il simbolo S oppure non sono ` state provate tutte le possibilita
28 / 64
Maniglie
Durante il parsing bottom-up bisogna capire: Quale sottostringa ridurre Che produzione usare
` una sottostringa tale che X e ` una proLa maniglia (handle) e duzione che corrisponde ad un passo in una derivazione rightmost della stringa in input
Attenzione: la sottostringa piu ` a sinistra che coincide con la parte destra di ` sempre una maniglia una produzione non e
Dove ridurre: sia la stringa corrente durante un parsing bottom-up ` una stringa e la riduzione successiva sia fatta usando X . Allora e ` rightmost) di soli teminali (la derivazione e
29 / 64
Dove ridurre
Come trovo dove ridurre? Dividere la stringa in due sottostringhe:
1
La sottostringa di destra deve essere ancora esaminata (una stringa di terminali) ` messa su La sottostringa di sinistra ha terminali e non terminali (e una pila) ` marcato da un |, (e il simbolo | non e ` parte Il punto di divisione e della stringa)
30 / 64
ABC |xyz ABCx |yz Applica una produzione inversa nella parte nale della stringa di sinistra (rimpiazza una stringa di terminali in cima alla pila con la parte sinistra di una produzione) ` una produzione, allora CBxy |ijk CBA|ijk Se A xy e Come decidere quando fare shift o reduce?
REDUCE :
31 / 64
Esempio
E T + E | T , T int T | int | (E ) Consideriamo int | int + int . Se decidessimo di usare T int per ottenere T | int + int non riusciremmo poi ad arrivare ad E .... ` ancora essere ridotto no al Vogliamo ridurre solo se il risultato puo simbolo iniziale: ` prendiamo una derivazione da destra S X , allora e una maniglia di In un parsing shift-reduce, le maniglie appaiono solo in cima alla pila, mai nel mezzo:
` certamente vero: pila e ` vuota - allinizio e ` in cima alla pila e la prossima - Dopo aver ridotto una maniglia il non terminale piu ` a destra e maniglia deve essere alla destra del non terminale piu ` a destra, dato che stiamo considerando derivazioni rightmost - La sequenza di mosse shift raggiunge la prossima maniglia
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 32 / 64
LR(k )
L: scorre linput da sinistra a destra R: segue una derivazione a destra al contrario ` numero di simboli in input da guardare per prendere una ke decisione (il solito lookahead) LR(1) legge anticipatamente 1 simbolo in input per capire cosa fare ` generale: funziona per quasi tutti i costrutti che possono essere e descritti da grammatiche context free ` essere implementato efcientemente non fa backtracking, e puo trova gli errori sintattici appena possibile in uno scan da sinistra a destra dellinput ` essere applicato ad un insieme di linguaggi che contiene i puo linguaggi LL
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 34 / 64
Pressi
` ovvio come trovare le maniglie: Sappiamo che non e ` un Ad ogni passo il parser vede solo la pila, non lintero input. e ` ` presso valido se e tale che | e uno stato di un parser shift-reduce
1 2 3
un presso valido non va oltre la ne destra di una maniglia ` un presso valido dato che e ` un presso della maniglia e ` ci sono pressi validi sulla pila, non sono stati visti errori di nche parsing
` un linguaggio regoPer ogni grammatica, linsieme dei pressi validi e lare Mostreremo come costruire FA che accettano pressi validi
35 / 64
Item
` una produzione con un . nella sua parte destra (se ho X Un item e ho un unico item: X .) gli item per T (E ) sono: T .(E ) | (.E ) | (E .) | (E ). Un item indica quanto abbiamo visto di una produzione no al punto A .XYZ indica che speriamo di vedere nellinput una stringa derivabile da XYZ ` visto una stringa derivabile da A X .YZ indica che abbiamo gia X e che speriamo di vedere una derivabile da YZ nellinput ` visto una stringa derivabile da A XYZ . indica che abbiamo gie XYZ e che potrebbe essere il momento di ridurre XYZ ad A Sono spesso chiamati item LR(0)
36 / 64
GOTO ,
costruiamo gli
37 / 64
CLOSURE
consideriamo una grammatica G = (V, , P , S ), aggiungiamo S S con S nuovo non terminale, che serve a dire al parser quando fermarsi (quando riduce tramite questa produzione) ` un insieme di item per una grammatica, e ` un CLOSURE (I ), dove I e insieme tale che
1 2
e B P allora B . sta
` ntantoche ` non si puo ` aggiungere piu Applichiamo questa proprieta ` niente a CLOSURE (I ) A .B CLOSURE (I ) se pensiamo di poter vedere in futuro una ` un sottostringa derivabile da B come input, e questa sottostringa avra presso derivabile da B , per cui dobbiamo aggiungere gli item B . per tutte le B P
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 38 / 64
Esempio
la solita grammatica E T + E | T , T int T | int | (E ) Consideriamo linput (int ) ` uno stato in un parsing shift-reduce (E |) e ` un presso della parte destra di T (E ) (E e ` ridotto dopo il prossimo shift Sara litem T (E .) dice che nora abbiamo visto (E di questa produzione e speriamo di vedere )
` che la pila ha solo alcune parti Il problema nel riconoscere pressi validi e della parte destra di una produzione (se avesse una parte destra completa, potremmo ridurre), ma Queste parti sono sempre pressi della parte destra di una produzione
I = {S .E }, allora
CLOSURE(I )
Pressi
` avere molti pressi di parti destre di produzioni: siano La pila puo pref1 , pref2 , . . . , prefn Sia prefi un presso della parte destra di Xi i , allora ` prima o poi ridotto a Xi prefi verra la parte mancante di i 1 inizia con Xi ` un Xi 1 prefi 1 Xi per qualche ci sara ` ridotto alla parte ricorsivamente, prefk +1 . . . prefn prima o poi verra mancante di k ` uno stato in un parsing Consideriamo la stringa (int int ): (int |int ) e shift-reduce e: 1 ` un presso della parte destra di T (E ) ( e
2 3
` un presso della parte destra di E T e ` un presso della parte destra di T int T int e
Automi e Linguaggi Formali Anno Accademico 2010 40 / 64
Esempio
La pila di items T (.E ) E .T T int .T dice:
1 2 3
41 / 64
GOTO
La funzione GOTO la usiamo per denire le transizioni dellautoma Dato I insieme di items e X simbolo non terminale della grammatica, GOTO (I , X ) = CLOSURE (I ) dove I = {A X . | A .X I }
quindi se gli stati sono
CLOSURE(I )
` (CLOSURE(I ), X ) = GOTO(CLOSUREI , X ). Lautoma deve transizione nellautoma sara ` ovvio quale e ` il suo alfabeto: V riconoscere pressi quindi e
42 / 64
data una grammatica G = (V, , P , S ), aggiungere un simbolo S e una produzione S S alla grammatica C = CLOSURE ({S .S })
1
per ogni insieme di item I C e per ogni simbolo X V, se GOTO(I , X ) C allora aggiungi GOTO (I , X ) a C no a che non viene aggiunto piu ` niente a C
43 / 64
DFA LR(0)
1
data una grammatica G = (V, , P , S ), aggiungere un simbolo S e una produzione S S alla grammatica C = CLOSURE ({S .S })
1
per ogni insieme di item I C e per ogni simbolo X V, se GOTO(I , X ) C allora aggiungi GOTO (I , X ) a C no a che non viene aggiunto piu ` niente a C
stati: insiemi di item dalla collezione canonica Transizioni: funzione GOTO stato iniziale:
CLOSURE ({S
.S })
Altri stati: ottenuti applicando GOTO e poi facendo la chiusura Tutti gli stati sono nali
43 / 64
44 / 64
prendi una sottostringa non vuota di w cerca una produzione X rimpiazza con X in w ` una produzione, torna indietro se non ci fosse alcun tale che X e ripeti no a che non si raggiunge il simbolo S oppure non sono state ` provate tutte le possibilita
bisogna capire quale sottostringa ridurre e che produzione usare ` la maniglia (handle), che e ` una sottostringa La sottostringa da ridurre e ` una produzione che corrisponde ad un passo in tale che X e ` una derivazione rightmost della stringa in input: S X ( e ` la derivazione e ` rightmost). una stringa di soli teminali, perche
45 / 64
La sottostringa di destra deve essere ancora esaminata (una stringa di terminali) ` messa su La sottostringa di sinistra ha terminali e non terminali (e una pila) ` marcato da un |, (e il simbolo | non e ` parte Il punto di divisione e della stringa)
` contenere non Intuizione: Se ho | signica che S e puo terminali. ` non esaminato: |x1 x2 . . . xn Inizialmente, tutto linput e
46 / 64
47 / 64
CLOSURE(I ),
dove
CLOSURE(I )
` un insieme di item I tali Ogni stato dellautoma LR(0) che vogliamo costruire e che CLOSURE (I ) = I
48 / 64
CLOSURE(I ),
dove
CLOSURE(I )
` un insieme di item I tali Ogni stato dellautoma LR(0) che vogliamo costruire e che CLOSURE (I ) = I La funzione GOTO la usiamo per denire le transizioni dellautoma: dato I ` insieme di items e X V , allora GOTO(I , X ) = CLOSURE (I ) dove I e linsieme di item {A X . | A .X I }
48 / 64
DFA LR(0)
Sia G = (V, , P , S ) una grammatica context free, allora ` un insieme tale che I I (G), e
1 2
CLOSURE(I ),
dove
CLOSURE(I )
` un insieme di item I tali Ogni stato dellautoma LR(0) che vogliamo costruire e che CLOSURE (I ) = I La funzione GOTO la usiamo per denire le transizioni dellautoma: dato I ` insieme di items e X V , allora GOTO(I , X ) = CLOSURE (I ) dove I e linsieme di item {A X . | A .X I } Aumentiamo la grammatica ottenendo G = (V {S }, , P {S S }, S ). Sia Q {CLOSURE (I ) | I I (G )} tale che ogni per ogni q Q vale che q = e esistono q Q , X V tali che GOTO(q , X ) = q , allora LR(0) = (Q {}, V , GOTO, CLOSURE ({S .S }), Q )
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 48 / 64
Esempio
Ovviamente mettiamo solo gli stati raggiungibili dellautoma. Consideriamo la grammatica G = ({S }, {a, b }, {S aSb , S ab }, S ). Allora I (G ) = {S .S , S S ., S .aSb , S a.Sb , S aS .b , S aSb ., S .ab, S a.b, S ab .}
CLOSURE({S
# # # # # # # # # # #
.S }) = {S .S , S .aSb , S .ab } = q0 e
= = = = = = = = = = =
CLOSURE ({S CLOSURE ({S CLOSURE () GOTO (q1 , a)
GOTO (q0 , S ) GOTO (q0 , a) GOTO (q0 , b ) GOTO (q1 , S ) GOTO (q2 , S ) GOTO (q2 , a) GOTO (q2 , b ) GOTO (q3 , S ) GOTO (q3 , b ) GOTO (q4 , S ) GOTO (q5 , S )
S .} ) = { S S .}
CLOSURE ()
CLOSURE ({S CLOSURE ({S CLOSURE ({S GOTO (q3 , a) CLOSURE ({S GOTO (q4 , a) GOTO (q5 , a)
aS .b}) = {S aS .b} a.Sb, S a.b}) = {S a.Sb, S a.b, S .aSb, S .ab} ab.}) = {S ab.}
= = =
CLOSURE ()
= = =
CLOSURE () CLOSURE ()
aSb.}) = {S aSb.}
GOTO (q4 , b ) GOTO (q5 , b )
= =
q1
S Start a a S
q3
b
q0
q2
b
q5
q4 Quali sono le parole accettate dallautoma? , S , an b , an Sb con n 1, che sono tutti pressi di an Sb n per un qualche n 0...
50 / 64
S S.
S aS .b
S S a
Start
S .S S .aSb S .ab
S aSb.
S ab.
50 / 64
51 / 64
Esempio
q1
S
a a
q3
S b
Consideriamo
Start
q0
q2
b
q5 q4
e aabb
52 / 64
Esempio
q1
S
a a
q3
S b
Consideriamo
Start
q0
q2
b
q5 q4
e aabb
Mettiamo sulla pila q0 , vediamo a e mettiamo q2 , ora vediamo un altro a e mettiamo un altro q2 sulla pila, vediamo un b e mettiamo q4 sulla pila... posso fare la prima riduzione dato che q4 = {S ab.}. Quindi leviamo q4 e q2 e dato che GOTO(q2 , S ) = q3 mettiamo q3 sulla pila che ora contiene q0 , q2 e ` b, e q3 . Ora lautoma si trova nello stato q3 e linput che rimane da leggere e GOTO (q3 , b) = q5 = {S aSb.}. Riduciamo, leviamo q5 , q3 e q2 dalla pila, quindi siamo in q0 e con S si va in q1 = {S S .}. Abbiamo nito...
52 / 64
Esempio
q1
S
a a
q3
S b
Consideriamo
Start
q0
q2
b
q5 q4
e aabb
Da S aabb abbiamo prima ridotto usando S ab , ottenendo aSb che poi abbiamo ridotto usando S aSb
52 / 64
53 / 64
Esempio
` nella seguente condizione |t , LR(0) si trova nello stato qi , Il parser e ` in cima alla pila degli stati, e action[i , t ] contiene Shift j , allora che e vado nello stato qj di LR(0) (che metto nella pila degli stati) e metto t ` t | sulla pila, quindi lo stato del parser sara ` nella seguente condizione |t , LR(0) si trova nello stato Il parser e qi e action[i , t ] contiene Reduce X , allora metto X sulla pila al posto di , e vado nello stato qj di LR(0) che determino levando dalla pila degli stati esattamente | | elementi e metto lo stato che ottendo ` lo stato in cima alla pila degli stati dopo aver da GOTO (q , X ), dove q e ` X |t levato | | elementi, quindi lo stato del parser sara ` nella seguente condizione S |, LR(0) si trova nello stato qi e Il parser e action[i , ] contiene accept, allora accetto ` nella seguente condizione |t , LR(0) si trova nello stato qi Il parser e e action[i , t ] contiene error, allora non accetto
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 54 / 64
55 / 64
Esempio
Vediamo la tabella action per la grammatica vista prima: a b $ Shift 2 error error error error accept Shift 2 Shift 4 error error Shift 5 error error Reduce S ab error error Reduce S aSb error
0 1 2 3 4 5
56 / 64
Tabella goto[i , A]
quando facciamo le riduzioni dobbiamo anche trovare lo stato corretto in cui mettere lautoma. Mentre la tabella action la costruiamo per i terminali, la tabella goto la costruiamo per i non terminali (che corrispondono a delle azioni Reduce goto[4, S ] = 3. Il parser si trova in questa situazione: an b|b n1 $ e action[4, b ] contiene Reduce S ab , quindi rimuovo ab da an b, ag...) (q0 , an1 S ) = q3 (uso la giungo S e determino lo stato. GOTO goto[5, S ] = 3. Il parser si trova in questa situazione: an Sb|b n1 $ e action[5, b ] contiene Reduce S aSb , quindi rimuovo aSb da an Sb, ...) (q0 , an1 S ) = q3 (uso la aggiungo S e determino lo stato. GOTO tutte le altre caselle sono vuote
57 / 64
Ricapitoliamo
Vogliamo costruire un parser bottom-up (shift-reduce):
1 2
prendo la grammatica G ` il simbolo iniziale aggiungo la nuova produzione S S , dove S e di G denisco lautoma LR(0) (e quindi determino gli stati fatti da item canonici) denisco le tabelle action e goto come visto prima
ho anche una pila dove accumulo simboli (e dove faccio push e pop di simboli) ho linput da scandire da sinistra a destra, con un simbolo terminatore sulla destra
58 / 64
Conitti
` avere dei problemi, legati a situazioni in questo schema di parsing puo cui non si sa decidere: Conitto shift/reduce: Lo stato qi contiene gli item X .a e X . Conitto reduce/reduce: Lo stato qi contiene gli item X . e X . ` SLR se non ci sono conitti (S sta per simple) Una grammatica e
59 / 64
Quindi ora faccio una riduzione solo nel caso in cui dopo quella posso ` un esempio delle euristiche) continuare a ridurre (questo e
60 / 64
Esempio
Consideriamo S S + S | S S | (S ) | int
q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 = = = = = = = = = = =
{S .S , S .S + S , S .S S , S .(S ), S int } {S S .} {S int .} {S (.S ), S .S + S , S .S S , S .(S ), S int } {S S . + S , S S . S } {S S + .S , S .S + S , S .S S , S .(S ), S int } {S S .S , S S .S , S .S + S , S .S S , S .(S ), S int } {S S + S ., S S . + S , S S . S } {S S S ., S S . + S , S S . S } {S (S .)} {S (S ).}
lo stato q7 (e q8 ) contiene sia S S + S . che da un reduce, e S S . S che da un shift... mettiamo le precedenze trasformando la grammatica in non ambigua
G. Michele Pinna (Univ. Cagliari) Automi e Linguaggi Formali Anno Accademico 2010 61 / 64
Alcune conclusioni
Il parser LR lo denisco usando la nozione di presso i pressi sono riconosciuti da un automa tramite lautoma costruisco le tabelle action e goto per il parsing SLR: la tabella action la costruiamo usando FOLLOW Posso fare meglio, ma prima:
1 2
` anche LR ogni grammatica LL e ` non ambigua e il linguaggio e ` accettato ogni grammatica LR(0) e da un automa deterministico il parsing LR funziona anche con grammatiche ambigue
62 / 64
` un parser in cui la costruzione della tabella action Un parser SLR(1) e non genera conitti ` unottimizzazione del parser SLR(1) un parser LALR e
63 / 64
Esercizi
1
Considerate la grammatica S B , B aBc | C , C bCc | . Trovate ` LL(1)? il FIRST ed il FOLLOW di ogni simbolo. La grammatica e fate il parsing top down delle seguenti parole aabbcccc , aabbbcccc e abbbbccccc dicendo dove eventualmente fallisce della grammatica S E , E E + T | T , T id | (E ), dite quali sono gli item LR(0) e costruite lautoma per riconoscere le maniglie. Non ` gia ` aumentata) avete bisogno di aumentare la grammatica (e costruite le tabelle action e goto per la grammatica vista prima, usando il FOLLOW fate il parsing bottom up di x + (y + x ) (x e y sono generate da id ) della grammatica S E , E E + T | T , T T F | F , F id | (E ), dite quali sono gli item LR(0) e costruite lautoma per riconoscere le ` gia ` maniglie. Non avete bisogno di aumentare la grammatica (e aumentata) fate il parsing bottom up di (x (y + z )) (x , y e z sono generate da id )
Automi e Linguaggi Formali Anno Accademico 2010 64 / 64
5 6