Linguaggi context free Analizzatori sintattici e alberi di analisi E E * E ( E ) id E + E id id 2 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche Context Free • Una grammatica CF ha produzioni del tipo X → α con X ∈ N e α ∈ (T ∪ N)* ▫ sono “libere dal contesto” perché la sostituzione di X è indipendente dal contesto in cui si trova. E’ sempre possibile sostituire X con α βXγ ⇒ βαγ G ∀ β,γ ∈ (T ∪ N)* ▫ permettono di definire linguaggi abbastanza espressivi come i linguaggi di programmazione, le espressioni aritmetiche e… le espressioni regolari 3 Marco Maggini Grammatiche CF – Tecnologie per l'elaborazione del linguaggio espressioni artimetiche E → “numero” E→(E) E → E + E E → E – E T = {“numero”,(,),+,-,*,/} N = {E} E → E * E E → E / E ▫ La grammatica definisce la struttura delle espressioni aritmetiche in modo ricorsivo ▫ Il simbolo terminale “numero” corrisponde ad un insieme di stringhe che si possono descrivere con una RE ▫ A partire da simbolo E si possono generare tutte le espressioni aritmetiche possibili 4 Marco Maggini Grammatiche CF - Tecnologie per l'elaborazione del linguaggio esempio S → aB A → bAA T = {a,b} S → bA B → b N = {S,A,B} A → a B →bS A →aS B →aBB • In genere è complesso descrivere in modo compatto qual è il linguaggio definito da una grammatica ▫ Nel caso dell’esempio L(G) è il linguaggio che in base alla scelta del simbolo di start * w tale che w ha un ugual numero di a e b S ⇒ * w tale che w ha un numero di a maggiore di 1 rispetto a quello di b A ⇒ * w tale che w ha un numero di b maggiore di 1 rispetto a quello di a B ⇒ ▫ La dimostrazione si fa per induzione 5 Marco Maggini Gramatiche CF – Tecnologie per l'elaborazione del linguaggio esempio: dimostrazione • Per |w| = 1 le uniche derivazioni possibili sono * ae B⇒ * b ▫ A ⇒ • Si suppone che le ipotesi siano vere per |w|=k-1 * w può essere ottenuta da ▫ S ⇒ S → aB dove aB = a w1 con |w1| = k-1 e B ⇒ w1. Per ipotesi di induzione w1 ha 1 b in più e quindi w ha lo stesso numero di a e b S → bA dove bA = b w1 con |w1| = k-1 e A ⇒ w1. Per ipotesi di induzione w1 ha 1 a in più e quindi w ha lo stesso numero di a e b ▫ A ⇒ w può essere ottenuta da A → aS dove aS = a w1 con |w1| = k-1 e S ⇒ w1. Per ipotesi di induzione w1 ha lo stesso numero di a e b e quindi w ha 1 a in più A → bAA dove bAA = b w1w2 con |w1| < k-1 |w2| < k-1 e A ⇒ w1, A ⇒ w2. Per ipotesi di induzione w1, w2 hanno 1 a in più delle b e quindi in totale c’è 1 a in più in w * w si procede in modo analogo ▫ B ⇒ 6 Marco Maggini Tecnologie per l'elaborazione del linguaggio Alberi di analisi (parse tree) • La derivazione di ogni stringa appartenente ad un linguaggio generato da una grammatica CF si può rappresentare con un albero E → id E E→(E) E * E ( E ) id E + E E → E + E E → E – E E → E * E E → E / E id id (id+id)*id 7 Marco Maggini Tecnologie per l'elaborazione del linguaggio Parse tree - definizione • I nodi dell’albero di analisi sono etichettati dai simboli terminali o non terminali ▫ I simboli terminali si trovano nelle foglie ▫ I simboli non terminali si trovano nei nodi interni ▫ La radice corrisponde al simbolo non terminale di start • Ogni nodo interno corrisponde esattamente ad una produzione ▫ Il nodo padre è il simbolo non terminale che viene espanso ▫ I nodi figli corrispondono ai simboli terminali e non terminali nella parte destra della produzione. I figli sono ordinati nello stesso modo dei rispettivi simboli nella stringa della produzione • La struttura di analisi è un albero per il fatto che le produzioni hanno la struttura N→ α propria delle grammatiche CF ▫ La stringa analizzata è leggibile con una visita anticipata dell’albero che considera solo i simboli terminali 8 Marco Maggini Tecnologie per l'elaborazione del linguaggio Parse tree e derivazioni • Un albero sintattico può essere visto come una rappresentazione di una serie di derivazioni α1⇒α2⇒…. ⇒αn dove α1= A ∈ N ▫ Per ciascuna stringa αi la derivazione che produce αi+1 corrisponde all’applicazione di una produzione e quindi all’espansione di un sottoalbero in corrispondenza di un simbolo non terminale in αi A A Xj → β tree αk-1 X1 X2 tree β=Y1Y2…Yr Xj Xk X1 produzione usata per derivare αk αk X2 Xj Y1 Y2 Xk Yr 9 Marco Maggini Tecnologie per l'elaborazione del linguaggio Parse tree - esempio • L’albero di analisi risultante mostra quali sono le produzioni * αn ma non l’ordine con cui applicate per ottenere la derivazione α1⇒ sono state applicate E ⇒ E*E ⇒ (E) * E ⇒ (E + E) * E ⇒ (id + E) * E ⇒ (id + E)* id ⇒ (id + id)*id E E E * E E E * E ( E ) E E * E ( E ) E + E E E id E * E ( E ) E + E E id E * E ( E ) id E + E E * E ( E ) id E + E id id 10 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche ambigue • Una grammatica è ambigua se è possibile costruire più di un albero sintattico per descrivere la derivazione di una stessa stringa appartenente al linguaggio generazione di ()()() B B→(B) B → B B B → ε B B B B B ( B ) ( B ) ( B ) ε ε B B B→(B)B B ( B ) B B ε ( B ) ( B ) ε grammatica ambigua ε ε B → ε ( B ) ε B ( B ) ε B ( B ) B ε ε grammatica non ambigua 11 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche ambigue – problemi • In genere è complesso provare se una grammatica è ambigua • L’ambiguità può portare problemi in alcuni casi quando l’albero sintattico è usato per interpretare sematicamente la stringa E → id E→(E) E → E + E E → E * E E → - E E ⇒ E + E ⇒ E + E * E ⇒ id + E * E ⇒ id + id * E ⇒ id + id * id id+id*id E ⇒ E * E ⇒ E + E * E ⇒ id + E * E ⇒ id + id * E ⇒ id + id * id 12 Marco Maggini Grammatiche ambigue – id OK espressioni 1 E E E Tecnologie per l'elaborazione del linguaggio E + E id * E E id id E E * + E id id KO • La grammatica non modella la precedenza degli operatori aritmetici ▫ Se si usasse l’albero a destra per valutare l’espressione il valore non sarebbe corretto (si effettua prima la somma) ▫ Il problema può essere risolto con una modellazione più precisa ovvero definendo una grammatica che considera gli operatori in modo diverso Si introducono più categorie sintattiche per raggruppare le singole parti dell’espressione nel modo corretto 13 Marco Maggini Grammatiche ambigue – Tecnologie per l'elaborazione del linguaggio espressioni 2 • Si introducono tre categorie sintattiche ▫ F – fattore: è un singolo operando o espressione fra () ▫ T – termine: è un prodotto/quoziente di fattori ▫ E – espressioni: è la somma/sottrazione di termini E→E+T|E–T|T T→T*F|T/F|F F → ( E ) | id • L’uso di produzioni del tipo E→ E + T impone che i termini siano raggruppati da sinistra a destra (es. 1+2+3 → (1+2)+3 ) 14 Marco Maggini Grammatiche ambigue - Tecnologie per l'elaborazione del linguaggio espressioni id+id*id derivazione con sostituzione a sinistra E ⇒ E + T ⇒ T + T ⇒ F + T ⇒ id + T ⇒ id + T * F ⇒ id + F * F ⇒ id + id * F ⇒ id + id * id E E E→E+T|E–T|T T→T*F|T/F|F F → ( E ) | id T + T T F F id id * F id OK 15 Marco Maggini Ambiguità – Tecnologie per l'elaborazione del linguaggio if… then… else <stmt> → if <expr> then <stmt> <stmt> → if <expr> then <stmt> else <stmt> <stmt> → “instruction” …. • La grammatica è ambigua perché “if E1 then if E2 then S1 else S2” ha due alberi di analisi validi <stmt> <stmt> if <expr> then <stmt> if <expr> then <stmt> E1 if <expr> then <stmt> else <stmt> E2 S1 S2 E1 else if <expr> then <stmt> E2 S1 <stmt> S2 16 Marco Maggini Ambiguità – Tecnologie per l'elaborazione del linguaggio “if.. then.. else” non ambiguo • L’ambiguità deriva dal fatto che la grammatica non permette una chiara associazione fra “else” e gli “if” presenti ▫ La regola normalmente usata è che l’else si riferisca all’if più vicino <stmt> <stmt> → <stmt_c> | <stmt_u> <stmt_c> → if <expr> then <stmt_c> else <stmt_c> <stmt_c> → “instruction” …. <stmt_u> if <expr> then <stmt> <stmt_u>→ if <expr> then <stmt> <stmt_u>→ if <expr> then <stmt_c> else <stmt_u> <stmt_u> E1 if <expr>then<stmt_c> else <stmt_u> fra then-else possono solo trovarsi espressioni if-then-else complete E2 S1 S2 17 Marco Maggini Tecnologie per l'elaborazione del linguaggio Produzioni equivalenti • Si possono riscrivere alcune produzioni in modo da ottenere una * grammatica equivalente (che genera lo stesso linguaggio) ma in cui le produzioni rispettano tutte alcune strutture ▫ Eliminazione della ricorsione a sinistra Una grammatica è ricorsiva a sinistra se esiste un simbolo non terminale + A per il quale esiste una derivazione A ⇒ Aα dove α (T ∪ N)* Produzione semplice A→Aα A → β A’ A → β A’ → α A’ | ε genera le stringhe A → β αn 18 Marco Maggini Tecnologie per l'elaborazione del linguaggio Ricorsione a sinistra • La ricorsione a sinistra nelle produzioni si può eliminare semplicemente anche nel caso più generale A → A α1 | Aα2 | … | Aαm A → β1 A’ | β2 A’ | …. | βn A’ A → β1 | β2 | …… | βn A’ → α1 A’ | α2 A’ | …. | αm A’ | ε • Per eliminare la ricorsione a sinistra che riguarda derivazioni a uno o più passi esiste comunque un algoritmo ▫ L’eliminazione della ricorsione a sinistra semplifica la realizzazione di parser che analizzano la stringa da sinistra a destra ▫ La ricorsione a destra realizza un’espansione delle stringhe verso destra 19 Marco Maggini Ricorsione a sinistra - Tecnologie per l'elaborazione del linguaggio esempio • Si elimina la ricorsione a sinistra per la grammatica delle espressioni aritmetiche E→E+T|T α = +T β = T E’ → + TE’ | ε T → T * F | F F → (E) | id E → TE’ α = *F β = F T → FT’ T’ → * FT’ | ε F → (E) | id 20 Marco Maggini Tecnologie per l'elaborazione del linguaggio Fattorizzazione a sinistra • La fattorizzazione a sinistra è una trasformazione delle produzioni che genera una grammatica equivalente A → α β1 | α β2 | … | α βn A → α A’ A’ → β1 | β2 | … | βn ▫ Nelle produzioni si assume che β1, β2, … ,βn non abbiano un prefisso in comune ▫ Nell’ottica di un’analisi della stringa da sinistra a destra la fattorizzazione permette una prima decisione con l’espansione di α rimandando la scelta di quale espandere fra β1, β2, … ,βn al passo successivo 21 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi discendente (top-down) • L’analisi discendente è un algoritmo che mira a costruire l’albero di analisi a partire dalla radice, creando i nodi in ordine anticipato • L’analisi discendente ricorsiva ▫ In genere necessità di backtracking – quando ci sono più opzioni nella scelta della produzione da applicare, si prova con la prima e si tentano quelle seguenti in caso di fallimento ▫ La grammatica definisce un insieme di funzioni mutuamente ricorsive ciascuna delle quali corrisponde ad una delle categorie sintattiche (simboli non terminali) ▫ La chiamata di una delle funzioni corrisponde all’applicazione di una produzione (espansione del simobolo non terminale) ▫ La funzione per il simbolo di start S legge una sequenza di ingresso e produce il puntatore all’albero di analisi generato (nullo se la stringa in ingresso non appartiene al linguaggio e si genera un errore di analisi) 22 Marco Maggini Analisi top-down - Tecnologie per l'elaborazione del linguaggio esempio S → cAd S() A → ab | a A() backtracking S() w = cad c cad A() S A d S() S c d A a cad b c cad A() S A d c S A a cad • Una grammatica ricorsiva a sinistra può generare un numero infinito di espansioni in un parser top-down ricorsivo (si espande all’infinito lo stesso simbolo) d 23 Marco Maggini Analisi top-down – Tecnologie per l'elaborazione del linguaggio procedura di analisi 1 • Si tiene traccia del prossimo simbolo della stringa di ingresso che deve essere generato nell’albero di analisi ▫ Si restringe il numero di produzioni che possono essere applicate per espandere un simbolo non terminale • Si espandono i nodi da sinistra a destra ▫ un simbolo terminale soddisfa l’obiettivo se coincide col prossimo simbolo della sequenza di ingresso ▫ un simbolo non terminale soddisfa l’obiettivo se lo soddisfa la chiamata alla funzione ricorsiva corrispondente Stringa in ingresso x1x2…..xn$ Cursore di ingresso (simbolo di previsione) 24 Marco Maggini Analisi top-down – Tecnologie per l'elaborazione del linguaggio procedura di analisi 2 • Quando si espande una produzione (si chiama la funzione ricorsiva corrispondente) e si genera un simbolo terminale si verifica se corrisponde a quello puntato dal cursore in ingresso ▫ Sì – si sposta il cursore in avanti e si procede nell’analisi ▫ No – si fallisce e si prova a soddisfare l’obiettivo precedente con una nuova ipotesi (backtracking) • Se l’espansione introduce un simbolo non terminale T si chiama la funzione ricorsiva corrispondente che crea il sottoalbero di analisi che ha T come radice 25 Marco Maggini Analisi top-down - Tecnologie per l'elaborazione del linguaggio esempio Node *B(int *lh, char *s) { Node *b1,*b2; cursore di ingresso if(s[*lh]==‘(‘) { stringa da analizzare (*lh)++; b1 = B(lh,s); if((s[*lh]==‘)‘)&&(b1!=NULL)){ (*lh)++; b2 = B(lh,s); B→(B)B|ε if(b2==NULL) { freeTree(b1); return(NULL); } else B return tree4(‘B‘,tree0(‘(‘),b1,tree0(‘)’),b2); } else { freeTree(b1); return NULL; b1 } } else return tree1(‘B’,tree0(‘e’)); } ) ( B ε b2 26 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analizzatori predittivi • Si elimina la ricorsione a sinistra • Si fattorizzano a sinistra le produzioni ▫ Per alcune grammatiche si può costruire un parser predittivo che non effettua il backtracking durante l’analisi ▫ Il simbolo terminale di previsione permette sempre di selezionare una sola produzione da applicare a simbolo in ingresso A non terminale da espandere A→α1|α2|….|αn produzioni per A 27 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi predittiva • Per riconoscere quale pruduzione si applica si possono usare diagrammi a stati che rappresentano le sequenze nel lato destro delle produzioni ▫ Le transizioni fra gli stati possono avvenire in corrispondenza di un simbolo terminale (si legge l’input) o non terminale (si chiama la procedura corrispondente) E → TE’ 0 T 1 E’ → + TE’ | ε 3 + 4 T → FT’ 7 F 8 T’ → * FT’ | ε 10 F → (E) | id 14 * ( 11 15 E’ T ε T’ F ε E id 2 5 9 12 16 E E’ T T’ ) 6 E’ 13 T’ 17 F 28 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi guidata da tabella • Si utilizza esplicitamente uno stack invece delle chiamate ricorsive a + b $ simboli della grammatica T∪N stringa da analizzare X albero sintattico Y Z $ stack Tabella di analisi Tabella bidimensionale M[A,s] non terminale terminale o $ 29 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi con tabella - funzionamento • Lo stack inzialmente contiene il simbolo S • Il controllo determina l’azione da eseguire in base al simbolo in testa allo stack (X) e al simbolo terminale in ingresso (a) ▫ Se X=a=$ il parser termina con successo ▫ Se X=a≠$ il parser estrae X dallo stack e avanza il cursore di ingresso di una posizione (match del simbolo di previsione) ▫ X è un simbolo non terminale – si controlla M[X,a] Se corrisponde a una produzione si fa il push nello stack degli elementi della stringa a destra X → UVW push(W); push(V); push(U) Altrimenti si genera un errore di analisi ▫ Se X≠a e X è un simbolo terminale, allora si genera un errore di analisi 30 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi con tabella - esempio E → TE’ stack E’ → + TE’ | ε $E $E’T $E’T’F $E’T’id $E’T’ $E’ $E’T+ $E’T $E’T’F $E’T’id $E’T’ $E’T’F* $E’T’F $E’T’id $E’T’ $E’ $ T → FT’ T’ → * FT’ | ε F → (E) | id id E E’ T T’ TE’ F id + * ( ) $ TE’ +TE’ ε FT’ ε FT’ ε *FT’ ε (E) ε input id+id*id$ id+id*id$ id+id*id$ id+id*id$ +id*id$ +id*id$ +id*id$ id*id$ id*id$ id*id$ *id$ *id$ id$ id$ $ $ $ output E → TE’ T → FT’ F → id T’ → ε E’ → +TE’ T → FT’ F → id T’ → *FT’ F → id T’ → ε E’ → ε 31 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi con tabella – costruzione 1 • Si considerano due funzioni ▫ FIRST(α) è l’insieme dei simboli terminali che inziano le stringhe generate a partire da α ∈ (T ∪ N)* ▫ FOLLOW(A) è l’insieme dei simboli terminali che possono apparire subito alla destra del simbolo A ∈ N in una stringa derivata da S (esiste una derivazione S⇒αAaβ con a ∈ T) • Calcolo di FIRST(x) x ∈ T ∪ N ▫ se x ∈ T allora FIRST(x) = {x} ▫ se x ∈ N ed esiste la produzione x→ε allora ε ∈ FIRST(x) ▫ se x ∈ N ed esiste la produzione x→Y1Y2…Yk a ∈ FIRST(x) se a ∈ FIRST(Yi) e ε ∈ FIRST(Yj) j=1,..,i-1 (Y1…Yi-1⇒ε) ε ∈ FIRST(x) se ε ∈ FIRST(Yj) j=1,..,k In pratica si aggiungono a FIRST(x) gli elementi di FIRST(Yi) fino a che non si incontra un Yi per il quale ε ∉ FIRST(Yi) 32 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi con tabella – costruzione 2 • Calcolo di FIRST(α) α ∈ (T ∪ N)* con α = Y1Y2…Yk ▫ F = FIRST(Y1) ▫ for(i=1; i<k && ε ∉ FIRST(Yi);i++) F = F ∪ FIRST(Yi+1) • Calcolo di FOLLOW(A) ▫ $ ∈ FOLLOW(S) ▫ Se esiste la produzione B→αAβ tutti i simboli in FIRST(β) eccetto ε sono in FOLLOW(A) ▫ Se esiste una produzione B→αA o B→αAβ e FIRST(β) contiene ε allora tutto ciò che è in FOLLOW(A) è anche in FOLLOW(B) 33 Marco Maggini Analisi con Tabella – E → TE’ E’ → + TE’ | ε T → FT’ Tecnologie per l'elaborazione del linguaggio esempio FIRST/FOLLOW FIRST(E) = {(,id} FIRST(T) = {(,id} FIRST(F) = {(,id} FIRST(E’) = {+,ε} FIRST(T’) = {*,ε} T’ → * FT’ | ε FOLLOW(E) = {),$} F → (E) | id FOLLOW(T) = {),+,$} FOLLOW(F) = {),*,+,$} FOLLOW(E’) = {),$} FOLLOW(T’) = {),+,$} 34 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi con tabella – costruzione • Gli elementi della tabella di analisi sono definiti da ▫ Se esiste la produzione A →α allora per ogni simbolo a in FIRST(α) M[A,a] = {A →α} Infatti se si è nello stato A e si legge a si dve espandere la produzione A in α perché garantisce che venga generato il simbolo a ▫ Se ε ∈ FIRST(α) allora M[A,b] = {A →α} per ogni simbolo b in FOLLOW(A) Tiene conto del fatto che se α⇒ε allora il simbolo a deve essere generato da qualche produzione in cui A compare seguito da un’altra espressione che può generare a 35 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi con tabella – grammatiche ambigue • Il procedimento per la costruzione della tabella d’analisi può portare a generare elementi della matrice M che contengono più alternative S → i E t S S’ | a S’ → eS | ε FIRST(S) = {i,a} FIRST(S’) = {e, ε} FIRST(E) = {b} E → b grammatica per if-then-else FOLLOW(S) = {e,$} FOLLOW(S’) = {e,$} FOLLOW(E) = {t} a S S’ E b e a i t $ iEtSS’ ε eS b ε 36 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche LL(1) • Una grammatica che non ha definizioni multiple nella tabella di analisi discendente si dice LL(1) ▫ Left-to-right nella scansione dell’ingresso ▫ Leftmost – si espande sempre il simbolo più a sinistra ▫ 1 si usa un simbolo di previsione • Non tutte le grammatiche CF sono LL(1) ▫ Si deve eliminare la ricorsione a sinistra ▫ Si deve fattorizzare a sinistra ▫ Non è comunque detto di ottenere una grammatica LL(1) 37 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi ascendente (bottom-up) • L’albero di analisi viene costruito a partire dalle foglie verso la radice ▫ Si riduce una stringa di ingresso al simbolo non terminale di scopo S ▫ Ad ogn passo una sottostringa che corrisponde al lato destro di una produzione è rimpiazzata col simbolo terminale a sinistra ▫ si genera il corrispondente nodo dell’albero di analisi collegando i nodi figli al padre abbcde sottostringhe a cui S → aABe si può applicare aAbcde A → Abc | b A → b aAde B → d B → d aABe S 38 Marco Maggini Analisi bottom-up – Tecnologie per l'elaborazione del linguaggio scelta delle riduzioni S → aABe A → Abc | b S ⇒ aABe ⇒ aAde ⇒ aAbcde ⇒ abbcde B → d • Nell’esempio le riduzioni sono sempre scelte tenendo conto di una derivazione della stringa in cui si sostituisce sempre il non terminale più a destra ▫ l’approccio bottom-up riduce da sinistra a destra • Come si sceglie la sottostringa da ridurre? ▫ Si può prendere la più a sinistra che fa il match con il lato destro di una produzione ▫ Non è detto che si riesca ad arrivare ad S (occorre il backtracking) 39 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analisi bottom-up –scelta delle riduzioni • Nell’esempio la scelta di ridurre al primo passo la sottostringa più a sinistra ha permesso di completare l’analisi ▫ Non è in genere detto che questo accada…. ▫ Se si sceglie in modo sbagliato si arriva ad un risultato intermedio in cui non esistono sottostringhe che fanno match con almeno un lato destro di una produzione S → aABe abbcde A → Abc | b B → d se al secondo passo si sceglie A → b aAAcde aAbcde aAAcBe non è più riducibile…. 40 Marco Maggini Tecnologie per l'elaborazione del linguaggio Sottostringhe handle • Una sottostringa handle ▫ corrisponde al lato destro di una produzione A→β ▫ si può rimpiazzare con A nella stringa di partenza γ ottenendo un passo * αAw ⇒ αβw (w contiene solo della derivazione destra di γ da S S rm ⇒ simboli terminali perché la derivazione è destra - right most) ▫ Se la grammatica è ambigua la stessa sottostringa può appartenere a più di un handle S A α β handle w ridurre β in A corrisponde ad eliminare tutti i figli di A dall’albero 41 Marco Maggini Tecnologie per l'elaborazione del linguaggio Riduzione • La procedura di riduzione mira a sostituire progressivamente sottostringhe handle con il corrispondente simbolo non terminale ▫ Si parte dalla stringa di soli simboli terminali da analizzare S = γ0 ⇒ γ ⇒ ….. ⇒ rm γ1⇒ rm γn-1⇒ rm γn = w rm 2rm ▫ Si sotituisce l’handle βn in γn utilizzando la produzione An→βn in modo tale che γn-1 = αn-1Awn-1 ▫ Si ripete il processo fino ad ottenere S • Questa procedura ha due problemi correlati da risolvere ▫ Come determinare la sottostringa da ridurre ▫ Come scegliere la produzione corretta per la riduzione 42 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analizzatore bottom-up con stack • Si utilizza uno stack per i risultati intermedi (frontiera dell’albero) ▫ il parser inserisce simboli nello stack a partire dalla stringa in ingresso w fino a che non viene individuato un handle β in cima allo stack ▫ Il parser riduce l’handle β al simbolo non terminale A corrispondente ▫ L’analisi si ferma con successo quando lo stack contiene solo il simbolo S e la stringa in ingresso è vuota • Le azioni che il parser può eseguire sono ▫ SPOSTA (shift) – il prossimo simbolo di w è inserito nello stack ▫ RIDUCI (reduce) – viene riconosciuta una stringa handle in testa allo stack e si rimpiazza col simbolo non terminale corrispondente ▫ ACCETTA – fine con successo dell’analisi ▫ ERRORE – l’analizzatore genera un errore di sintassi 43 Marco Maggini Analisi bottom-up - Tecnologie per l'elaborazione del linguaggio esempio E → id stack input output E→(E) $ $id $E $E+ $E+id $E+E $E+E* $E+E*id $E+E*E $E+E $E id+id*i$ +id*id$ +id*id$ id*id$ *id$ *id$ id$ $ $ $ $ SHIFT REDUCE E → id SHIFT SHIFT REDUCE E → id SHIFT (*) SHIFT REDUCE E → id REDUCE E → E*E REDUCE E → E+E ACCEPT E → E + E E → E * E id+id*id • La grammatica è ambigua ed esiste un’altra riduzione possibile ▫ Esiste un conflitto SHIFT/REDUCE in (*) che è stato risolto con SHIFT ▫ Si poteva anche applicare REDUCE E → E+E 44 Marco Maggini Tecnologie per l'elaborazione del linguaggio Analizzatori bottom-up - conflitti • Il parsing ascendente con stack senza backtracking non può essere applicato a tutte le grammatiche CF ▫ Noto il contenuto dello stack e il prossimo simbolo in ingresso C’è un conflitto SHIFT/REDUCE Non si riesce a decidere quale riduzione fare in un insieme di riduzioni possibili • Le azioni del parser ascendente con stack si possono determinare in modo univoco solo se la grammatica ha date proprietà ▫ Analizzatori con operatori a precedenza Corrispondono a grammatiche molto particolari (grammatiche ad operatori) in cui non esistono produzioni del tipo A→ε e per qualsiasi produzione A→β in β non esistono due simboli non terminali adiacenti (esiste sempre un “operatore” in mezzo). Es. espressioni aritmetiche. ▫ Analizzatori LR (Left-to-right Right-most-derivation) 45 Marco Maggini Analizzatori LR(k) Tecnologie per l'elaborazione del linguaggio 46 Marco Maggini Analizzatori LR(k) - Tecnologie per l'elaborazione del linguaggio struttura a ToS stato - simbolo a1 … … ai .. an $ Sm stringa da analizzare albero sintattico Xm Sm-1 Xm-1 Tabelle di analisi .. .. $ stack AZIONE GOTO AZIONE[S,a] GOTO[S,A] 47 Marco Maggini Analizzatori LR – Tecnologie per l'elaborazione del linguaggio schema di funzionamento 1 • Nello stack è memorizzata una stringa di coppie simbolo-stato S0X1S1X2S2….XmSm ▫ Ciascun stato riassume l’informazione contenuta nello stack al di sotto di esso ai fini del riconoscimento degli handle ▫ L’azione del parser è determinata dallo stato in testa allo stack e dal simbolo corrente (Tos,a) ▫ Nell’implementazione i simboli del linguaggio Xi∈ (T ∪ N) non sono strettamente necessari (lo stato già memorizza l’elaborazione della loro sequenza) ▫ La configurazione del parser è data dal contenuto dello stack e dalla parte di ingresso ancora da espandere S0X1S1X2S2….XmSmaiai+1…..an$ 48 Marco Maggini Analizzatori LR – Tecnologie per l'elaborazione del linguaggio schema di funzionamento 2 • La configurazione rappresenta una stringa derivata con sostituzione a destra già espanso X1X2….Xmaiai+1…..an$ da espandere ▫ Il controllo dell’analizzatore LR termina l’azione da compiere in base allo stato Sm in testa allo stack e al simbolo corrente ai 1. AZIONE[Sm,ai]=SHIFT S (PUSH ai, PUSH S) La nuova configurazione è a S0X1S1X2S2….XmSmaiS ai+1…..an$ ToS 49 Marco Maggini Analizzatori LR – Tecnologie per l'elaborazione del linguaggio schema di funzionamento 3 2. AZIONE[Sm,ai]=REDUCE A → β Si opera una riduzione che corrisponde alla nuova configurazione a S0X1S1X2S2….Xm-rSm-rAS ai…..an$ ToS dove S = GOTO[Sm-r,A] e r=|β| β=Xm-r+1….Xm 3. AZIONE[Sm,ai]=ACCEPT L’analisi è completata con successo 4. AZIONE[Sm,ai]=ERROR Si è rilevato un errore e si chiama la procedura di gestione degli errori 50 Marco Maggini Analisi LR – un esempio 1 AZIONE 1 E → E+T 2 E → T 3 T → T*F 4 T → F 5 F → (E) 6 F → id s# = shift #nuovo stato r# = reduce #produzione Tecnologie per l'elaborazione del linguaggio stato id 0 1 2 3 4 5 6 7 8 9 10 11 + * s5 ( GOTO ) $ s4 s6 r2 r4 s7 r4 s5 r2 r4 r6 s5 s5 r6 s7 r3 r5 s11 r1 r3 r5 F 1 2 3 8 2 3 9 3 10 r6 s4 s4 s6 r1 r3 r5 T Ac r2 r4 s4 r6 E r1 r3 r5 51 Marco Maggini Analisi LR – stack 0| 0|id|5| 0|F|3| 0|T|2| 0|T|2|*|7| 0|T|2|*|7|id|5| 0|T|2|*|7|F|10| 0|T|2| 0|E|1| 0|E|1|+|6| 0|E|1|+|6|id|5| 0|E|1|+|6|F|3| 0|E|1|+|6|T|9| 0|E|1| Tecnologie per l'elaborazione del linguaggio un esempio 2 input id*id+id$ *id+id$ *id+id$ *id+id$ id+id$ +id$ +id$ +id$ +id$ id$ $ $ $ $ AZIONE SHIFT 5 REDUCE 6 REDUCE 4 SHIFT 7 SHIFT 5 REDUCE 6 REDUCE 3 REDUCE 2 SHIFT 6 SHIFT 5 REDUCE 6 REDUCE 4 REDUCE 1 ACCEPT riduzione GOTO F→id T→F G[0,F]=3 G[0,T]=2 F→id T→T*F E→T G[7,F]=10 G[0,T]=2 G[0,E]=1 F→id T→F E→E+T G[6,F]=3 G[6,T]=9 G[0,E]=1 52 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche LR - definizione • Una grammatica LR è una grammatica per la quale si riescono a costruire le tabelle AZIONE e GOTO per un analizzatore LR ▫ Esistono grammatiche CF che non sono LR ▫ Una grammatica è LR se il parser SHIFT/REDUCE riesce a riconoscere le sottostringhe handle quando compaiono in testa allo stack (per fare questo basta lo stato) Per riconoscere un handle sullo stack si può usare un automa a stati finiti che analizza i simboli contenuti nello stack e dice quale è l’handle in testa Questo meccanismo è implementato nella tabella GOTO Lo stato nella testa dello stack è lo stato di questo automa riconoscitore dopo aver elaborato la stringa di simboli dalla base alla testa dello stack 53 Marco Maggini Grammatiche LR - Tecnologie per l'elaborazione del linguaggio proprietà • Le grammatiche LR(k) sono più generali di quelle LL(k) ▫ Le grammatiche LR(k) richiedono di riconoscere, con k simboli di previsione, il lato destro di una produzione (l’handle) A→ β dopo aver visto tutto quello che è derivato da β ▫ Le grammatiche LL(k) richiedono di riconoscere una produzione in base ai primi k simboli di ciò che si può derivare dalla sua parte destra • Come si costruiscono le tabelle di analisi? ▫ Si considera un caso semplice, quello delle Simple LR (SLR) che non copre tutte le grammatiche LR 54 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche SLR • Un elemento LR(0) di una grammatica G è una produzione annotata con un punto () che individua una posizione nel lato destro #i A → xyz A → xyz (i,0) A → xyz (i,1) A → xyz (i,2) A → xyz (i,3) ▫ Un elemento è individuato dalla coppia di indici (#produzione,posizione) ▫ Un elemento tiene traccia di quanta parte del lato destro di una produzione è stata vista fino ad un certo punto dell’analisi • La costruzione delle tabelle di analisi parte dalla costruzione di un automa a stati finiti che riconosce i prefissi associabili alle produzioni in una derivazione destra 55 Marco Maggini Grammatiche SLR – Tecnologie per l'elaborazione del linguaggio elementi • Gli elementi definiti a partire da ciascuna produzione possono essere visti come stati di un automa a stati finiti A→αxβ A→αBβ x ε A→αxβ B→γ si accetta l’input x per avanzare di un passo nel riconoscimento di αxβ soddisfare B significa applicare ogni sua espansione ▫ Si introduce un nuovo simbolo di partenza S’ e la produzione S’ →S (è la riduzione che determina l’accettazione della stringa) ▫ Si costruisce l’automa che riconosce i lati destri delle produzioni 56 Marco Maggini Grammatiche SLR – ε ε E’→E E→E+T ε ε 1 E’ → E 2-3 E → E+T| T E’→E T esempio 1 E→E+T + E→E+T F T→F ε ε 2 ε ε T T→T*F 7 id F→id ε ( F→(E) ε 6-7 F → (E) | id F 4 T→T*F F→(E) E ε F→(E) * T→T*F F→id ε T→T*F E→T 4-5 T → T*F | F E→E+T ε T→F ε 3 T ε 5 E→T E 1 E Tecnologie per l'elaborazione del linguaggio ) 6 F→(E) ε 57 Marco Maggini Grammatiche SLR – ε eliminazione delle ε-transizioni ε E’→E E→E+T ε ε E’→E T E→E+T + E→E+T ε F T→F ε ε ε ε T T→T*F 7 id F→id T→T*F F T→T*F F→id ε ε ( F→(E) ε F→(E) E ε F→(E) * 4 E→E+T 2 T→T*F 3 T ε T→F E→T I0={[E’→E],[E→T], [E→E+T],[T→F], [T→T*F],[F→id], [F→(E)]} esempio 2 ε 5 E→T E 1 E Tecnologie per l'elaborazione del linguaggio ) 6 F→(E) ε 58 Marco Maggini Grammatiche SLR – esempio 3 I0={[E’→E],[E→T], [E→E+T],[T→F], [T→T*F],[F→id], [F→(E)]} F E E→E+T I1 I0 T→F I3 I5 F→id I5={[F→id]} E’→E dagli stati in I1,I2, I3, I5 non ci sono ε-transizioni I3={[T→F]} id T I1={[E→E+T],[E’→E]} Tecnologie per l'elaborazione del linguaggio T→T*F E→T I2 I2={[E→T],[T→T*F]} ( I4 I4 va determinato con eliminazione di ε-transizioni 59 Marco Maggini Grammatiche SLR – ε eliminazione delle ε-transizioni per lo stato F→(E) ε E’→E E→E+T ε ε E’→E T E→E+T + E→E+T ε F T→F ε ε ε ε T T→T*F 7 id F→id T→T*F F T→T*F F→id ε ε ( F→(E) ε F→(E) E ε F→(E) * 4 E→E+T 2 T→T*F 3 T ε T→F E→T I4={[F→(E)],[E→E+T], [E→T],[T→F], [T→T*F],[F→id], [F→(E)]} esempio 4 ε 5 E→T E 1 E Tecnologie per l'elaborazione del linguaggio ) 6 F→(E) ε 60 Marco Maggini Grammatiche SLR – ε transizioni da I1={[E→E+T],[E’→E]} E’→E ε E→E+T ε ε E’→E T E→E+T + E→E+T ε F T→F ε ε ε ε T T→T*F 7 id F→id T→T*F F 4 T→T*F F→id ε ε ( F→(E) ε F→(E) E ε F→(E) * I6={ [E→E+T],[T→F], [T→T*F],[F→id], [F→(E)]} E→E+T 2 T→T*F 3 T ε T→F E→T La transizione con + definisce esempio 5 ε 5 E→T E 1 E Tecnologie per l'elaborazione del linguaggio ) 6 F→(E) ε 61 Marco Maggini Grammatiche SLR – transizioni da I6={ [E→E+T],[T→F], [T→T*F],[F→id], [F→(E)]} E’→E ε ε E→E+T E’→E I3 5 ε ε E→T E 1 E T ε E→E+T + E→E+T 3 ε T→F F T T→T*F T→T*F F 4 T→T*F 7 I5 ε id F→id F→id ε ε ( F→(E) ε F→(E) I4 E ε F→(E) * I9={ [E→E+T],[T→T*F]} E→E+T 2 T→T*F ε T ε ε E→T La transizione con T definisce esempio 6 T→F ε Tecnologie per l'elaborazione del linguaggio ) 6 F→(E) ε 62 Marco Maggini Grammatiche SLR – ε transizioni da I2={[E→T],[T→T*F]} E’→E ε E→E+T ε ε E’→E T E→E+T + E→E+T ε F T→F ε ε ε ε T T→T*F 7 id F→id T→T*F F 4 T→T*F F→id ε ε ( F→(E) ε F→(E) E ε F→(E) * I7={ [T→T*F],[F→id], [F→(E)]} E→E+T 2 T→T*F 3 T ε T→F E→T La transizione con * definisce esempio 7 ε 5 E→T E 1 E Tecnologie per l'elaborazione del linguaggio ) 6 F→(E) ε 63 Marco Maggini Grammatiche SLR – La transizione con E definisce ε I8={ [E→E+T],[F→(E)]} E’→E ε E→E+T ε ε E’→E I3 5 E→T E 1 E T ε E→E+T + E→E+T ε I2 E→E+T 2 ε T→F F T T→T*F * T→T*F F 4 T ε T→T*F 7 I5 ε id F→id ε T→T*F transizioni da I4={[F→(E)],[E→E+T], [E→T],[T→F], [T→T*F],[F→id], [F→(E)]} I8 ε E→T 3 esempio 8 T→F ε Tecnologie per l'elaborazione del linguaggio ( F→(E) ε F→id ε I4 F→(E) E ε I8 F→(E) ) 6 F→(E) ε 64 Marco Maggini Grammatiche SLR – ε transizioni da I7={ [T→T*F],[F→id], [F→(E)]} E’→E ε E→E+T ε ε E’→E T 3 E→E+T + E→E+T T E→E+T 2 ε T→F ε F T→F ε ε ε T T→T*F ε id F→id ε ( F→(E) ε T→T*F I10 4 F T→T*F F→id ε I4 F→(E) E ε F→(E) * I10={ [T→T*F]} 7 I5 T→T*F E→T La transizione con F definisce esempio 9 ε 5 E→T E 1 E Tecnologie per l'elaborazione del linguaggio ) 6 F→(E) ε 65 Marco Maggini Grammatiche SLR – ε transizioni da I8={ [E→E+T],[F→(E)]} E’→E ε E→E+T ε ε E’→E T E→E+T + E→E+T ε F T→F ε ε 3 T T→T*F 2 ε 7 id F→id T→T*F F 4 T→T*F F→id ε ε ( F→(E) ε F→(E) E ε F→(E) * I11={ [F→(E)]} E→E+T I6 T→T*F ε T ε T→F E→T La transizione con ) definisce esempio 10 ε 5 E→T E 1 E Tecnologie per l'elaborazione del linguaggio I11 6 ) F→(E) ε 66 Marco Maggini Grammatiche SRL – Tecnologie per l'elaborazione del linguaggio esempio 11 I0={[E’→E],[E→T],[E→E+T],[T→F],[T→T*F],[F→id],[F→(E)]} I1={[E→E+T],[E’→E]} I2={[E→T],[T→T*F]} I3={[T→F]} I4={[F→(E)],[E→E+T],[E→T],[T→F],[T→T*F],[F→id],[F→(E)]} I5={[F→id]} I6={ [E→E+T],[T→F],[T→T*F],[F→id],[F→(E)]} I7={ [T→T*F],[F→id],[F→(E)]} I8={ [E→E+T],[F→(E)]} I9={ [E→E+T],[T→T*F]} I10={ [T→T*F]} I11={ [F→(E)]} 67 Marco Maggini Grammatiche SRL – stato id * T F 4 1 2 3 5 4 8 2 3 6 5 4 9 3 7 5 4 5 1 ( ) esempio 12 E 0 + Tecnologie per l'elaborazione del linguaggio 6 2 7 3 4 5 8 9 10 11 6 10 11 7 Tabella di transizione di stato 68 Marco Maggini Tabelle di analisi – Tecnologie per l'elaborazione del linguaggio costruzione AZIONE • A partire dall’automa deterministico che riconosce i prefissi dei lati destri delle produzioni è possibile ottenere le tabelle di analisi AZIONE e GOTO ▫ Gli stati dell’automa C={I0,I1,…,In} corrispondono agli stati 0,1,..n usati dall’analizzatore • Le azioni per lo stato i sono definite come segue ▫ Se [A→αaγ] ∈ Ii e nell’automa esiste la transizione Ii→Ij per ingresso a∈T allora AZIONE[i,a]=SHIFT j (si passa nel nuovo stato dell’automa e non è ancora completato il match con il lato destro di una produzione) ▫ Se [A→α] ∈ Ii allora AZIONE[i,a]=REDUCE A→α per ogni simbolo a in FOLLOW(A) ▫ Se [S’→S] ∈ Ii allora AZIONE[i,$]=ACCEPT 69 Marco Maggini Tabelle di analisi – Tecnologie per l'elaborazione del linguaggio costruzione GOTO • La tabella GOTO è costruita considerando per ogni stato le transizioni per simboli non terminali ▫ Se esiste la transizione Ii→Ij per ingresso A∈N allora GOTO[i,A]=j • Lo stato iniziale è quello contenente [S’→S] • Le componenti non definite corrispondono ad errori • Le tabelle costruite con questo algoritmo si dicono SLR(1) ▫ Una grammatica che ammette un parser SLR(1) si dice SLR(1) 70 Marco Maggini Tabelle di analisi I0={[E’→E], [E→T], [E→E+T], [T→F], [T→T*F], [F→id], [F→(E)]} I1 E I0 I2 T F ( id I3 Tecnologie per l'elaborazione del linguaggio esempio I1={[E→E+T],[E’→E]} + I1 I6 AZIONE[0,id] = SHIFT 4 AZIONE[1,$] = ACCEPT AZIONE[0,(] = SHIFT 5 AZIONE[1,+] = SHIFT 6 GOTO[0,E] = 1 GOTO[0,T] = 2 GOTO[0,F] = 3 I2={[E→T],[T→T*F]} I2 * I7 FOLLOW(E)={$,+,)} AZIONE[2,*] = SHIFT 7 AZIONE[2,$] = REDUCE E→T I4 AZIONE[2,)] = REDUCE E→T I5 AZIONE[2,+] = REDUCE E→T 71 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche non SLR(1) • La costruzione delle tabelle per un analizzatore SLR(1) fallisce quando c’è un conflitto nella definizione di una componente • Per linguaggi più generali di SLR(1) si possono costruire ▫ Tabelle canoniche LR ▫ Tabelle LALR (LookAhead LR) • I problemi nascono quando ci sono più riduzioni possibili e si deve evitare di applicare riduzioni errate che richiederebbero il backtracking ▫ Una soluzione è usare uno stato più informativo che memorizza esplicitamente i simboli che possono seguire un handle α per cui è ammessa la riduzione A → α 72 Marco Maggini Tecnologie per l'elaborazione del linguaggio Grammatiche LR • Si definiscono gli elementi LR(1) come coppie [A → αβ,a] ▫ L’elemento di lookahead a ha effetto solo per gli elementi della forma [A → α,a] dove si prevede la riduzione solo se il prossimo simbolo è a ▫ Infatti non è detto che la riduzione sia applicabile per tutti gli elementi in FOLLOW(A) come ipotizzato nella costruzione delle tabelle SLR • Un analizzatore canonico LR ha molti più stati degli analizzatori SLR e LALR • I generatori automatici di parser sintattici generano analizzatori LALR