La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

i linguaggi di programmazione sono un caso particolare dei

Presentazioni simili


Presentazione sul tema: "i linguaggi di programmazione sono un caso particolare dei"— Transcript della presentazione:

1 i linguaggi di programmazione sono un caso particolare dei
SISTEMI FORMALI GENERATIVI: LINGUAGGI A STRUTTURA DI FRASE i linguaggi di programmazione sono un caso particolare dei linguaggi a struttura di frase, che sono un caso particolare dei sistemi formali generativi.

2 linguaggi a struttura di frase
un particolare caso di sistema formale generativo sono le grammatiche a struttura di frase di questo tipo sono le gramatiche dei linguaggi di programmazione Una grammatica generativa a struttura di frase produce un linguaggio a struttura di frase in tali linguaggi una stringa ben formata si dice frase cioe’ le stringhe del linguaggio generato dalla grammatica [appunto stringhe ben formate] sono "frasi" del linguaggio

3 linguaggi a struttura di frase
una grammatica generativa a struttura di frase e' una quadrupla ( V, T, P, S ) dove: V = Vocabolario = insieme di tutti i simboli usati, terminali e non T = insieme dei simboli Terminali (ovvero quelli che possono apparire in una s.b.f. o frase del linguaggio, P = insieme di Produzioni S = Simbolo iniziale (un solo simbolo di base, S = Sentence)

4 linguaggi a struttura di frase
una grammatica generativa a struttura di frase e' una quadrupla ( V, T, P, S ) dove: V = Vocabolario = insieme di tutti i simboli usati, terminali e non T = insieme dei simboli Terminali (ovvero quelli che possono apparire in una s.b.f. o frase del linguaggio, P = insieme di Produzioni S = Simbolo iniziale (un solo simbolo di base, S = Sentence) ... confronta con i sistemi formali generativi, dove il linguaggio su A (alfab.) viene definito come sottoinsieme di A* da una terna ( Alfabeto, Base, Produzioni ); ora la base si riduce a un solo simbolo, e si introducono due categorie di simboli dell' alfabeto, Terminali e NonTerminali

5 linguaggi a struttura di frase // un’osservazione:
Grammatica G = ( V, T, P, S ) con : S = Simbolo iniziale, V = vocabolario = insieme simboli usati, terminali e non, T = insieme simboli Terminali (che possono apparire in una s.b.f. o frase del linguaggio), P = insieme delle Produzioni nota: il vocabolario V = T ∪ N = unione dei due insiemi, con T = insieme dei simboli Terminali e N = ins. dei simb. Non terminali -> N = V-T = insieme simboli Non terminali = "categorie sintattiche" = simboli usati nella grammmatica, ma che non appaiono in una frase del linguaggio (nb: almeno un simbolo NON terminale c’e’ sempre nella parte sinistra delle produzioni) T = simboli terminali, che NON possono apparire mai da soli a sinistra in una produzione

6 linguaggi a struttura di frase, primo esempio
es. di grammatica a struttura di frase: G1 = (V,T,P,S) con: V = { cane, gatto, morde, guarda, N, V } T = { cane, gatto, morde, guarda} [terminali] P = { p1,p2,p3,p4,p5 } [produzioni] p1: S-> N V N p2: V-> morde p3: V-> guarda p4: N-> cane p5: N-> gatto es. derivazione: S ->1 N V N ->2 N morde N ->4 cane morde N ->4 cane morde cane ovvero: S cane morde cane NOTA che "cane morde N" non e’una frase del linguaggio, infatti qui c’e’ N (simbolo non terminale) che per definizione NON puo' stare in una stringa del linguaggio di G1 esercizio: costruire altre frasi ... *

7 linguaggi a struttura di frase, primo esempio
esercizio: cosa ancora produce la grammatica vista G1 = (V,T,P,S), con: T = { cane, gatto, morde, guarda} V = { cane, gatto, morde, guarda, N, V } con le produzioni: P = { p1,p2,p3,p4,p5 } con p1: S-> N V N; p2: V-> morde; p3: V-> guarda; p4: N-> cane; p5: N-> gatto; con la produzione S ->1 N V N e poi scelgo altre produzioni (invece di 2,4,4 che da'cane morde cane) e ho: a= cane morde cane, b= cane guarda cane, c= cane morde gatto, d= cane guarda gatto, e= gatto morde cane, f = gatto guarda cane, g= gatto morde gatto, h=gatto guarda gatto. G1 produce un linguaggio finito di otto frasi: L1 = { a,b,c,d,e,f,g,h }

8 linguaggi a struttura di frase, secondo esempio
alla grammatica G1 = (V,T,P,S) con: V = { cane, gatto, morde, guarda, N, V } T={ cane, gatto, morde, guarda } P = { p1, p2, p3, p4, p5 } con p1: S -> N V N; p2: V -> morde; p3: V -> guarda; p4: N -> cane; p5: N -> gatto; aggiungo una produzione e un simbolo terminale "che" p6: s V N -> s V N che V N; (s sta per stringa qualsiasi) e ho: G2 = (V,T,P,S) con: V ={cane,gatto,morde,guarda, che, N, V} vocabolario T ={ cane,gatto,morde,guarda, che } simboli terminali P ={ p1,p2,p3,p4,p5,p6 } con: p1: S-> N V N; p2: V-> morde; p3: V-> guarda; p4: N-> cane; p5: N-> gatto; p6: s V N -> s V N che V N ;

9 linguaggi a struttura di frase, secondo esempio
G2: V = { cane,gatto,morde,guarda, che, N, V } T = { cane,gatto,morde,guarda, che } P = { p1,p2,p3,p4,p5,p6 } con: p1: S-> NVN; p2: V-> morde; p3: V-> guarda; p4: N-> cane; p5: N-> gatto; p6: s V N -> s V N che V N; genera frasi del tipo: S ->1 N V N ->6 N V N che V N ->6 N V N che N V che N V -> ... e, dopo aver sostituito ai N un po’ di cane o gatto e ai V un po’ di "guarda" o "morde" otteniamo la frase: gatto guarda cane che guarda gatto che morde gatto posso ottenere altre frasi -> ho un linguaggio INFINITO

10 linguaggi a struttura di frase, definizione di linguaggio
Data una grammatica a strutt. di frase, quadrupla G = (V,T,P,S) con: [ S = simbolo di partenza] V = { a, b, y, P, Q, ... } = [ vocabolario simboli di G] T = { a, b, y } = [ simboli terminali di G] P = { p1,p2... } = [ produzioni] definizione: un linguaggio a struttura di frase generato da G e' l’insieme delle stringhe sull’ alfabeto T (simboli terminali) che possono essere derivate (direttamente o indirettamente) da S: L (G) = { w | w ∈ T *, S w } * G

11 linguaggi a struttura di frase, terzo esempio
terzo esempio, G3 = (V,T,S,P), con V= { A, S, a,b }, T = { a, b }, ( non terminale A ) P= { p1: S -> aAb; p2: aA -> aaAb; p3: A -> λ } esempi di derivazioni: S ->1 aAb ->3 ab oppure: S ->1 aAb ->2 aaAbb ->3 aabb S ->1 aAb ->2 aaAbb ->2 aaaAbbb ->3 aaabbb e quindi L(G3) = { ab, aabb, aaabbb, ... } = { akbk | k>=1 }

12 linguaggi a struttura di frase, terzo esempio
continua esempio G3 =(V,T,S,P), con V={A,S,a,b}, T={a,b}, e P = { p1: S -> a A b; p2: aA -> aaAb; p3: A -> λ } es ( ricorda: si parte sempre da S !! ) S ->1 aAb ->2 aaAbb ->2 aaaAbbb ->3 aaabbb; quindi: L(G1) = { ab, aabb, aaabbb, ... } = { akbk | k>=1 } si noti che la produzione p2: aA -> aaAb e’ "ricorsiva", ^ ^2 nel senso che A appare nella parte a sinistra della " -> ", ^1, [parte definita dalla produzione] e appare anche a destra, ^2, [parte definente nella produzione] -->> e quindi A e’ in parte definita in termini di se’ stessa ... ovvero la produzione e' ricorsiva !

13 linguaggi a struttura di frase, esempio G4 errata
nota: attenzione alla ricorsione: sappiamo dalla programmazione in C++ che un sottoprogramma ricorsivo DEVE avere un test di fine ricorsione; nel caso delle grammatiche, devo avere una produzione alternativa alla ricorsione che mi consente di fermare la ricorsione: infatti, nella G3 =(V,T,S,P), con V={A,S,a,b}, e con T={a,b} e P = { p1: S -> a A b; p2: aA -> aaAb; p3: A -> λ } vi sono due produzioni con A a sinistra: p2: aA -> aaAb; che e' ricorsiva p3: A -> λ che permette di fermare la ricorsione

14 linguaggi a struttura di frase, esempio G4 errata
nota: attenzione alla ricorsione: sappiamo dalla programmazione in C++ che un sottoprogramma ricorsivo DEVE avere un test di fine ricorsione; nel caso delle grammatiche, devo avere una produzione alternativa alla ricorsione che mi consente di fermare la ricorsione: la grammatica seguente e' errata: G4 = ( V, T, S, P ), con: V = { x, y, w, z, A, B, S } T = { x, y, w, z } e P = { S -> AB; A -> xA; A -> yA; B -> zB; B -> wB } vi sono 4 produzioni ricorsive, ma ... mancano (almeno) due produzioni per fermare la ricorsione

15 linguaggi a struttura di frase, G4 corretta
nella grammatica errata G4 = ( V,T,S, P ), con: V = { x, y, w, z, A, B, S } T = { x, y, w, z } e P = { p1: S -> AB; p2: A -> xA; p3: A -> yA; p4: B -> zB; p5: B -> wB } mancano (almeno) due produzioni per arrestare la ricorsione per poter eliminare i simboli non terminali A e B; aggiungo ad es. p6: A -> x; p7: B -> z; esempi: S ->1 AB ->6 xB ->7 xz S ->1 AB ->3 yAB ->5 yAwB ->6 yxwB ->7 yxwz S ->1 AB ->2 xAB ->2 xxAB ->2 xxxAB ->2 xxxxAB ->6 xxxxxB ->4 xxxxxzB ->7 xxxxxzz S ->1 AB ->5 AwB ->5 AwwB ->6 xwwB ->7 xwwz

16 linguaggi a struttura di frase, grammatica errata
ancora: G5 = ( V, T, S, P ), con: V = { x, y, z, A, C, S }, T = {x,y,z}, P = { p1: S -> AA; p2: A -> xA; p3: A -> C; p4: C -> yC; p5: C -> S; } problemi in questa definizione? vediamo un es. di derivazione : S ->1 AA ->2 xAA ->2 xxAA ->3 xxCA ->2 xxCxA ->4 xxyCxxA ->3 xxyCxxC ->5 xxySxxC ->5 xxySxxS ...

17 linguaggi a struttura di frase, grammatica errata
ancora: G5 = ( V, T, S, P ), V = { x, y, z, A, C, S }, T = {x,y,z}, P = { p1: S -> AA; p2: A -> xA; p3: A -> C; p4: C -> yC; p5: C -> S; } qui si ha una definizione circolare (ricorsione circolare) : S e’ definita in termini di A, A e’ definita in termini di C, C e’ definita in termini di S! -> mancano produzioni per eliminare i simboli non terminali dalle stringhe; dobbiamo ad es. aggiungere qualche produzione del tipo: C -> z; oppure A -> w o altra produzione simile

18 linguaggi a struttura di frase, esercizio 3
cosa produce G6 = (V,T,S,P) = G6 = ( { A, a }, { a }, A, { A -> a A A -> a } ) ovvero: V = { A, a }, T = { a }, S = A, P = { p1: A -> a A; p2: A -> a; } (segue soluzione)

19 linguaggi a struttura di frase
soluzione esercizio: la grammatica (V,T,S,P) : G6 = ( { A, a }, { a }, A, { A -> a A A -> a } ) ovvero: V = { A, a }, T = { a }, S = A, P = { p1: A -> a A; p2: A -> a; } produce ad es: A ->2 a; A ->1 aA ->2 aa; A ->1 aA ->1 aaA ->2 aaa; A ->1 aA ->1 aaA ->1 aaaA ->2 aaaa; ecc... cioe’ il linguaggio prodotto dalla G6 e' L(G6) = { ai | i> 0 }

20 linguaggi a struttura di frase
esercizio: cosa produce la grammatica G7 = ( V, T, S, P ) con V = { S, A, B, a, b }, T = { a, b }, P = { p1: S -> AB; p2: A-> aA; p3: A -> a; p4: B -> Bb; p5: B -> b } ?

21 linguaggi a struttura di frase
soluzione esercizio: la grammatica G7 = ( V, T, S, P ) con V = { S,A,B, a,b }, T = { a,b }, P = { p1: S -> AB; p2: A-> aA; p3: A -> a; p4: B -> Bb; p5: B -> b } produce ad es: S ->1 AB ->3 aB ->5 ab; S ->1 AB ->2 aAB ->3 aaB ->5 aab; S ->1 AB ->2 aAB ->4 aABb ->4 aAbb ->3 aabb; S ->1 AB ->2 aAB ->4 aABb ->2 aaABb ->2 aaaABb ->3 aaaaBb ->4 aaaabb; ecc, quindi L(G7) = { aibk | i>0, k>0 }

22 linguaggi a struttura di frase
ripetiamo: grammatiche a struttura di frase, composte da: G = (V,T,P,S) con: [ S = simbolo di partenza] V = { a, b, y, P, Q, ... } = [ vocabolario simboli di G] T = { a, b, y } = [ simboli terminali di G] P = { p1,p2... } = [ produzioni] definizione: un linguaggio a struttura di frase generato da G e' l’insieme delle stringhe sull’ alfabeto T (simboli terminali) che possono essere derivate (direttamente o indirettamente) da S: L (G) = { w | w ∈ T *, S w } * G

23 vediamo ora una rappresentazione grafica della catena di derivazioni che porta dal simbolo iniziale S ad una stringa di simboli terminali cioe' ad una frase del linguaggio e che visualizza graficamente la struttura della frase; la rappresentazione grafica della catena di derivazione si dice albero sintattico

24 struttura di una frase e albero sintattico
riprendiamo G1 = (V,T,P,S), V={cane, gatto, morde, guarda, N,V} T = {cane, gatto, morde, guarda} cinque produzioni: p1: S-> N V N p2: V-> morde p3: V-> guarda p4: N-> cane p5: N-> gatto la frase "gatto morde cane" e' del L(G1), e si deriva con la catena di derivazioni: S ->1 N V N ->2 N morde N ->5 gatto morde N ->4 gatto morde cane

25 struttura di una frase e albero sintattico
riprendiamo G1 = (V,T,P,S), V={cane, gatto, morde, guarda, N,V} T = {cane, gatto, morde, guarda} cinque produzioni: p1: S-> N V N p2: V-> morde p3: V-> guarda p4: N-> cane p5: N-> gatto la frase "gatto morde cane" e' del L(G1), e si deriva con la catena di derivazioni: S ->1 N V N ->2 N morde N ->5 gatto morde N ->4 gatto morde cane la catena di derivazioni si rappresenta con l' albero sintattico a destra: S 1 NVN 5 4 2 gatto cane morde ALBERO SINTATTICO

26 struttura di una frase e albero sintattico
una catena di produzioni che porta dal S ad una stringa del linguaggio puo’ essere data in forma grafica: interpretaz. grafica della derivazione: S ->1 AB ->2 aAB ->4 aaB ->5 aab G7 = ( V, T, S, P ), V = { S, A, B, a, b } T = { a, b } P = { p1: S -> AB; p2: A -> aA; p3: B -> Bb; p4: A -> a; p5: B -> b; } stringa aab del ling. L(G7) ha la struttura a destra 1 2 4 5 S A B b a radice nodi intermedi foglie o nodi terminali ALBERO SINTATTICO:

27 struttura di una frase e albero sintattico
5 b struttura di una frase e albero sintattico G7 = ( V, T, S, P ), V = { S, A, B, a, b } T = { a, b } P = { p1: S -> AB; p2: A -> aA; p3: B -> Bb; p4: A -> a; p5: B -> b; } una derivazione: S ->1 AB ->2 aAB ->2 aaAB ->4 aaaB ->3 aaaBb ->3 aaaBbb ->5 aaabbb 1 2 S A B b a radice dell'albero nodi intermedi dell'albero 4 5 3 foglie o nodi terminali = stringa con albero a destra

28 linguaggio delle espressioni aritmetiche senza parentesi
G8: grammatica (semplificata) delle espressioni aritmetiche senza parentesi, del tipo a+b*b+a; i simboli terminali sono a,b, +,* non c'e' priorita' tra i due operatori + e *; G8: T= {a,b,+,*}, V= {a,b,+,*, S}, P = {p1,p2,p3,p4} p1: S-> S+S p2: S-> S*S p3: S-> a p4: S-> b le produzioni p1 e p2 sono "ricorsive", ad esempio in p1: S -> S + S; qui il simbolo S = a sinistra della " -> " [che e’ la parte definita dalla produzione] (*) appare anche a destra [parte definente nella produzione] (*) nb: s -> q leggi: " s produce q", leggi anche: "la stringa q deriva da s", e anche: " s e’ definita come q"

29 linguaggio delle espressioni aritmetiche
continua G8 = ( V,T,P,S ) = grammatica "espressioni aritmetiche" T= {a,b,+,*}, V= {a,b,+,*, S}, P = {p1,p2,p3,p4} p1: S->S+S p2: S->S*S p3: S->a p4: S->b (nota le produzioni p1 e p2 di tipo ricorsivo) es. di catene di produzioni: 1) S ->3 a; [ "a" e’ una frase] 2) S ->1 S+S ->4 S+b ->4 b+b; 3) S ->2 S * S ->3 a * S ->4 a * b; 4) S ->2 S*S ->1 S*S+S ->3 a*S+S ->3 a*a+S ->3 a*a+a; S a*a+a posso concludere che a*a+a e'una frase ben form. * G

30 linguaggio delle espressioni aritmetiche
ancora per G8=(T,V,S,P) con T={a,b,+,*}, V= {a,b,+,*, S}, P={p1,p2,p3,p4} con p1: S->S+S p2: S->S*S p3: S->a p4: S->b ancora due esempi di derivazione: 5) S ->2 S * S ->2 S * S * S ->1 S * S + S * S ->3 a * S + S * S ->4 a * b + S * S ->3 a * b + a * S ->4 a * b + a * b; 6) S ->1 S+S ->2 S+S*S ->1 S+S*S+S ->3 a+S*S+S ->4 a+b*S+S ->1 a+b*S+S+S ->4 a+b*b+S+S ->5 a+b*b+a+S -> a+b*b+a+b ... G8 genera delle stringhe che possono essere interpretate come semplici espressioni aritmetiche senza parentesi.

31 linguaggio delle espressioni aritmetiche: albero sintattico
la catena di derivazione implica la struttura della frase. G8=(T,V,S,P), T={a,b,+,*}, V={a,b,+,*, S}, P={p1,p2,p3,p4} p1: S->S+S p2: S->S*S p3: S->a p4: S->b un esempio: derivazione (struttura) della stringa a*a : S ->2 S*S ->3 a*S ->3 a*a la struttura dell' albero viene dalle derivazioni ! S 2 2 S S 2 3 3 a * a

32 linguaggio delle espressioni aritmetiche: albero sintattico
la catena di derivazione implica la struttura di una frase. G8=(T,V,S,P), T={a,b,+,*}, V={a,b,+,*, S}, P={p1,p2,p3,p4} p1: S->S+S p2: S->S*S p3: S->a p4: S->b S ->2 S*S ->1 S*S+S ->3 a*S+S ->3 a*a+S ->3 a*a+a; a destra l’albero sintattico corrispond. (radice S in alto, nodi intermedi S, foglie=nodi terminali in basso [a,*,a,+,a]; sui rami sono segnate le produzioni) Quindi a*a+a,frase del linguag-gio I8 generato dalla grammat. G8, si interpreta come: a * (a+a) e non: (a*a)+a ! S a * + 2 1 3

33 espressioni aritmetiche – grammatiche ambigue
attenzione pero’ la grammatica G8 G8 = (T,V,S,P) con T= {a,b,+,*}, V= {a,b,+,*, S}, P = {p1,p2,p3,p4} p1: S->S+S p2: S->S*S p3: S->a p4: S->b se una G permette di ottenere la stessa stringa con catene di produzioni diverse, diremo che e’ una grammatica ambigua, in G8 la frase a*a+a puo’ essere ottenuta con derivazioni diverse, e puo’ essere interpretata con strutture diverse: S ->2 S*S ->1 S*S+S -> a*S+S -> a*a+S -> a*a+a che si interpreta come: a* (a+a) - come visto prima, S ->1 S+S ->2 S*S+S -> a*S+S -> a*a+S -> a*a+a che si interpreta invece come (a*a)+a !

34 espressioni aritmetiche – grammatiche ambigue
attenzione pero’ la grammatica G8 G8 = (T,V,S,P) con T= {a,b,+,*}, V= {a,b,+,*, S}, P = {p1,p2,p3,p4} p1: S->S+S p2: S->S*S p3: S->a p4: S->b se una G permette di ottenere la stessa stringa con catene di produzioni diverse, diremo che e’ una grammatica ambigua, quindi se la stessa stringa puo’ avere due strutture diverse, allora, interpretando la struttura (nel senso di sequenza di operazioni aritmetiche dove associo alle variabili dei valori numerici) ottengo due valori diversi! per un linguaggio di programmazione (che deve essere non ambiguo) la grammatica G8 non va bene; ma: nota che una grammatica ambigua non implica che il linguaggio sia ambiguo -

35 linguaggi a struttura di frase
come in un dato linguaggio generato da una data grammatica posso produrre la stessa stringa (frase) con due catene di derivazioni diverse, cosi' anche un linguaggio puo' esser prodotto da piu' grammatiche - def.: se un linguaggio non ha alcuna grammatica che lo generi di tipo non ambiguo (cioe’ tutte le grammatiche che lo generano sono ambigue) allora diremo che e’ un linguaggio ambiguo. abbiamo visto che G5 = (T,V,S,P), T= {a,b,+,*}, V= {a,b,+,*, S}, P = { p1: S->S+S p2: S->S*S p3: S->a p4: S->b } e’ una grammatica ambigua - ma: vediamo ora che lo stesso linguaggio puo’ essere descritto con una grammatica NON ambigua

36 linguaggi a struttura di frase
la grammatica seguente G9 produce lo stesso linguaggio della G8 ma non e' ambigua. Qui sono introdotti due nuovi simboli non terminali (due categorie sintattiche), T e F (termine e fattore): G9 = ( V,T,P,S ), con T = {a,b,+,*}, V = {a,b,+,*, S,T,F}, e P = { p1: S->S+T p2: S->T ("termine"= significato...) p3: T->T*F p4: T->F ( "fattore"=significato... ) p5: F->a p6: F->b ( a,b sono "variabili"... ) } es.: S ->1 S+T ->2 T+T ->3 T*F+T ->4 F*F+T ->4 F*F+F ->3 a*F+F -> a*a+F -> a*a+a a*a+a ha la struttura sintattica: ( termine + termine ) dove il 1.o termine e’ dato da (fattore * fattore)

37 G9 = ( V,T,P,S ), con T = {a,b,+,*}, V = {a,b,+,*, S,T,F},
foglie nodi non terminali radice G9 = ( V,T,P,S ), con T = {a,b,+,*}, V = {a,b,+,*, S,T,F}, P = {p1: S->S+T p2: S->T p3: T->T*F p4: T->F p5: F->a p6: F->b } S ->1 S+T ->2 T+T ->3 T*F+T ->4 F*F+T ->4 F*F+F ->3 a*F+F ->5 a*a+F ->5 a*a+a che ha quindi la struttura ( a*a ) + a come evidenziato dall’ albero sintattico a destra:

38 V={ a,b,c,-,A,S };T={ a,b,c,-}; P = { S -> A; S -> S - A;
es.: due gramm. diverse per lo stesso linguaggio, es. di precedenza di valutazione da destra a sinistra o viceversa: G10 = ( V,T,S, P ) con V={ a,b,c,-,A,S };T={ a,b,c,-}; P = { S -> A; S -> S - A; A -> a; A-> b; A -> c } > interpretazione da sinistra a destra G11 = ( V,T,S, P ) con P = { S -> A; S -> A - S; interpretazione da destra a sinist. S A - c b a a-b-c = (a-b)-c da sinistra a destra S A - a b c a-b-c = a-(b-c) da destra a sinistra

39 linguaggi a struttura di frase
abbiamo cosi’ visto alcuni concetti relativi alle grammatiche a struttura di frase : * def. di grammatica * def. di derivazione diretta / catena di derivazione * albero sintattico / struttura della frase * ambiguita’ : grammatica ambigua, linguaggio ambiguo

40 grammatiche generative -
analisi sintattica: accettazione di una stringa ben formata e riconoscimento della struttura di una stringa

41 premessa – traduzione premessa: alcune osservazioni introduttive sui linguaggi di programmazione

42 premessa – traduzione un calcolatore esegue delle procedure di elaborazione dati descritte con dei programmi (software) – piu' precisamente, le istruzioni del programma, memorizzate nella memoria centrale, sono eseguite una dopo l'altra dall' unita' centrale. un programma e' la descrizione completa di tutta la sequenza delle azioni da fare per ottenere qualcosa da un calcolatore, compresa la descrizione completa dei dati elaborati (iniziali, intermedi, finali) ovvero * e' una lista di istruzioni (descrive azioni e dati) da far eseguire ad un calcolatore per ottenere un risultato,

43 premessa – traduzione un calcolatore esegue delle procedure di elaborazione dati descritte con dei programmi (software) – piu' precisamente, le istruzioni del programma, memorizzate nella memoria centrale, sono eseguite una dopo l'altra dall' unita' centrale. il programma (la lista istruzioni) deve essere scritto * in un linguaggio comprensibile direttamente al calcolatore (linguaggio della macchina, o Linguaggio Macchina LM) * oppure puo' essere scritto in un linguaggio intermedio, comprensibile ad una persona e ad una macchina, ma in tale caso deve prima essere tradotto per poter dopo essere eseguito.

44 premessa – traduzione Abbiamo detto che un programma e' la descrizione di tutta la sequenza delle azioni da fare per ottenere qualcosa dal calcolatore, compresa la descrizione completa dei dati (iniziali, intermedi, finali) - il programma deve essere scritto in un linguaggio comprensibile al calcolatore .... ma: ogni calcolatore ha un suo linguaggio macchina LM: esistono tanti LM quanti sono i diversi calcolatori (vedremo questo in seguito) qui un elenco brevissimo : EDSAC ('48), IBM7090('57), IBM/360('65), Digital PDP11('71), Zilog Z80('75), Intel 8086 ('79), PowerPC 601 ('91), PowerPC G5 (02), Intel Pentium 4 HT (03) ecc negli anni 50 i programmi venivano effettivamente scritti in linguaggio macchina ....

45 premessa – traduzione un programma scritto nel LM di un calcolatore X puo' essere eseguito solo da tale macchina .... per poter eseguire un programma su macchine diverse e' necessario usare un linguaggio "intermedio" - (nel senso che puo' essere capito "abbastanza" facilmente sia da una persona che da una macchina) non vincolato ad una macchina specifica, dal 55 ad oggi sono state definite diverse migliaia di linguaggi di programmazione (qualche ) esistono molti tipi di linguaggi intermedi, * procedurali (es. Fortran, C#, ...) * funzionali (es. Lisp) * logici (es. Prolog) ecc

46 premessa – traduzione diversi tipi di linguaggi intermedi - in particolare ci interessano i linguaggi procedurali - o LP; i LP sono linguaggi progettati per descrivere procedimenti di calcolo (procedure di calcolo), alcuni nomi: Fortran ('57..'90..), Cobol ('59), Algol '60, Basic (65), Algol68(68), Pascal (71), C ('71), Ada ('78), C++(84), Java(94), C#(2001) -> il programma scritto in LP deve essere tradotto nel LM di un particolare calcolatore un LP e'definito in modo che la traduzione da LP in LM e' eseguita da una macchina (in modo meccanico) velocemente cioe' da un calcolatore, e il procedimento di traduzione e' specificato nel programma di traduzione o traduttore.

47 continua nota sui linguaggi di programmazione:
un programma (testo "sorgente") e' scritto in un dato linguaggio di programmazione LP (C, Pascal, Fortran, ...), il traduttore (il programma di traduzione eseguito su un calcolatore) traduce il programma da LP nel linguaggio macchina LM; program a; begin write(‘bla’) end. 010... progr. in L.P. (Pascal) prog. in L.M. (PPCG6) traduttore esecutore

48 il compilatore deve tradurre il nostro programma -> quindi
il linguaggio LP e' definito in modo che la traduzione puo’ essere meccanica cioe' fatta da un programma traduttore, detto compilatore prog. in L.M. (PPCG6) program a; begin write(‘bla’) end. 010... progr. in L.P. (Pascal) il calcolatore esegue un prog traduttore = "Compilatore" esegue il nostro programma il compilatore deve tradurre il nostro programma -> quindi deve riconoscere se e’ scritto correttamente [se e’ una frase del linguaggio di programmazione usato] e deve riconoscere la sua struttura [per tradurlo!] ...

49 il compilatore esegue * un' analisi lessicale - per individuare i componenti elementari (nomi, operatori, simboli riservati ecc), * un' analisi sintattica della struttura del programma, e in particolare della struttura sintattica del programma * infine traduce il testo LP in un programma "equivalente" in LM (analisi semantica) il compilatore deve riconoscere se il programma scritto in un LP rispetta le regole della grammatica di quel linguaggio !

50 analisi sintattica: riconoscimento della struttura
data una grammatica G e data una stringa s si deve riconoscere 1) se s appartiene al linguaggio L(G), (cioe’ se s e’ una s.b.f. ovvero se s e’ grammaticalmente corretta) e 2) come la s e’ producibile dalla G: (cioe’ individuare la catena delle produzioni che produce s a partire da S, e quindi la struttura della s) questo procedimento si dice analisi sintattica

51 analisi semantica una volta riconosciuta una stringa come frase del linguaggio (ovvero riconosciuto un testo come un programma grammaticalmente corretto) il compilatore ((che a questo punto conosce tutti gli elementi costitutivi di base (identificatori, costanti, separatori, parole chiave, commenti ecc) e la struttura della frase (del programma) )) dicevamo, il compilatore puo' procedere alla traduzione vera e propria, in base a regole (date una volta per tutte) sulla traduzione dei singoli pezzi dal linguaggio (Pascal, C++ ecc) in linguaggio macchina; questa fase si dice analisi semantica segue un micro esempio..

52 traduzione di un'istruzione molto semplice
un esempio banale: c=a+b; (istruzione linguaggio C) dove a,b,c sono variabili di indirizzi di memoria 7700, 7704, 7708 per una macchina (unita' centrale) con istruzioni macchina del tipo: carica da mem.in reg.: move indirizzo,registro oper.aritm.reg-reg: add registro,registro, copia da reg in mem: move registro,indirizzo dopo l'analisi lessicale (calcola indirizzi dei simboli a,b,c) e sintattica (struttura) l'istruzione sara' tradotta in linguaggio macchina, (R5,.. =registri di lavoro) es: move #7708,R7 //indir di c in R7 move 7700,R5 // valore di a in R5 move 7704,R6 // valore di b in R6 add R5,R6 // somma a in b move R6,(R7) // memor.risult.da R6 // in memoria indir.c che corrisponde all'istruzione c=a+b ovvero ( c = ( a + b) ) riscritta in notazione postfissa: cab+= ovvero (c ( ab+ ) = )

53 dove sta la grammatica in un compilatore ?
per la maggior parte dei compilatori la grammatica del linguaggio e' inserita NEL compilatore, una volta per tutte, come pure la descrizione della macchina cui e' destinata la traduzione; esistono compilatori dove la descrizioni del linguaggio e' fornita con un testo (una tabella) esterno, e esistono compilatori dove la descrizione della macchina di destinazione e' fornita con delle tabelle esterne - cambiando tabelle puo' cambiare il linguaggio cui il compilatore e' destinato, e/o puo' cambiare la macchina cui la traduzione e' destinata.

54 esempio di grammatica in un compilatore esterna:
Pascal descriz IstruzA gramm S = = cpu X Pascal Prog.p COMPILATORE con SINTASSI S e per CPU X CPU_A esempio: qui il compilatore legge da file esterno la descrizione delal grammatica e la descrizione della macchina cui e' destinata la traduzione ... Gramm C++ descriz IstruzB = cpu X gramm S = Progr. C++ COMPILATORE con SINTASSI S e per CPU X CPU_B

55 esempio di analisi sintattica: (cenni)
esempio di riconoscimento di una stringa : data una grammatica G19 = (V,T,S,P), con V = { S, A, x, y, z }, T={ x, y, z }, e con P = { p1: S -> xA; p2: A-> z; p3: A -> yA; } voglio sapere se la stringa xyyyz e' producibile da G19, ovvero se si puo’ derivare s da S, se esiste una catena di derivazione S -*> s diremo che se s deriva da S allora abbiamo riconosciuto s come stringa del linguaggio L(G19)

56 esempio di analisi sintattica: (cenni)
data G19 = (V,T,S,P), con V = { S,A,x,y,z}, T={x,y,z}, e con P = { p1: S -> xA; p2: A-> z; p3: A -> yA; } riconoscere s = xyyyz significa cercare una catena di derivazione S -*> s in genere s non e' ottenibile da S con una sola produzione - cerco quindi una produzione pk che generi almeno la parte iniziale di s; divido s = xyyyz in due parti: xyyyz==xT, quindi la stringa da riconoscere e' s = xT = xyyyz, (T=yyyz), cerco allora una pk che generi una x iniziale; tra le produzioni trovo p1 che produce una x iniziale: S -> xA al posto del problema trova catena produzioni S -*> xyyyz ho ora xA -*> xyyyz cioe' trova catena A ->* yyyz

57 esempio di analisi sintattica: (cenni)
ripetizione: data G19 = (V,T,S,P), con V = { S,A,x,y,z}, T={x,y,z}, e con P = { p1: S -> xA; p2: A-> z; p3: A -> yA; } ; riconoscere s = xyyyz equivale a riconoscere s = xT = xyyyz, (T=yyyz), cerco allora una pk che generi una x iniziale; tra le produzioni trovo la p1 che produce una x iniziale: S -> xA quindi invece di S -*> xyyyz ? possiamo considerare xA -*> xyyyz possiamo dire di avere riconosciuto x, le due stringhe a sinistra e a destra iniziano entrambe con x, lo tolgo, ... e quindi ora il problema e' trovare se A -*> yyyz

58 cont. esempio di analisi sintattica (cenni) :
data G19 = (V,T,S,P), con V = { S,A,x,y,z}, T={x,y,z}, e con P = { p1: S -> xA; p2: A-> z; p3: A -> yA; } data s = xyyyz; domanda: S s ? il procedimento consiste nel provare le produzioni possibili, scelgo quella che mi da' la parte iniziale uguale : S -> xyyyz da’ la parte iniziale di s, qui "x". p1: S-> xA xyyyz provo p1, "riconosco" x, e lo elimino ora devo riconoscere yyyz come A: (cioe' se A puo' produrre s2 == yyyz) A yyyz s2 inizia con y, scelgo la p3 che da’ y: p3: A->yA yyyz riconosco y e lo elimino: abbiamo cosi' A yyz riconosciuto i primi due simboli x,y della stringa di partenza xyyyz *

59 cont. esempio di analisi sintattica (cenni) :
data G19 = (V,T,S,P), con V = { S,A,x,y,z}, T={x,y,z}, e con P = { p1: S -> xA; p2: A-> z; p3: A -> yA; } data s = xyyyz; domanda: S s ? procedim.: provo le produzioni possibili, scelgo quella che S -> xyyyz da’ la parte iniziale di s, qui "x". p1: S-> xA xyyyz provo con p1, riconosco x, e lo elimino A yyyz s inizia con y, scelgo la p3 che da’ y: p3: A->yA yyyz riconosco y e lo elimino; ripeto con A yyz quello che rimane: A -> yyz ? provo p3: A-> yA yyz ancora con p3: yA -> yyz ? riconosco A yz la y iniziale, resta da vedere se A->yz p3: A-> yA yz provo con p3, riconosco y, elimino; A z cerco produz. che da A mi da’ z, e’ p2: p2: A -> z z riconosco z, elimino - e ho finito: abbiamo ricostruito la catena di derivazione S s * *

60 cont. esempio di analisi sintattica (cenni) :
data G19 = (V,T,S,P), con V = { S,A,x,y,z}, T={x,y,z}, P = { p1: S -> xA; p2: A-> z; p3: A -> yA; } abbiamo ricostruito la catena di derivazione della stringa s = xyyyz : S ->1 xA ->3 xyA ->3 xyyA ->3 xyyyA ->2 xyyyz e quindi abbiamo anche costruito l’ albero sintattico cioe’ la struttura della frase s S x A y z 1 1 3 3 3 3 3 3 2

61 notazione delle produzioni: l'operatore oppure |
nota - vi sono diversi formalismi o modi di scrivere le produzioni (vedremo in seguito la forma piu'usata o "forma normale") al posto di: P = { S-> A; S-> B; A -> xA; A -> y; } scrivo le stesse produzioni anche cosi’: P = { S -> A | B; A -> xA | y; } il simbolo "|" sta per alternativa, leggi: "oppure": S produce A oppure B; A produce xA oppure y;

62 problemi di riconoscimento
ma spesso il procedimento di analisi sintattica di una stringa s (riconoscere la struttura, cioe’ la catena delle produzioni che da S produce s) NON e’ cosi’ semplice. Es: G20 = ( V= { x,y,z,A,B,S}, T= {x,y,z}, S, P ) dove P = { p1: S-> A; p2: S-> B; [cioe’: S produce A oppure B] p3: A -> xA; p4: A -> y; p5: B -> xB; p6: B -> z } nota: due produzioni p3 e p5 producono entrambe stringhe che iniziano con la x; ... vedremo che questo crea dei problemi) alcuni es. di derivazioni: y e xy appartengono a L(G20): S ->1 A ->4 y S ->1 A -> 3 xA ->4 xy

63 problemi di riconoscimento
spesso il procedimento di analisi sintattica di una stringa s (riconoscere la struttura, cioe’ la catena delle produzioni che da S produce s) NON e’ cosi’ semplice. Es: G20 = ( { x,y,z,A,B,S}, {x,y,z}, S, P ) dove P = {p1: S-> A; p2: S-> B; [cioe’: S produce A oppure B] p3: A -> xA; p4: A -> y; p5: B -> xB; p6: B -> z } (nota: vi sono due produz.,p3 e p5 che producono stringhe che iniziano con la x; vedremo che questo crea dei problemi) altri esempi di stringhe prodotte dalla G20: S ->2 B -> z S ->2 B -> xB ->6 xz S ->1 A ->3 xA -> xxA ->4 xxy S ->2 B -> xB ->5 xxB ->6 xxz quindi y, xy, z, xz, xxy, xxz sono producibili dal L(G20)

64 problemi di riconoscimento
G20 = ( { x,y,z,A,B,S}, {x,y,z}, S, P ) dove P = { p1: S-> A; p2: S-> B; p3: A -> xA; p4: A -> y; p5: B -> xB; p6: B -> z } esempio: riconoscere: s = xxz (che abbiamo visto prodotto dalle produzioni 2,5,5,6: S ->2 B -> xB ->5 xxB ->6 xxz considero s = xxz = xt (con t=xz) quindi da riconoscere x: p3 e p5 producono una x iniziale, scelgo la prima, p3; quindi devo riconoscere una A (catena a ritroso: xA prodotto da A prodotto da S ): 1) S -> A xxz 3) A -> xA xxz ho riconosciuto il 1.o x, rimane da riconoscere se A -> xz quindi cerco ancora una produzione A -> x....

65 problemi di riconoscimento
G20 = ({ x,y,z,A,B,S}, {x,y,z}, S, P), P= {p1: S->A; p2: S->B; p3: A -> xA; p4: A -> y; p5: B -> xB; p6: B -> z } da riconoscere: s = xxz, s = xt (con t=xz) quindi da riconoscere x: p3 e p5 producono una x iniziale, scelgo p3; quindi devo riconoscere una A, che e' producibile da S (derivazioni S->1 A, A ->3 xA ): p1) S -> A xxz p3) A -> xA xxz ho riconosciuto il primo x, rimane da riconoscere se A -> xz, uso la p3: p3) A -> xA xz riconosco x, elimino, resta da riconoscere se A produce z ....

66 problemi di riconoscimento
G20 = ({ x,y,z,A,B,S}, {x,y,z}, S, P), P= {p1: S->A; p2: S->B; p3: A -> xA; p4: A -> y; p5: B -> xB; p6: B -> z } da riconoscere: s = xxz, s = xt (con t=xz) quindi da riconoscere x: p3 e p5 producono una x iniziale, scelgo p3; quindi devo riconoscere una A (derivaz.: xA 3<- A 1<- S): S -> xxz 1) S -> A xxz 3) A -> xA xxz ho riconosciuto il 1.o x, rimane da riconoscere A -> xz, uso la p3: 3) A -> xA xz riconosco x, elimino, resta da riconoscere A z ma-unica produz. che produce z e’ la p6, la p6 produce z, MA inizia con B , nessun’altra pk produce z ... concludo che ho sbagliato strada in una delle scelte precedenti! Le scelte per A ->? xA erano senza alternativa, devo cambiare ["ritrattare"] la 1) S -> A

67 problemi di riconoscimento: ripensamenti
G20 = ({ x,y,z,A,B,S}, {x,y,z}, S, P), P = {p1: S->A; p2: S->B; p3: A -> xA; p4: A -> y; p5: B -> xB; p6: B -> z } da riconoscere: s = xxz, cioe’ da trovare se S -> xxz S -> xxz la scelta 1) ci 1) S -> A xxz porta in un vicolo A z ? cieco, quindi ... S -> xxz devo cambiare e 2) S -> B xxz scelgo la p2: 5) B -> xB xxz poi la p5, infine 5) B -> xB xz riconosco z, e ho 6) B -> z z finito,e ho la strut- tura a fianco: S B x z 2 5 5 5 5 6 il procedimento di ripensamento [ che si deve applicare se si arriva ad un punto dove non e’ possibile trovare una pro- duzione che vada bene ] si dice "backtracking"

68 nota... Abbiamo visto due algoritmi a backtracking o a prova e riprova (problema delle 8 regine, problema del cavallo) – ma abbiamo anche visto che i tempi di esecuzione crescono enormemente al crescere della dimensione del problema! Un programma traduttore deve poter riconoscere la struttura del programma senza troppi tentativi infruttuosi, e senza ripensamenti... si pensi ai tempi di traduzione di un programma di 100 pagine ovvero righe ... in effetti i linguaggi di programmazione sono definiti in modo da consentire un riconoscimento veloce, senza tornare indietro nel testo (testo letto e tradotto in una passata)

69 problemi di riconoscimento
talvolta e’ possibile riscrivere la grammatica in modo che il processo di riconoscimento non richieda ripensamenti (senza backtracking): per la G20 di prima, P = { S-> A|B; A-> xA|y; B-> xB|z } [ricorda: le P della G20 producono stringhe del tipo: y,z,xy,xz,xxy,xxz,xxxy,xxxz ... ] in due produzioni di G20 la parte destra inizia con lo stesso simbolo x; [questo causa backtracking - da evitare!] ... al posto delle P della G20 di sopra definiamo le produzioni: P1 = { S-> xS | C; C -> y | z } dove la prima produzione produce una o piu’ x e la seconda produzione S -> C genera una C al posto di S, C che poi produce o una y o una z; abbiamo ora...

70 riconoscimento : una grammmatica migliore
G20bis: P1={p1,2: S-> xS|C; p3,4: C -> y|z} p1: S -> xS cioe: S produce una o piu’ x, p2: S -> C cioe' S genera una C che poi p3: S -> y|z ovvero C produce una y (p3) o una z (p4) ora per riconoscere xxz: S -*> xxz ? S ->1 xS -*> xxz riconosco x, elimino, guardo il resto: S -*> xz di nuovo la produzione p1 : S ->1 xS -*> xz riconosco x, elimino, guardo il resto: S -*> z per riconoscere z non va bene p1 o p2; S ->2 C -*> z ma indirettamente: C->z, e S->C C ->4 z == z provo con p4, riconosco z e ho finito.

71 riconoscimento : una grammmatica migliore
... una grammatica tale da consentire un riconoscimento veloce di una stringa (e quindi tale che permetta una traduzione veloce dei nostri programmi...) deve avere alcune proprieta' ... per definire una "buona" grammatica che poi consenta un riconoscimento veloce vi sono diverse precauzioni da usare ma noi ci fermiamo qui

72 esercizi sull' analisi sintattica - primo esercizio:
definire una grammatica che produce stringhe del tipo: 1.0 33.22 4.5E-2 77.88E+123 (costante floating senza segno) segue una soluzione trovare una grammatica per le stringhe date = significa trovare uno schema di struttura comune a tutte queste stringhe .. individuo due parti, la parte cifre e la parte esponente; a loro volta le due parti hanno una struttura...

73 esercizi sull' analisi sintattica - primo esercizio:
es.: definire una grammatica che produce stringhe del tipo costante floating senza segno: 1.0 33.22 4.5E-2 77.88E+123 segue una soluzione trovare grammatica per le stringhe date = significa trovare uno schema di struttura comune a tutte queste stringhe : individuo due parti, la parte cifre e la parte esponente; a loro volta le due parti hanno una struttura: parte cifre e' fatta da due parti intere separate da un punto , parte esponente e' fatta da una E seguita da un segno seguito da un intero...

74 esercizi sull' analisi sintattica - primo esercizio:
E E E+123 costante float ha due parti, la parte cifre e la parte esponente; a loro volta le due parti hanno una struttura: parte cifre e' fatta da due parti intere separate da un punto , parte esponente e' fatta da una E seguita da un segno seguito da un intero... G21 = ( S, T, V, P ), con T = (0,1,2,3,4,5,6,7,8,9,0,+,-,E) S = X | Y (X cost senza parte espon, Y con espon) X = I . I (I intero senza segno) I = C | C I ( C cifra, un Intero e' una o piu' cifre) Y = X E Z I (costante con parte esponente) Z = + | (segno) es. di derivazione per le due stringhe 1.0 e 3.3E-5: S -> X -> I.I -> C.I -> 1.I -> 1.C -> 1.0 S -> Y -> X E Z I -> I.I E Z I -> C.CEZI -> 3.3EZI -> 3.3E-5

75 esercizi sull' analisi sintattica - primo esercizio:
riprendiamo la grammatica di costante floating senza segno: G21 = ( S, T, V, P ), con T = (0,1,2,3,4,5,6,7,8,9,0,+,-,E) S = X | Y (X cost senza parte espon, Y con espon) X = I . I (I intero senza segno) I = C | C I ( C cifra, un Intero e' una o piu' cifre) Y = X E S I (costante con parte esponente) S = + | (segno) con questa grammatica posso produrre anche costanti tipo: E perche' questa grammatica non specifica limiti; esempio di stringa non corretta: 1E manca il punto (le cifre del dato sono prodotte da X, con la produzione X-> I . I )

76 riprendiamo ancora alcune considerazioni...
l’ analisi sintattica e’ alla base di ogni programma traduttore (compilatore) da un linguaggio di programmazione (Pascal, C, Fortran, ...) in linguaggio macchina: il compilatore deve ricostruire la derivazione S -> programma, e cioe’ l’albero sintattico ovvero la struttura del programma (programma = frase del linguaggio Pascal o C..) e in base alla struttura traduce il nostro programma in linguaggio macchina ( mantenendo il significato == semantica del programma ); La gramm. e’ (quasi) sempre "incorporata" nel compilatore.

77 riprendiamo ancora alcune considerazioni...
l’ analisi sintattica : il compilatore deve ricostruire la catena di derivazione S -*> programma, cioe’ l’albero sintattico o la struttura del programma (programma = frase del linguaggio C ) in generale una prima parte del compilatore riconosce le "parole", ovvero gli "identificatori", e ricostruisce il lessico usato nel programma (fa un "dizionario" delle parole usate nel programma) ... > questa parte si chiama analisi lessicale

78 versione Ada o Pascal o C alla versione in linguaggio macchina
programma sorgente analisi lessicale (identificatori) analisi sintattica (albero sintattico) analisi semantica (traduz.in ling.macch) programma tradotto traduzione di un programma dalla versione Ada o Pascal o C alla versione in linguaggio macchina con lo stesso significato (stessa semantica)

79 esercizi sull' analisi sintattica - secondo esercizio:
data G21 = ( V, T, S, P ), T = { a,b,1,2 }, V = T u { L,C,I,S }, P = { p1: S -> I; p2: I -> IL; p3: I -> IC; p4: I -> L; p5: I -> C; p6: L -> a; p7: L -> b; p8: C -> 1; p9: C -> 2 } es: S ->1 I ->3 IC ->2 ILC ->3 ICLC ->4 LCLC ->6 aCLC ->8 a1LC ->6 a1aC ->9 a1a2 (nota: L = lettera, C=cifra, I = Item=elemento) domanda: 2b2 appartiene a L(G21) ? ovvero: e’ producibile dalla grammatica G11 ? segue soluzione

80 continua 2.o esercizio sull' analisi sintattica
G21: P = { 1: S-> I; 2: I-> IL; 3: I-> IC; 4: I-> L; 5: I-> C; 6: L-> a; 7: L-> b; : C-> 1; : C-> 2 } domanda: 2b2 appartiene a (e’ producibile da) L(G21) ? per riconoscere 2b2 procedo come segue: ? -> 2b2 cerco produz. con 2 in 1.a posiz. a dest., e' la p9 C? ->9 2b2 quindi C seguito da ? produce 2 seguito da b2; ? -> Cb2 cerco produzione con C in 1.a posiz.a destra: I? ->5 Cb2 solo la p5 produce C come richiesto, quindi ? -> Ib2 ->5 Cb2 ->9 2b2 dopo 2 tentativi di riconoscimento

81 continua 2.o esercizio sull' analisi sintattica
G21: P = { 1: S-> I; 2: I-> IL; 3: I-> IC; 4: I-> L; 5: I-> C; 6: L-> a; 7: L-> b; : C-> 1; : C-> 2 } ... (continua) per riconoscere 2b2 abbiamo trovato: ? -> Ib2 ->5 Cb2 ->9 2b2 dopo 2 tentativi di riconoscimento ? -> Ib2 devo ottenere I in 1.a posizione a destra - provo (in ordine) le produzioni che soddisfano questa condizione: provo con p1: S ->1 I che da' luogo alla catena di produzioni : S ->1 I ->5 C -> ma: rimane da riconoscere b2 ! -> queste scelte fatte per ricostruire questa catena non e' ok-> cambio ? C->9 2 non si puo' cambiare, I->5 C non si puo', cambio S->1 I, scelgo la prossima p che produce I in 1.a posizione: I->2 IL per produrre I in 1.a posizione, -> I ->2 IL ->5 CL ->9 2L ... rimane da trovare se L -> b2

82 continua 2.o esercizio sull' analisi sintattica
G21: P = { 1: S-> I; 2: I-> IL; 3: I-> IC; 4: I-> L; 5: I-> C; 6: L-> a; 7: L-> b; : C-> 1; : C-> 2 } S -> ? 2b2 ... dopo alcuni tentativi si propone la catena : S ->1 I ->2? IL ->? Ib2 ->5 Cb2 -> b2 rimane da trovare se L -> b2 ... dopo altri tentativi, sempre applicando le produzioni in ordine, si trova che S ->1 I ->3 IC >2 ILC ->5 CLC ->9 2LC ->7 2bC b2 ... per riconoscere la stringa molto semplice 2b2 abbiamo dovuto ritrattare piu' volte una scelta di produzione ! ... non e' una grammatica che consenta un riconoscimento veloce ...

83 continua 2.o esercizio sull' analisi sintattica
G21: P = { 1: S-> I; 2: I-> IL; 3: I-> IC; 4: I-> L; 5: I-> C; 6: L-> a; 7: L-> b; : C-> 1; : C-> 2 } G21bis: cambio la grammatica in modo da avere le produzioni ricorsive a sinistra invece che a destra, e in modo da non avere lo stesso simbolo all'inizio della parte destra in piu' produzioni: vediamo come si riconosce 2b2 ... P= { 1: S-> I; 2: I-> II; 4: I->L; 5: I->C; 6: L-> a; 7: L-> b; 8: C-> 1; 9: C-> 2 } Cb2 ->9 2b2 I? >5 Cb2 riconosco 2 come una C, e C come una I poi analizzo il resto della stringa... L? ->7 b2 ? ->? come gia' visto, C ->9 2, e I >5 C abbiamo cosi' riconosciuto tutta la 2b2:

84 continua 2.o esercizio sull' analisi sintattica
G21bis: P= { 1: S-> I; 2: I-> II; 4: I->L; 5: I->C; 6: L-> a; 7: L-> b; 8: C-> 1; 9: C-> 2 } riconoscere 2b2 significa riconoscere 2xx; la produz. 9 da': Cb2 ->9 2b2 (rimane da riconoscere b2) C e' ottenibile da I, e I da S: I? >5 Cb2 riconosco 2 come una C, e C come una I poi analizzo il resto della stringa, b2: L? ->7 b L puo' produrre b, quindi resta ?->2; ? ->? come gia' visto, C ->9 2, e I >5 C abbiamo cosi' riconosciuto tutta la 2b2: S ->1 I ->2 II ->2 III ->6 ILI ->5 CLI ->5 CLC CLC ->9 2LC ->7 2bC ->9 2b2

85 continua 2.o esercizio sull' analisi sintattica
fare l'albero sintattico di 2 b 2 per G21bis P= { 1: S-> I; 2: I-> II; 4: I->L; 5: I->C; 6: L-> a; 7: L-> b; 8: C-> 1; 9: C-> 2 } ovvero esprimere graficamente la catena di derivazione S ->1 I ->2 II ->2 III III ->6 ILI ->5 CLI CLI ->5 CLC ->9 2LC 2LC ->7 2bC ->9 2b2

86 S P= { 1: S-> I; 2: I-> II; 4: I->L; 5: I->C; I
6: L-> a; 7: L-> b; 8: C-> 1; 9: C-> 2 } S ->1 I ->2 II II -> III ->6 ILI ILI ->5 CLI ->5 CLC CLC ->9 2LC ->7 2bC 2bC ->9 2b2 1 I 2 II 2 III 4 ILI 7 IbI ... 2b2

87 T = { bello, basso, buono, cattivo, ascolta, guarda, mangia, il, un }
3.o esercizio : G22 = ( V, T, S, P ), T = { bello, basso, buono, cattivo, ascolta, guarda, mangia, il, un } V = T u {A,B,C,X,Y } P = { p1: S -> A B C; p2: A -> X Y; p3: C -> X Y; p4: X -> il | un ; p5: Y -> bello | basso | buono | cattivo; p6: B -> ascolta | guarda | mangia; } es: S->ABC ->XYBXY -> il bello ascolta il bello domanda: quante frasi genera la G22? S A B C X Y il bello ascolta

88 3.o esercizio G22 = ( V, T, S, P ), V = T u {A,B,C,X,Y }
T = { bello, basso, buono, cattivo, ascolta, guarda, mangia, il } P = { p1: S-> A B C; p2: A-> X Y; p3: C -> X Y; p4: X-> il | un; p5: Y-> bello | basso | buono | cattivo; p6: B -> ascolta | guarda | mangia; } domanda: quante frasi genera la G22? il bello ascolta il bello; un bello ascolta il bello; ... un cattivo mangia il cattivo; un cattivo mangia un cattivo; dalla produzione S->ABC ->XYBXY e dalle produzioni seguenti X->.., Y->.., per trasformare XYBXY in una frase ci sono 2 * 4 * 3 * 2 * 4 possibilita' ovvero 8 * 3 * 8 = 192 frasi un numero finito !

89 GRAMMATICHE a struttura di frase:
riassumendo: GRAMMATICHE a struttura di frase: G = ( Vocabolario, Terminali, S, Produzioni ) G genera un Linguaggio L, L = tutte le stringhe derivabili da S ("string. ben formate") una frase s deriva (indirettamente) da S: S s la catena di derivazione corrisp. all' albero sintattico e quindi da' la struttura della frase traduzione: l’ analisi sintattica riconosce una s.b.f. e ricostruisce la catena -> la struttura -> l’ albero sintattico grammatiche "opportune" consentono un riconoscimento veloce *

90 4.o esercizio quarto esercizio:
scrivere una grammatica che produca frasi del tipo: a = b a = b + c a = a + b + a + c b = c + c c = c b = b + c + c + a + b + a + c a = a + a + a + a + a ...

91 4.o esercizio scrivere una grammatica che produce frasi del tipo:
a = b a = b + c a = a + b + a + c b = c + c c = c soluzione: scegliamo due simboli non terminali, Espressione e Termine, T= { a, =, b, c }, V= { S, E, T, a, =, b, c }, P= { p1, p2, p3 }, con: p1: S -> T = E p2: E -> T | T + E p3: T -> a | b | c vediamo ad es. la derivazione della stringa a = a + b + a + c : S ->1 T=E >2b T=T+E >2b T=T+T+E ->2b T=T+T+T+E ->2a T=T+T+T+T ->3a a=T+T+T+T ->3a a=a+T+T+T ->3b a=a+b+T+T ->3a a=a+b+a+T ->3c a=a+b+a+c

92 classificazione delle grammatiche vediamo due precisazioni
vediamo una classificazione delle grammatiche ... ma prima vediamo due precisazioni

93 - tassonomia delle grammatiche -
1) riprendiamo il concetto di produzione: una G e' una quadrupla ( V, T, S, P ), con P produzioni (regole di sostituzione che producono da stringhe date altre stringhe). Finora le regole di sostituzione o di produzione erano del tipo: A -> stringa, ma - in generale le produzioni sono regole di sostituzione del tipo: a A b -> a s b dove: leggi: A produce s se sta tra le stringhe a e b, ovvero se sta nel contesto di a e b; s e' stringa di V* che sostituisce A, A = simbolo sostituito (non terminale), A ∈ N, con N = V-T, a,b sono stringhe di V* , sono stringhe "costanti" per la produzione P; a, b fissano il contesto per A (vedremo subito)

94 grammatiche e contesto
esempi di produzioni dipendenti dal contesto: di seguito sono riportati alcuni esempi di produzioni dove viene sostituito il simbolo non terminale A con stringhe diverse a seconda del contesto: 1) b A c B -> b c B c B A produce cB se sta tra b e cB 2) x A y -> x b C y A produce bC se sta tra x e y 3) D A x -> D x A si puo’ togliere se sta tra D e x 4) A C -> C A si puo’ eliminare se e’ seguito da C, 5) zot A -> zot A si puo’ eliminare se preceduto da zot ecc

95 lunghezza delle stringhe nelle produzioni
consideriamo la lunghezza delle stringhe a sinistra e a destra delle produzioni : 1) d X d -> d gatto d /si puo’sost. gatto a X se X sta tra d e d ovvero X in contesto d,d produce gatto; 2) x X y -> x cane y /si puo’sost.X con cane se X sta tra x e y; 3) D X qh -> D XX qh /puoi raddopp. X se sta tra D e qh le produzioni 1,2,3 allungano la stringa di partenza, mentre le produzioni seguenti 4,5,6 accorciano la stringa di partenza: 4) A C -> C / A si puo’ eliminare se e’ seguito da C, 5) D -> λ / D si puo’ eliminare in ogni situazione 6) i j M o -> i j o / M si puo’eliminare se sta tra ij e o...

96 classificazione delle grammatiche secondo Chomsky
0) Gramm.Gener. insiemi di grammatiche: gramm. di tipo 0 o generali generano un insieme di linguaggi molto vasto, che contiene come sottoins. le gramm. di tipo 1 o monotone, che a loro volta contengono le gramm. di tipo 2 o libere da contesto, che contengono le grammatiche lineari ... ecc... vediamo una parte ... 1) Gramm.Monot. 2) Gramm. Libere da Contesto 3) Gr. Lineari ...

97 classificazione delle grammatiche secondo Chomsky
nota: la classificazione di Chomsky definisce piu' insiemi propriamente contenuti uno nell’altro, nel senso che ad es. esistono linguaggi di tipo zero che non sono producibili da alcuna grammatica di tipo 1 o piu’ alto, ecc vediamo meglio i vari tipi ...

98 linguaggi di tipo 0 sono i piu’ generali:
per tali linguaggi si possono generare ordinatamente tutte le frasi del linguaggio (sono enumerabili) ma in generale alla domanda " la s e’ ben formata ? " con 1) data Gramm. di tipo zero (V,T,S,P) 2) e con data una stringa s di T* , ossia se appartiene al linguaggio L(G). cioe’ se essa e’ producibile da S cioe’ S s nota: se s appartiene a L(G) allora enumerando le frasi di L(G) prima o poi fabbrico anche la s, e ho la risposta SI, ma se s NON appartiene a L(G) allora non arriviamo mai alla risposta ... per tale motivo i L(G) di tipo zero si dicono semidecidibili *

99 grammatiche di tipo 1, monotone: c’ e’ un vincolo:
le produzioni delle grammatiche di tipo uno sono monotone, cioe’ per qualunque produzione a -> b (ad es. una C mangia -> una cicogna mangia) la lunghezza della stringa prodotta b e’ sempre maggiore o uguale alla lunghezza della a: |a| <= |b| ovvero a B c -> a s c con |B|=1, e con |s| >= 1 (s mai vuota);

100 le grammatiche dove per qualunque produzione a -> b
grammatiche di tipo 1, monotone: le grammatiche dove per qualunque produzione a -> b la lunghezza della stringa prodotta b e’ sempre maggiore o uguale alla lunghezza della a: |a| <= |b| si dicono monotone, ad esempio : a B c -> a s c con |B|=1, e con |s| >= 1 (s mai vuota); i linguaggi generati da grammatiche monotone sono di tipo uno - questi linguaggi di tipo 1 sono decidibili, nel senso che data una s si puo’ sempre rispondere alla domanda se s e’ derivabile da S (basta generare l'insieme IZ di tutte le stringhe z con |z|<=|s| e conrollare se s ∈ IZ ) ma: per un uso pratico i ling.di tipo 1 sono ancora troppo generali, il procedimento di riconoscimento puo’essere MOLTO lungo (sono grammatiche con uso di contesto !!)

101 linguaggi di tipo 2 o "liberi da contesto"
le produzioni dei linguaggi di tipo 2 sono tutte del tipo: A -> m con A simbolo non terminale (UNO!), e con m stringa su V* con |m| >= 1 di questo tipo sono le grammatiche dei linguaggi di programmazione, e in particolare le grammatiche esprimibili con i formalismi del tipo: ** grammatiche BNF (Backus Naur Form) ** grammatiche EBNF (Extended Backus Naur Form) ** diagrammi sintattici ( tre formalismi che vedremo ) ** e altri (che non vedremo) (*) Backus: partecipo' alla def. del Fortran 57 e, con Naur ed altri, alla definizione dell'Algol 60, da cui derivano C,Pascal, Ada, Java,

102 linguaggi di tipo 2 o "liberi da contesto"
le produzioni dei linguaggi di tipo 2 sono tutte del tipo: A -> m con A simbolo non terminale (UNO!), e con m stringa su V* con |m| >= 1 cioe': tutte le produzioni hanno la forma: un simbolo non terminale A produce qualcosa, cioe' una stringa m di V* ; le grammatiche dei linguaggi di programmazione sono monotone e libere dal contesto, sono di tipo 2, esprimibili con i formalismi (vedremo tra breve) come BNF (Backus Naur Form), EBNF (Extended Backus Naur Form), i diagrammi sintattici e altri; in particolare, una stringa di un linguaggio di tipo 2 (monotono e libero da contesto) puo' esser riconosciuta con un procedimento lineare, senza ripensamenti, dove il costo del riconoscimento cresce linearmente con la lunghezza della stringa;

103 es: grammatica delle espressioni aritmetiche senza segno
G23 = ( V,T,E,P ), (simbolo di partenza E), con: V = {E,T,F,A,M,V,C} + T, T = {a,b, 1,2,3,0, (, ) }, P = { p1, p2, p3, p4, p5, p6, p7 } con: p1: E -> T | T A E; // Espressione e' un Termine oppure e' un // Termine seguito da un A seguito da una Espress p2: T -> F | F M T; // Termine e'un Fattore oppure un F seguito.. p3: F -> V | C | ( E ) // un Fattore e' una variabile oppure e' una // Costante oppure e' una Espressione tra parentesi p4: A -> + | -; // un operatore di Addizione e' un + oppure un - p5: M -> * | /; // un operatore di Moltiplicazione e' * oppure / p6: V -> a | b; // una variabile e' un a oppure un b p7: C -> 0 | 1 | 2 | 3; // una costante e' un 0, o un 1, o un 2,o un 3

104 G23 grammatica delle espressioni aritmetiche, es.1: S->0
G23 = ( V = {E,T,F,A,M,V,C} + T, T = {a,b, 1,2,3,0, (, ) }, P = { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p6: V -> a | b; p5: M -> * | /; p7: C -> 0 | 1 | 2 | 3; ) tre esempi di derivazione: 1) costante 0 e' un' espressione aritmetica, e si deriva con: E ->1a T E produce T (E e' un termine) T ->2a F T produce F (un termine e' un fattore) F ->3b C F produce C (un fattore e' una cifra) C ->7a C produce 0 (una cifra e' uno "0") quindi 0 e’ un’espress. aritmetica; vediamo l'albero sintattico ...

105 G23 grammatica delle espressioni aritmetiche, es.1: S->0
G23: V = {E,T,F,A,M,V,C} + T, T = {a,b, 1,2,3,0, (, ) }, P= { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p6: V -> a | b; p5: M -> * | /; p7: C -> 0 | 1 | 2 | 3; cont.es.1: costante 0 si deriva: E ->1a T E produce T T ->2a F T produce F F ->3b C F produce C C ->7a C produce 0 E 1a T 2a F 3b C 7a albero sintattico della costante 0

106 G23 grammatica delle espressioni aritmetiche, es.2: S->a+a
G23 = ( V = {E,T,F,A,M,V,C} + T, T = {a,b, 1,2,3,0, (, ) }, P = { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p6: V -> a | b; p5: M -> * | /; p7: C -> 0 | 1 | 2 | 3; ) secondo esempio di derivazione, l' espressione a+a: E >1b T A E T A E >1a T A T T A T >2a F A T >2a F A F F A F >4a F + F >3a V + F V + F >3a V + V >6a a V a + V >6a a a quindi a + a e’ un’espressione aritmetica, e' una frase del linguaggio L(G23); a+a ha significato e struttura come abituali ...

107 G23 grammatica delle espressioni aritmetiche, es.2: S->a+a
G23 P = { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p6: V -> a | b; p5: M -> * | /; p7: C -> 0 | 1 | 2 | 3; E >1b T A E T A E ->1a T A T T A T ->2a F A T F A T ->2a F A F F A F ->4a F + F F + F ->3a V + F V + F ->3a V + V V + V ->6a a V a + V ->6a a a 1b T E A 1a 4a T F + 3a 2a F V 3a 6a V a 6a albero sintattico dell'espressione E -*> a+a a

108 G23 gramm.delle espressioni aritmetiche, es.3: S->3*(a+2)-b
G23 = ( V = {E,T,F,A,M,V,C} + T, T = {a,b, 1,2,3,0, (, ) }, P = { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p6: V -> a | b; p5: M -> * | /; p7: C -> 0 | 1 | 2 | 3; ) terzo esempio di derivazione, stringa 3 * ( a + 2 ) – b: E >1b T A E >1a T A T T A T >2b F M F A T >2a F M F A F F M F A F ->4b F M F - F >5a F * F - F F * F – F ->3c F * ( E ) - F >1b F * ( T A E ) - F ->3 F * ( T A T ) - F ->2 F * ( T A T ) - F ->2 F * ( F A T ) - F ->2 F * ( F A F ) - F ... ->6 3 * ( a + 2 ) - b quindi * ( a + 2 ) – b e’ un’ espressione aritmetica, con significato e struttura come abituali ...

109 G23 gramm.delle espressioni aritmetiche, es.3: S->3*(a+2)-b
G23 P = { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p6: V -> a | b; p5: M -> * | /; p7: C -> 0 | 1 | 2 | 3; E ->1b TAE >1a TAT ->2b FMTAT ->2a FMFAF ->4b FMF-F ->5a F*F-F ->3c F*( E )-F ->1b F*(TAE )-F ->1a F*( T A T )-F ->2a F*( F A T )-F ->2a F*( F A F )-F ...-> ->6b 3*( a + 2 )-b 1b E T 2b A T 4b F T M - 3b 5a F F C 3c 7d * ( E ) V 3 1b E b T A T F + C F a C albero sintattico di 3 * ( a + 2 ) – b 2

110 A -> m con A non terminale, e
grammatiche lineari un caso particolare delle grammatiche monotone e libere dal contesto (tipo 2) sono le grammatiche lineari, dove sono permesse solo le produzioni del tipo A -> m con A non terminale, e con m stringa con al piu’ un simbolo non terminale; se le produzioni sono solo del tipo: A -> B p oppure A -> p allora diremo la grammatica lineare sinistra, gramm. lineare destra se le prod. sono: A -> B p o A -> p le grammatiche dei linguaggi di programmazione sono in gran parte (*) del tipo "lineari destre" dove tutte le produzioni sono del tipo A -> n B (A,B non terminali, n stringa di terminali) oppure A -> n [tali grammatiche consentono una veloce analisi sintattica] (*) attenz: alcune parti delle gramm. (Pascal, C, Java) non sono di questo tipo, sono in parte dipendenti dal contesto...

111 vediamo la classica notazione BNF per grammatiche
notazione BNF = Backus Naur Form (Backus Normal Form) la notazione BNF fu usata nel 1960 per definire la sintassi del linguaggio Algol 60, ed e' tuttora in uso. Backus (USA) dell'IBM, co-autore di Fortran 57 e Algol 60, Naur (Danese), co-autore Algol-60, in BNF le produzioni sono scritte in modo un po' diverso: i simboli non terminali sono scritti tra <parentesi spigolose> il simbolo -> (genera, produce) e' scritto con ::= es.la produzione A -> x (A non terminale, x terminale) in BNF si scrive <A> ::= x (A produce x ) la BNF prevede le seguenti operazioni sulle stringhe: * concatenazione * alternativa * ricorsione

112 BNF = Backus Naur Form (o Backus Normal Form)
notazione BNF distingue i simboli non terminali con le <SimNonTerm> e indica l'operazione "produce" con ::=, es A > x (A non terminale, x terminale) in BNF <A> ::= x la BNF prevede sulle stringhe le operazioni di: * concatenazione * alternativa * ricorsione es: la produzione gia’ vista: E -> T | T A E si scrive in BNF: < Espr > ::= <Term> | <Term > < OpAddiz> < Espr > che si legge: un’Espressione produce un Termine oppure un Termine seguito da un Operatore di Addizione seguito da un’ Espressione ...

113 notazione BNF per grammatiche
si noti l’uso della ricorsione diretta nelle produzioni, usata di solito semplicemente per specificare la ripetizione: a+b+c+a+d e’ prodotta da E -> T | T A E ovvero (in BNF) : < Espr > ::= <Term> | <Term > < OpAddiz> < Espr > E -> T A E -> T A T A E -> T A T A T A E -> T A T A T A T A E -> T A T A T A T A T -> T + T A T A T A T -> ... -> T + T + T + T + T -> ... -> a + b + T + T + T ..-> a+b+c+a+d

114 notazione BNF per grammatiche
una variante della BNF e' la notazione EBNF Extended BNF di Niklaus Wirth (del 1977): usa = invece di ::= 2) mette un punto alla fine di ogni produzione; e 3) alle operazioni gia’ note di * concatenazione G= A B leggi:G produce A seguito da B * alternativa : X= A | B leggi: X produce A oppure B sono aggiunte 3 coppie di simboli particolari (semplificano la notazione e il lavoro dell'analisi sintattica) * ripetizione (zero o piu’ volte): Y = { A }. leggi: Y produce A ripetuto zero o piu’ volte * opzionalita’: D = [ d ]. leggi: D produce d oppure nulla * raggrup- X = ( a | b ) c. leggi: X produce ac oppure bc -pamento

115 un esempio: riconoscitore di espressioni
grammatica EBNF di espressioni aritmetiche con parentesi: Expr = Term { ( "+" | "-" ) Term } . (expr e' un termine oppure piu' termini separati da + o -) Term = Fact { ( "*" | "/" ) Fact } . (term e' un fattore oppure piu' fattori separati da * o /) Fact = Lett | "(" Expr ")" . (un fattore e' una lettera o un'espressione tra parentesi) Lett = "a" | "b" | "c" . (una lettera e' una a oppure una b o una c) secondo questa grammatica, a+z, a*a+a/a-b*c, d, d*e*f*g, a+b+c+d, c*(d+e)+f ecc sono espressioni corrette o ben formate.

116 un esempio: riconoscitore di espressioni
il frammento di programma seguente trasforma espressioni scritte con questa grammatica in notazione polacca postfissa (che poi e' la base per l'esecuzione su un calcolatore) a+b in ab+, a+b+c+d in ab+c+d+, a+b*c in abc*+ (a+b)*(c+d) in ab+cd+* nota: l' "esecuzione" di ab+cd+* si realizza con: interpreta ab+ ottieni risult.intermedio x, e quindi: xcd+* poi interpreta cd+ e ottieni y: xy* infine esegui xy* e ottieni il risultato

117 un esempio: riconoscitore di espressioni
il frammento di programma seguente trasforma espressioni scritte con questa grammatica in notazione polacca postfissa (che poi e' la base per l'esecuzione su un calcolatore) a+b diventa ab+, (a+b)*(c+d) diventa ab+cd+* il main legge il primo carattere dell' espressione da riconoscere e poi chiama la procedura che riconosce un'espressione: char c; int main () { cout << "inserisci espressione poi RETURN "; R( c ); // leggi un carattere in anticipo Expr(); // e poi leggi un'espressione return 0; } // main

118 un esempio: riconoscitore di espressioni
segue la procedura che legge un'espressione secondo la gra.: Expr = Term { ( "+" | "-" ) Term } . un expr e' un termine oppure piu' termini separati da + o - e quindi: void Expr() { // Legge in forma infissa es: a+b+c // e la riscrive in forma postfissa es: ab+c+ // entro con il 1.o carattere gia' letto in var.globale c char addoperator; // memoria locale !! Term(); // Un'espressione inizia con un termine, while ( (c=='+') || (c=='-') ) { // se leggo oper.addiz., addoperator = c; // memorizza, scriveremo dopo R ( c ); Term(); // scrivi oper.addizione cout << addoperator; // dopo i 2 termini }; // while dei termini ripetuti } // Expr;

119 un esempio: riconoscitore di espressioni
procedura che riconosce un termine, da sintassi: Term = Fact { ( "*" | "/" ) Fact } . term e' un fattore oppure piu' fattori separati da * o / , quindi: void Term() { // legge un termine in forma infissa a*b char muloperator; // e lo riscrive in postfissa ab* Fact(); // un termine inizia con un fattore while ( (c== '*') || (c=='/') ) { // se segue * o / allora muloperator= c; // memorizza, per dopo R( c ); // Anticipa la lettura di un carattere. Fact(); // leggi il seguente fattore cout << muloperator; // scrivi dopo i due fattori }; // while } // Term;

120 infine la procedura per riconoscere un fattore con sintassi
un esempio: riconoscitore di espressioni infine la procedura per riconoscere un fattore con sintassi Fact = Lett | "(" Expr ")" . un fattore e' una lettera o un'espressione tra parentesi void Fact() { // legge un fattore ad es. a oppure (a+b) // entro con il primo carattere di fatt gia' letto if ( c == '(' ) { // allora Fact e' una espressione tra parentesi R( c ); // Leggi il primo carattere dell'espressione e Expr(); // vai a leggere un'espressione -> RICORSIONE } else { // se fatt.non inizia con ( allora e' una lettera cout << c; // scrivi questa lettera } // if R( c ); // Lettura anticipata di un carattere } // Fact;

121 un esempio: riconoscitore di espressioni
il riconoscitore di espressioni aritmetiche visto si modifica facilmente in un interprete di espressioni, che anche calcola il valore delle espressioni; semplificando, assumo gli operandi numeri di una cifra soltanto, con la grammatica del fattore: Fact = Cifra | "(" Expr ")" . e quindi le espressioni corrette sono del tipo: (1+2)*(3+4-1) che il programma trasforma in: * * aggiungo a tutte le operazioni di scrittura cout<<x [[ con x operando(cifra) oppure operatore (+ - * / ) ]] una delle due cose: se sto scrivendo un operando, ad es. 1, lo metto in pila [ push(x) ] se sto scrivendo un operatore, ad es. +, allora prelevo dalla pila i due operandi [che saranno 1 e 2 nell'es. sopra] con due pop(op1) e pop(op2) e poi eseguo quanto richiesto, cioe' 1+2; il risultato va in pila: push(1+2)

122 un esempio: riconoscitore di espressioni
la procedura Espr si modifica come segue: void Expr() { ... Term(); // Un'espressione inizia con un termine. // leggo sempre un carattere in avanti; se c e' un addoper: while ( (c=='+') || (c=='-') ) { addoperator = c; // Da scrivere dopo (appunto postfix) R ( c ); // inizio del Termine, e quindi chiama Term(); // legge un Termine e lo memorizza in pila cout << addoperator; // scrivi l'operatore di // addizione dopo i 2 termini pop(op1); //espress= op1 addop op2, con op1 e op2 in pila pop(op2); if(addoperator=='+') op3=op2+op1; else op3=op2-op1; push(op3); // ora in cima sta il risultato della terna }; // while }//expr

123 un esempio: riconoscitore di espressioni
la procedura Term si modifica come segue: void Term() { // legge un termine char muloperator; int op1, op2, op3; Fact(); // un T e' un F oppure un F*F oppure F/F oppure F*F*F while ( (c== '*') || (c=='/') ) { muloperator= c; // Memorizza muloperator, per dopo R( c ); // Anticipa la lettura di un carattere. Fact(); // e leggi il secondo fattore cout << muloperator; // scrivi su schermo, e valuta: pop(op1); // cima pop(op2); // sotto la cima if(muloperator=='*') op3=op1 * op2; else op3=op2 / op1; // il risultato sostituisce push(op3); // una terna e va in cima della pila }; // while } // Term;

124 un esempio: riconoscitore di espressioni
la procedura Fact si modifica come segue: void Fact() { // legge un fattore di P19ESER2 // entro con il carattere di fatt gia' letto if ( c == '(' ) { // Fact o e' una espressione tra parentesi R( c ); // Leggi prossimo carattere. Expr(); // RICORSIONE INDIRETTA !!! } else { // Oppure e' una cifra cout << c; // Scrivi questa cifra push(i); // e metti in pila il valore numerico } // if // del carattere cifra letto, poi fai R( c ); // lettura anticipata di un carattere } // Fact; es. di esecuzione: *3 diventa: *+ ed il risultato = 7

125 EBNF e diagrammi sintattici la notazione formale EBNF per le grammatiche lineari si puo' esprimere anche in forma grafica: * concatenazione G = A B . il diagramma sintattico rappresenta la sequenza degli stati di riconoscimento (del compilatore) della struttura sintattica: se G e' definito dalla concatenazione di A e di B, per riconoscere G devo riconoscere ("attraversare") prima A e poi B, da cui il diagramma sintattico sopra, che appunto rappresenta il percorso o la sequenza di riconoscimento ... B A G

126 EBNF e diagrammi sintattici
* concatenazione G = A B . * alternativa X = A | B . * ripetizione (zero o +) Y = { A } . * opzionalita’ D = [ d ] . * raggruppamento X = ( a | b ) c . B A G B X A A Y d D b X a c

127 identificatori generalmente azioni ed oggetti di un programma sono indicate riportando un "nome" (identificatore), l'identificatore e' una categoria sintattica (simbolo non terminale), con la grammatica: Identifier = Letter{Letter|Digit| "_" }. Letter = "A" | "B" | "C" | "D" ... "Y" | "Z" | "a" | "b" .. "y" | "z" . Digit = "0" | "1" | .... | "8" | "9" . per cui sono legali a FondamentiDiQualcosa media_pesata ScartoQuadrMed ImportoLordo31 distanza8 volumeTotale3 Tasse1995 X2 abcdefghijklmnopqrstuvwxyz_legale_ma_brutto ma NON sono legali: 7corsi ieri+oggi/domani 2hmeljak Mene.Furlan x2(lordo) 4$ piu’o_meno abra-cadabra 3 "Dina Carmela Rossi Coslovich de Cabbage von Bruegge"

128 usare sempre identificatori "auto-esplicativi"
per quanto riguarda gli identificatori, e' facile capire come si scrive un identificatore legale (e il compilatore non accetta identificatori non legali) - MA NON BASTA: ATTENZIONE: usare sempre identificatori "auto-esplicativi" quindi vanno bene distanza_massima totale_lordo VotoDiLaurea PesoNetto altezza media_voti_test_FI2 posizioneX invece NON vanno bene (per un buon programma) : x3 b sx8b quasi censura provvisorio schifo oggi_piove mio nebbia parolaccia_e_peggio (ho tolto le parolacce ;-)

129 esercizio a) scrivere una grammatica per "frasi" del tipo:
cout << " a = " << a; cout << k+2 << endl; cout << endl; cout <<'.'; cout << k << ' ' << l << ' ' << m << ' ' << n ; ... b) scrivere una grammatica per "frasi" del tipo: while ( k<6 ) { k=k+1; } while ( k>0 ) { z = z * k; k=k-1; } while ( k != n ) { n=n-1; k=k+1; } while ( a < 11 ) { n=n+a; a=a+2; p=p+n+a; }

130 esercizio a) scrivere una grammatica per "frasi" del tipo:
cout << " a = " << a; cout << k+2 << endl; cout << endl; cout <<'.'; cout << k << ' ' << l << ' ' << m << ' ' << n ; ... si riconosce la struttura - dove X sta per "espressionex" frase = cout "<<" X { "<<" X } ";" . X = CostStringa | CostCarattere | Espressione . dove CostCarattere = " ' " Carattere " ' " . Stringa = "\"" Carattere "\"" . Espressione = Term { OpereAdd Term } . Term = ...

131 esercizio b) scrivere una grammatica per "frasi" del tipo:
while ( k<6 ) { k=k+1; } while ( k>0 ) { z = z * k; k=k-1; } while ( k != n ) { n=n-1; k=k+1; } while ( a < 11 ) { n=n+a; a=a+2; p=p+n+a; } ... S= "while" "(" EB ")" "{" SIS "}" . EB = E1 OP E2 .

132 grammatica di una costante di tipo intero / Pascal
DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" . UNSIGNED_INTEGER = DIGIT {DIGIT} . da cui: ... sono legali costanti intere senza segno: 1, 33, , 0, 1995, 210 ecc non sono legali costanti intere senza segno: -1 3, X H $1AFF # (perche’ ... )

133 il C++ prevede molti tipi di costanti di tipo numero intero:
long int L -2L unsigned int U 77U unsigned long 123UL UL float E-1 double E-300 long double L esadecimale xFF (un byte) ottale (un byte, 10 in ottale) esercizio: sintassi costante float ?

134 virgola mobile Pascal, il C++ e' piu' permissivo ...
DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" . UNSIGNED_INTEGER = DIGIT {DIGIT} . SIGN = "+" | "-" . SCALEFACTOR = "E" [ SIGN ] DIGIT { DIGIT } . UNSIGNED_REAL = DIGIT {DIGIT} "." [ {DIGIT} ] [ SCALEFACTOR] . da cui ad es. le costanti seguenti sono legali: , 123E0, 1.0, 2., E+003, E-37, 5.5E+37 mentre le costanti seguenti no: , E E-3.5 (perche’ ?)

135 virgola mobile in C in "C": f_const = i_part "." f_part e_part | i_part "." f_part | i_part "." | "." f_part | i_part e_part | quindi: E23 oppure oppure oppure .2 oppure 77E66 sono quindi legali anche costanti del tipo: 12E12 oppure 3. meglio scrivere pero’: E

136 EBNF definita in EBNF ancora un esempio: definizione della stessa EBNF scritta in EBNF: (Niklaus Wirth, CACM, 1977, vol.20, n.11) : Syntax = { Production } . [leggi: una sintassi e’ data da zero o piu’ produzioni ] Production = Identifier "=" Expression "." . [una produz e’ data da un identificatore seguito dal simbolo = seguito da un’espressione seguita da un punto] Expression = Term { "|" Term } . [un’espress. e’ data da uno o piu’ termini separati dal simbolo "|" ] Term = Factor { Factor } [uno o piu’ fattori ] Factor = Identifier | Literal | "(" Expression ")" | "[" Expression "]" | "{" Expression "}" .

137 ripeto la definizione della EBNF scritta in EBNF:
Syntax = { Production } . Production = Identifier "=" Expression "." . Expression = Term { "|" Term } . Term = Factor { Factor } . Factor = Identifier | Literal | "(" Expression ")" | "[" Expression "]" | "{" Expression "}" . da aggiungere: (nota l’uso del delimitatore di stringa " nella stringa stessa, es: stringhe di 5 carateri: "12345", "a""a""a", "1""345" ) Literal = """" Character { Character } """" . Identifier = Letter { Letter | Digit } . Letter = "A" | "B" | "C" | "D" ... | "Z" | "a" | "b" .. | "z" . Digit = "0" | "1" | "2" | "3" | "4" | .... | "8" | "9" . Special = " " | "." | "," | "!" | """" | "+" | ... ";" . Character = Letter | Digit | Special .

138 Syntax = { Production } . ripeto la definizione della EBNF scritta in EBNF
Production = Identifier "=" Expression "." . Expression = Term { "|" Term } . Term = Factor { Factor } . Factor = Identifier | Literal | "(" Expression ")" | "[" Expression "]" | "{" Expression "}" . Literal = """" Character { Character } """" . Identifier = Letter { Letter | Digit } . Letter = "A" | "B" | "C" | "D" ... | "Z" | "a" | "b" .. | "z" . Digit = "0" | "1" | "2" | "3" | "4" | .... | "8" | "9" . Special = " " | "." | "," | "!" | """" | "+" | ... ";" . Character = Letter | Digit | Special . es.: A = B | xA | C A produce uno dei tre termini: B oppure xA oppure C; xA e’ un termine con due fattori x e A.

139 1) esercizio sulle grammatiche
riscrivere S -> S + T S -> T T -> T * F T -> F F -> a F -> b in BNF, es: <S> ::= <T> in EBNF, al posto di : S = T | S + T evito la ricorsione per indicare una ripetizione, utilizzando { } : S = T { "+" T } .

140 1) esercizio grammatiche,con la Backus Naur Form:
S -> S + T S -> T T -> T * F T -> F F -> a F -> b soluzione: <S> ::= <T> | <S> + <T> <T> ::= <F> | <T> * <F> <F> ::= a | b

141 1) esercizio grammatiche, Extended BackusNaur Form
S -> S + T S -> T T -> T * F T -> F F -> a F -> b soluzione: S = T { "+" T } . T = F { "*" F } . F = "a" | "b" .

142 2) esercizio grammatiche riscrivere in BNF e in EBNF:
( V = {E,T,F,A,M,V,C}+T, T = {a,b, 1,2,3,0, (, ) }, P= { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p5: M -> * | /; p6: V -> a | b; p7: C -> 0 | 1 | 2 | 3; } ) segue soluzione

143 2) esercizio grammatiche riscrivere in BNF e in EBNF:
( V = {E,T,F,A,M,V,C}+T, T = {a,b, 1,2,3,0, (, ) }, P= { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p5: M -> * | /; p6: V -> a | b; p7: C -> 0 | 1 | 2 | 3; } ) BNF Backus Naur Form: p1: <E> ::= <T> | <T><A><E> p2: <T> ::= <F> | <F><M><T> p3: <F> ::= <V> | <C> |(<E>) p4: <A> ::= + | - p5: <M> ::= * | / p6: <V> ::= a | b p7: <C> ::= 0 | 1 | 2 | 3

144 2) esercizio grammatiche riscrivere in BNF e in EBNF:
( V = {E,T,F,A,M,V,C}+T, T = {a,b, 1,2,3,0, (, ) }, P= { p1: E -> T | T A E; p2: T -> F | F M T; p3: F -> V | C | ( E ); p4: A -> + | -; p5: M -> * | /; p6: V -> a | b; p7: C -> 0 | 1 | 2 | 3; } ) EBNF Extended BackusNaur Form p1: E = T { A E }. p2: T = F { M T }. p3: F = V | C | "(" E ")" . p4: A = "+" | "-". p5: M = "*" | "/". p6: V = "a" | "b". p7: C = "0"|"1"|"2"|"3".

145 e con i diagrammi sintattici ? - ricorda:
* concatenazione G -> A B B A G * opzionalita’: D -> [ d ] d D * alternativa : X -> A | B B X A * ripetizione(0,1,2,..): Y -> { A } A Y * raggruppamento: X -> ( a | b ) c b X a c

146 esercizio diagrammi sintattici
S -> S + T S -> T T -> T * F T -> F F -> a F -> b riscrivere questa grammatica con i diagrammi sintattici... conviene ricordare la versione EBNF: S = T { "+" T } . T = F { "*" F } . F = "a" | "b" .

147 esercizio diagrammi sintattici
dalla versione EBNF, che ripetiamo: S = T { "+" T } T = F { "*" F } . F = "a" | "b" si ottiene : esercizio: S -> S + T S -> T T -> T * F T -> F F -> a F -> b riscrivere la grammatica con i diagram- mi sintattici... S T + T T F * F F "a" "b"

148 scrivere una grammatica che produce frasi del tipo:
esercizio: scrivere una grammatica che produce frasi del tipo: Oggi alle ore 13 telefona Mario. Alle 17 chiama Heinz. Oggi scrive Niki. Domani alle 23 canta Natasha. Oggi dorme Hassan. Alle 12 mangia. Oggi telefona Mirza. Scrive Momoko. Domani chiama. ( :-( da esame scritto di FI :-)

149 grammatica che produce frasi del tipo:
Oggi alle ore 13 telefona Mario Alle 17 chiama Heinz. Oggi scrive Niki Domani alle 23 canta Natasha. Oggi dorme Benny Alle 12 mangia Oggi telefona Mirza. Scrive Momoko Domani chiama Quando dorme Diego. e' facilmente riconoscibile la struttura: GIORNO TEMPO VERBO NOME dove solo VERBO appare sempre, quindi: S = [ GIORNO ] [TEMPO] VERBO [NOME] . con GIORNO = "Oggi" | "Domani" TEMPO = "alle" [ "ore" ] N . N = "12" | "13" | "17" | "23" . NOME = "Mario" | "Heinz" | "Niki" | "Natasha" | "Hassan" | "Mirza" | "Momoko" | "Diego" .

150 Scrivere una grammatica (produzioni) che produce frasi:
for( k=1; k<=77; k++ ) Istruzione; for( k=1, j=2; ! finito; k++, j--) Istruzione; for( ;; n-- ) Istruzione; for( ;; ) Istruzione; note: #) usare i simboli non terminali tipo: EspressioneBooleana, Espressione, Variabile, Istruzione (che si assumono definiti altrove!) e i terminali "for" "=" ";" "," "!" #) specificare in dettaglio le 3 parti di controllo del for che stanno tra parentesi ( ) dopo il simbolo "for". #) dare almeno un esempio di derivazione di un for non vuoto. (:-( Test FI2 1/4/ :-)

151 for( k=1; k<=77; k++ ) Istruzione;
for( k=1, j=2; ! finito; k++, j--) Istruzione; for( ;; n-- ) Istruzione; for( ;; ) Istruzione; (usare i simboli non terminali tipo: EspressioneBooleana, Espressione, Variabile, oltre al simbolo non terminale Istruzione) soluzione: una grammatica (semplificata) in EBNF per la frase for: FOR = "for" "(" INI ";" TEST ";" INCR ")" ISTRUZ ";" (il for e' fatto da 4 parti, tre stanno tra parentesi separate da ";" ... ) INI = [ ASSEGNA { "," ASSEGNA } ] . (zero o piu' istruz. di assegnaz) TEST = [ EspressioneBooleana ] (puo'essere omesso) INCR = [ INC1 { "," INC1 } ] . (puo'essere omesso) dove: ASSEGNA = Variab "=" Espressione (frase di assegnazione) INC1 = INCDEC Variab | Variab INCDEC | ASSEGNA . INCDEC = "++" | "--" .

152 esercizio: definire (almeno in parte) la sintassi del main program, (header, instr_sequence, block, ...)

153 fine introduzione alle grammatiche


Scaricare ppt "i linguaggi di programmazione sono un caso particolare dei"

Presentazioni simili


Annunci Google