Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. ·...

64
LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi Si definisce Alfabeto un qualunque insieme finito di Simboli e si indica con (sigma). Esempio se ={0,1} allora ‘0101’ è una parola nell’alfabeto. Tra tutte le parole dell’alfabeto vi è una parola vuota: essa si indica con ε Una Stringa è una successione finita di simboli dell’alfabeto. Def.: Sia un alfabeto allora: 1. ε è una stringa su ; 2. Se x è una stringa su allora anche xa è una stringa su a 3. Non c’è altro modo per definire la stringhe. L’insieme delle stringhe su un alfabeto si indica con * dove è l’alfabeto. Indichiamo con + =*\{ε}. Indichiamo con “stringa” oppure “parola” la stessa cosa. Se a possiamo definire delle stringhe particolari: a 0 = ε per definizione, a n = a n-1 a mediante operazione di concatenazione Operazioni su stringhe: Concatenazione x,y -> xy Es. ={a,b} x=aabb y=ab xy=aabbab Inversione x -> x R Es. x=abaab x R =baaba ε R = ε (xa) R = ax R Prefisso. Se x,y * allora x è un prefisso di xy. Il prefisso è proprio se x xy Suffisso. Se x,y * allora y è un prefisso di xy. Sottostringa. Se x,z,y * allora z è una sottostringa di xzy. E’ propria se z (xzy) Lunghezza di una stringa. La lunghezza di una stringa è una funzione | | : * -> Ν tale che | ε | = 0; | ax | = 1 + | x | x * e a ; Linguaggi. Se è un alfabeto, allora L * è un linguaggio su . Se L * è un linguaggio, diciamo che gode della proprietà prefissa (suffissa) se x,y L x non è prefisso (suffisso) di y. Anche l’insieme vuoto e l’insieme formato da solo ε sono linguaggi. {} *; {ε} *; Es. L = {a i b: i 1} gode della proprietà prefissa perché c’è la b alla fine. Non gode della proprietà suffissa perché le parole più piccole sono suffissi di quelle più grandi. Operazioni sui linguaggi. Sono le stesse operazione definite sugli insieme , , \ ed inoltre concatenazione o prodotto L 1 * 1 L 2 * 2 L 1 L 2 ={xy: xL 1 , yL 2 } Chiusura di Kleene di L. Tale chiusura si indica con L* 1. L°={ε} 2. L n+1 = L L n con n 0 3. L* = U 0 n L n dove L* è un linguaggio Chiusura positiva di L. Tale chiusura si indica con L + = 1 n L n L* è un linguaggio Si ha che L + =LL*=L*L ed L*=L + {ε} Omomorfismi tra linguaggi. Sia h:1 -> * 2 a partire da questa funzione è possibile costruire l’omomorfismo indotto da h su * 1 che è definito come h: * 1 -> * 2 tali che h(ε)=ε e h(xa)=h(x)h(a) x * 1 , a 1 Es. 1 ={0,1} 2 ={a,b} O -> a h = h:1 -> * 2 costruisco h:* 1 -> * 2 1 -> bb h(001)=h(00)h(1)=h(00)bb=h(0)h(0)bb=aabb l’omomorfismo conserva l’operazione di concatenazione Supponiamo di avere: h:* 1 -> * 2 L * 1 indichiamo con h(L) l’immagine secondo l’omomorfismo h di L allora h(L)={h(x):x ε L} Se ho un omomorfismo h:* 1 -> * 2 posso definire un omomorfismo inverso h -1 (y)={x * 1 : h(x)=y} * 1 . Si può definire l’omomorfismo inverso su un insieme: sia L * 2 allora h -1 (L)= U L I(I)={ x∈∑* 1 :h(x)L}. Definisco: h -1 (a*) dove a* è l’insieme su cui definisco la chiusura di Kleene (a*={a}*) a*={ε}{a}{aa}… nell’esempio precedente si ha h -1 (a*)={0,1}* infatti h(001)=aaa, h(01)=aa etc.etc. Es. h(0)=a h(1)=ε h -1 (ε)=1* h -1 (a)=1*01* Rappresentazione di linguaggi: I linguaggi finiti sono facili da rappresentare. I linguaggi infiniti si rappresentano tramite due possibili metodi: 1) Metodo generativo: si hanno delle regole per generare il linguaggio (grammatica); 2) Metodo riconoscitivo: cioè, la macchina (es. macchina di Touring) riceve in input una stringa ed è in grado di riconoscere se appartiene al linguaggio. Nel primo metodo vengono utilizzate le grammatiche di Chomsky utili per tradurre in codice da un linguaggio ad un altro.

Transcript of Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. ·...

Page 1: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi

• Si definisce Alfabeto un qualunque insieme finito di Simboli e si indica con ∑ (sigma). Esempio se ∑={0,1} allora ‘0101’ è una parola nell’alfabeto. Tra tutte le parole dell’alfabeto vi è una parola vuota: essa si indica con ε

• Una Stringa è una successione finita di simboli dell’alfabeto. • Def.: Sia ∑ un alfabeto allora:

1. ε è una stringa su ∑; 2. Se x è una stringa su ∑ allora anche xa è una stringa su ∑ ∀ a ∈ ∑ 3. Non c’è altro modo per definire la stringhe.

L’insieme delle stringhe su un alfabeto si indica con ∑* dove ∑ è l’alfabeto. Indichiamo con ∑+=∑*\{ε}. Indichiamo con “stringa” oppure “parola” la stessa cosa. Se a ∈ ∑ possiamo definire delle stringhe particolari: a0 = ε per definizione, an = an-1 • a mediante operazione di concatenazione

• Operazioni su stringhe: Concatenazione x,y -> xy Es. ∑={a,b} x=aabb y=ab xy=aabbab Inversione x -> xR Es. x=abaab xR =baaba εR = ε (xa)R = axR

• Prefisso. Se x,y ∈ ∑* allora x è un prefisso di xy. Il prefisso è proprio se x ≠ xy • Suffisso. Se x,y ∈ ∑* allora y è un prefisso di xy. • Sottostringa. Se x,z,y ∈ ∑* allora z è una sottostringa di xzy. E’ propria se z ≠ (xzy) • Lunghezza di una stringa. La lunghezza di una stringa è una funzione | | : ∑* -> Ν tale che

| ε | = 0; | ax | = 1 + | x | ∀ x ∈ ∑* e ∀ a ∈ ∑; • Linguaggi. Se ∑ è un alfabeto, allora L ⊆ ∑* è un linguaggio su ∑. Se L ⊆ ∑* è un

linguaggio, diciamo che gode della proprietà prefissa (suffissa) se ∀ x,y ∈ L x non è prefisso (suffisso) di y. Anche l’insieme vuoto e l’insieme formato da solo ε sono linguaggi. {} ⊆ ∑*; {ε} ⊆ ∑*; Es. L = {aib: i ≥ 1} gode della proprietà prefissa perché c’è la b alla fine. Non gode della proprietà suffissa perché le parole più piccole sono suffissi di quelle più grandi.

• Operazioni sui linguaggi. Sono le stesse operazione definite sugli insieme ∩, ∪, \ ed inoltre concatenazione o prodotto L1 ⊆ ∑*1 L2 ⊆ ∑*2 L1L2={xy: x∈L1, y∈L2}

• Chiusura di Kleene di L. Tale chiusura si indica con L* 1. L°={ε} 2. Ln+1 = L ⋅ Ln con n ≥ 0

3. L* = U0≥n

Ln dove L* è un linguaggio

• Chiusura positiva di L. Tale chiusura si indica con L+ = ∪≥1n

Ln L* è un linguaggio

Si ha che L+=LL*=L*L ed L*=L+∪{ε}

• Omomorfismi tra linguaggi. Sia h:∑1 -> ∑*2 a partire da questa funzione è possibile costruire l’omomorfismo indotto da h su ∑*1 che è definito come h: ∑*1 -> ∑*2 tali che h(ε)=ε e h(xa)=h(x)h(a) ∀ x ∈ ∑*1 , ∀ a ∈ ∑1 Es. ∑1={0,1} ∑2={a,b} O -> a h = h:∑1 -> ∑*2 costruisco h:∑*1 -> ∑*2 1 -> bb h(001)=h(00)h(1)=h(00)bb=h(0)h(0)bb=aabb l’omomorfismo conserva l’operazione di concatenazione Supponiamo di avere: h:∑*1 -> ∑*2 L ⊆ ∑*1 indichiamo con h(L) l’immagine secondo l’omomorfismo h di L allora h(L)={h(x):x ε L} Se ho un omomorfismo h:∑*1 -> ∑*2 posso definire un omomorfismo inverso h-1(y)={x ∈ ∑*

1 : h(x)=y} ⊆ ∑*1. Si può definire l’omomorfismo inverso su un insieme: sia L ⊆ ∑*2 allora h-1(L)= U

LI∈

(I)={

x∈∑*1:h(x)∈L}. Definisco: h-1(a*) dove a* è l’insieme su cui definisco la chiusura di Kleene (a*={a}*) a*={ε}∪{a}∪{aa}∪… nell’esempio precedente si ha h-1(a*)={0,1}* infatti h(001)=aaa, h(01)=aa etc.etc. Es. h(0)=a h(1)=ε h-1(ε)=1* h-1(a)=1*01*

• Rappresentazione di linguaggi: I linguaggi finiti sono facili da rappresentare. I linguaggi

infiniti si rappresentano tramite due possibili metodi: 1) Metodo generativo: si hanno delle regole per generare il linguaggio (grammatica); 2) Metodo riconoscitivo: cioè, la macchina (es. macchina di Touring) riceve in input una

stringa ed è in grado di riconoscere se appartiene al linguaggio.

Nel primo metodo vengono utilizzate le grammatiche di Chomsky utili per tradurre in codice da un linguaggio ad un altro.

Page 2: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Grammatica. Definiamo la grammatica G = (N, ∑, P, S) dove N ∩ ∑ = ∅ o N è l’insieme di simboli non terminali (finito); o ∑ è l’insieme dei simboli terminali (finito); o P è l’insieme delle regole di produzione della grammatica; o S ∈ N, è il simbolo iniziale.

Dalla grammatica si genera un linguaggio a partire da ∑

• Composizione di P. P ⊆ { (N ∪ ∑)* N(N ∪ E)*} x {(N ∪ ∑)*}

P è un insieme di coppie del tipo (a,b) dove: a ∈ { (N ∪ ∑)* N(N ∪ E)*} e b ∈ {(N ∪ ∑)*} Es. N={S,A} ∑={0,1} S -> oA1 Il termine a sinistra deve contenere almeno un simbolo P = oA -> ooA1 non terminale. A -> ε

• Forme proposizionali di una grammatica. Sia G=(N, ∑, P, S) una grammatica

1) S è una forma preposizionale 2) Se αβγ è una forma preposizionale e β->S è un P allora αSγ è una forma

preposizionale.

Nel nostro esempio le forme preposizionali sono: S, ε oA1, ooA11, …

• Si definisce frase generata da G (o parola) una forma proposizionale che ∈ ∑*. Il linguaggio generato da G è l’insieme di parole di G.

• Derivazioni di una grammatica. o Derivazione diretta (⇒

G

)

Se a,γ ∈ (∑ ∪ N)* e β -> S è un P allora si dice che αβγ produce direttamente αSγ e si scrive αβγ⇒

G

αSγ

• Derivazione non banale (+

⇒G

)

Si dice che α deriva β in modo non banale se ∃ α0, α1, …, αn ∈ (N ∪ ∑)* con n ≥ 1 tali

che α=α0⇒G

α1⇒G

α2 … ⇒G

αn=β e si scrive α+

⇒G

β

• Derivazione (⇒*

G)

Si dice che α deriva β se ∃ α0, α1, …, αn, n ≥ 0 tali che α=α0⇒G

α1⇒G

α2 … ⇒G

αn=β e

si scrive α ⇒*

G β

• Derivazione di lunghezza k (⇒k

G)

Si dice che α deriva β in k passi se ∃ α0, α1, …, αn,yali che α=α0⇒G

α1⇒G

α2 …

⇒G

αk=β. E si scrive α ⇒k

• Linguaggio generato da G, L(G).

L(G)={ω ∈ ∑* : S ⇒*

Gω}

E’ l’insieme delle stringhe di ∑* che possono essere derivate dal simbolo iniziale, S, della

grammatica. Nel nostro esempio: S -> oA1 -> ooA11 -> oo11 S ⇒*

G 0011 ∈ L(G)

S-> oA1 -> ooA11 -> oooA111 -> ooo111 ∈ L(G) possiamo concludere che il linguaggio di questa grammatica è L(G)= L(G)={on1n : n ≥ 1 }

Page 3: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• La derivazione diretta può essere considerata come una relazione definita su stringhe costituite da simboli terminali e non terminali. αβγ ⇒

G

αSγ se B->S posso considerarla come

una relazione biunivoca. Essa si può considerare come una relazione dell’insieme (N ∪ ∑)* • La derivazione non banale è la chiusura transitiva della derivazione diretta, mentre la

derivazione può essere considerata come la chiusura transitiva e riflessiva della derivazione diretta.

• Data una relazione R: Chiusura transitiva di R (R+) è la più piccola relazione che soddisfa la proprietà:

1. ∀ (a,b) ∈ R -> (a,b) ∈ R+; 2. (a,b) ∈ R (b,c)∈ R -> (a,c) ∈ R

• Chiusura transitive e riflessiva di R (R*) è la più piccola relazione ed è sia riflessiva

che transitiva: R*=R+ ∪ {(a,a) ∀ a ∈ S} Convenzione: i simboli non terminali si scrivono maiuscoli mentre quelli terminali minuscoli. Esempi di derivazione: S -> aSBC | abC CB -> BC bB -> bb bC -> bc cC -> cc S => aSBC => aabCBc => aabBCc => aabbCC => aabbcC => aabbcc si può dimostrare che: L(G)={an bn cn : n ≥ 1}

• Classificazione delle grammatiche: Le grammatiche vengono classificate a seconda delle loro

regole di produzione: o Lineari a destra. G è lineare a destra se ogni produzione è del tipo A -> xB oppure

A -> X con A,B ∈ N, x ∈ ∑* o Context-Free. G è context-free se ogni produzione è del tipo: A -> α con A ∈ N

α ∈ ( N ∪ ∑ )* o Context-sensitive. G è context-sensitive se ogni produzione è del tipo: α -> β con

α,β ∈ ( N ∪ ∑*) e |α| ≤ |β| o Unrestricted. G è unrestricted se non ci sono regole di produzione.

Se G è lineare a destra allora è context-free. Se G è context-free senza ε produzione allora G è context-sensitive. Se G è context-sensitive allora è unrestricted

• Classi di Grammatiche:

o La classe delle grammatiche lineari a destra formano la classe di linguaggi regolari. o Le grammatiche context-free formano la classe di linguaggi context-free. o Le grammatiche context-sensitive formano la classe di linguaggi context-sensitive. o La classe della grammatiche unrestricted formano la classe dei linguaggi

ricorsivamente enumerabili.

• Il riconoscitore. E’ una procedura per riconoscere un linguaggio. Il sistema fisico è fatto da un nastro infinito di input e una testina collegata al “controllo stati finiti”. La testina di input può leggere e scrivere da e su una sola locazione alla volta. Si distinguono riconoscitori:

o Unidirezionali: la testina può muoversi solo a destra o Bidirezionali: la testina può muoversi sia a destra che a sinistra.

Il nastro di input può essere: read-only; read-write. Il riconoscitore utilizza una memoria secondaria che può essere un nastro, uno stack (automi a pila). Il controllo a stati finiti è un programma costituito da:

• Stati; • Un insieme di regole che ci dicono come deve muoversi la testina in relazione

allo stato e al carattere letto Ogni passo del riconoscitore consiste nel:

• Scrivere un simbolo nel nastro di input; • Sopstare la testina di un passo dx o sx e lasciarla ferma ; • Cambiare lo stato • Cambiare il contenuto della memoria ausiliaria;

• Configurazioni: • Per descrivere una configurazione istantanea bisogna conoscere:

o Contenuto del nastro di input e la posizione testina; o Stato (del riconoscitore); o Contenuto della memoria ausiliaria;

• Configurazione iniziale: o Il riconoscitore è in uno stato detto iniziale (stato particolare); o La testina è nella prima casella non vuota ovvero sul primo simbolo del

nastro; o La memoria è in una condizione iniziale predefinita;

Page 4: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Un riconoscitore accetta una stringa ω se, partendo da una configurazione iniziale, con ω scritta sul nastro di input, esiste una successione di passi del riconoscitore che porta ad una configurazione finale. Configurazione finale:

o Stato finale; o Testina ha letto tutto l’input; o Condifizione finale predefinita;

Linguaggio accettato (definito o riconosciuto) da un riconoscitore: è l’insieme delle stringhe accettate dal riconoscitore

• Un riconoscitore può essere:

o Deterministico: se da una configurazione posso eseguire, al più, una mossa; o Non Deterministico: se da una configurazione posso eseguire una o più mosse.

• Esiste una corrispondenza tra grammatiche e riconoscitori:

o La classe dei linguaggi generati da una grammatica coincide con la classe corrispondente dei linguaggi riconosciuti da un riconoscitore;

Grammatiche lineari a destra <-> Automi a stati finiti (deterministici – unidirezionali) Grammatiche context-free <-> Automi a pila (non deterministici – unidirezionali) Grammatiche context-sensitive <-> Linear Bounded automata (non determ. – bidirezionali) Grammatiche unrestricted <-> Macchine di Touring (non deterministiche – bidirezionali)

• Linguaggi Regolari. Sono linguaggi generati da grammatiche lineari a destra, accettati da

Automi a Stati Finiti. • Espressioni regolari. Sia ∑ un alfabeto, definiamo le espressioni regolare su ∑ come:

o Φ è un’espressione regolare o ε è un’espressione regolare o a è un’espressione regolare ∀ a ∈ ∑ o Se α e β sono espressioni regolari allora anche α ∪ β, αβ, α* sono espressioni

regolari Se α è un espressione regolare su ∑, ad α posso associare un insieme ⊆ ∑* α -> < α > ⊆ ∑* che chiamo “insieme denotato da α”

• Definizione di un insieme denotato (denotato da α) 1. <Φ> = ∅; 2. <ε> = {ε} 3. <a> = {a} 4. Se <α> e <β> sono gli insiemi denotati rispettivamente da α e β allora:

<α ∪ β> = <α> ∪ <β>; <α β> = <α>⋅<β>; <α*>=<α>* I linguaggi regolari sono quei linguaggi per cui esiste un’espressione regolare che la denota. Non tutti i linguaggi hanno un’espressione regolare che li denota. Quindi per definizione: L ⊆ ∑* è regolare se ∃ α, espressione regolare su ∑ tale che L=<α> La classe dei linguaggi regolari è chiusa rispetto all’unione, alla concatenazione e chiusura di Kleene.

• Automi a Stati Finiti. Sono riconoscitori unidirezionali, deterministici, senza memoria ausiliaria. Il fatto che sia unidirezionale implica che ad ogni ossa si deve necessariamente spostare la testina. m=(Q, ∑, δ, qo, F)

o Q è l’insieme degli stati (finito) o ∑ è l’alfabeto (finito) o δ è la funzione di transizione dell’automa o qo ∈ Q è lo stato iniziale o F ⊆ Q è l’insieme degli stati finali.

δ è la funzione di transizione dell’automa definita come segue: δ: Q x ∑ -> Q Definiamo inoltre δ*: Q x ∑* -> Q definita come (è un estensione di δ che mi da’ lo stato che si ha fra uno stato precedente e una stringa):

1. δ*(q,ε) = q ∀ q ∈ Q 2. δ*(q,Xa)=δ(δ*(q,X),a) ∀ x ∈ ∑*, ∀ a ∈ ∑

Quindi δ=δ* (coincide) per a ∈ ∑ : δ* è un estensione di δ.

• Sia m un automa a stati finiti: Il linguaggio accettato da m è L(m)={ω ∈ ∑*: δ*(q0,ω)∈F} La funzione δ è definita:

Esempio: ∑={a,b} Q={q1, q2, q3, q4} F={q3} Calcoliamo δ*(q1, ab)=δ(δ*( q1, a),b=δ(q2,b)=q3 ∈ F. Quindi ab è riconosciuto dall’automa e quindi ab ∈ L(m).

• Una rappresentazione tramite diagramma di transizione è data da un grafo i cui nodi sono

etichettati con elementi di Q e gli archi sono etichettati con elementi di ∑. Lo stato iniziale è dato da una freccia entrante mentre quello finale è dato da un doppio cerchio. Per sapere se una stringa appartiene a L(m) basta leggere il cammino che parte da q1. Se

δ a B q1 q2 q4 q2 q2 q3 q3 q4 q3 q4 q4 q4

Page 5: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

arrivo ad uno stato finale allora ∈ L(m). Serve a riconoscere quali parole fanno parte di L(m). E’ un modo di rappresentare un automa. Es.:

Il linguaggio accettato da questo automa è: L(m)={anbm:n,m ≥ 1}

• L si dice che è un linguaggo AF-regolare se ∃ un automa m, a stati finiti, tale che L=L(m). Esempio L(m)={anbm:n,m ≥ 1} non è AF-regolare (sarà dimostrato successivamente)

• Automi a stati finiti non deterministici (AFN). Sia m(Q, ∑, δ, q0, F) un automa a stati finiti. Definiamo:

o δ: Q x ∑ -> Pow(Q) o δ*: Q x ∑* -> Pow(Q) definite come

1. δ*(q,ε) = {q} ∀ q ∈ Q.

2. δ*(q,Xa)= ),(* xqp δ∈

U δ(P,a) ∀ q ∈ Q , a ∈ ∑, x ∈ ∑*

Il linguaggio L(m) accettato da un automa non deterministico sarà: L(m)={ω ∈ ∑*: δ*(q0, ω}∩F ≠∅} Es di AFM: m=({ q0, q1, q2, q3, q4}, {a,b,c}, δ, q0 ,{q4})

Diagramma degli stati:

Il linguaggio di questo automa è dato da tutte le parole che soddisfano una delle seguenti condizioni:

- se finiscono con a devono contenere a - se finiscono con b devono contenere b - se finiscono con c devono contenere c L(m)={ a,b,c}*a{a,b,c}*a∪{a,b,c}*b{a,b,c}*b∪……………. Vediamo formalmente se aca ∈ L(m):

δ*(q0, aca)=),(* acqp oδ∈

U δ(p,a) => δ*(q0, ac)=),(* aqr oδ∈

U δ(r,c) => δ*(q0, a)= δ(q0, a)={q0, q1}

δ*(q0, ac) = δ(q0, c) ∪ δ(q1, c)={q0, q3}∪{q1}={q0,q1,q3} δ*(q0, aca)= δ(q0, a) ∪ δ(q1, a) ∪ δ(q3, a)={q0, q1}∪{q1,q4}∪{q3}={q0,q1,q3,q4} Quindi aca ∈ L(m) poiché δ*(q0,aca) ∩{q4}≠∅

• Si dice che L è un linguaggio AFN-regolare se esiste un automa m, AFN, tale che L=L(m) • Teorema. Se L è AF-regolare allora è anche AFN-regolare. Dimostrazione:

o Sia m=(Q,∑,δ,q0,F) un AF tale che L=L(m) e sia m=(Q,∑,δ,q0,F) un AFN tali che: δ: Q x ∑ -> Q δ: Q x ∑ -> Pow(Q) definito come δ(q,a)={δ(q,a)}

La tesi allora è banalmente vera.

• Teorema. Se L è AFN-regolare allora è anche AF-regolare. Dimostrazione: Creo il nuovo automa in modo che ogni stato del nuovo automa è corrispondente ad un insieme di stati del primo automa. In questo modo trasformo il modello non deterministico in un modello deterministico: Per ipotesi supponiamo che ∃ m=(Q,∑,δ,q0,F), AFN tale che L=L(m). Costruiamo allora m=(Q,∑,δ,q0,F), AF dove Q=Pow(Q); q0={q0}; F={Qi ∈ Q: Qi ∩ F ≠ ∅} dato che

indico gli elementi di Q con Qi con i=1,2,…,2n δ(Qi,a)=iQq∈

U δ(q,a)

Dimostriamo allora che, con questa definizione si ha che L=L(m). Per far ciò abbiamo bisogno di due lemmi:

δ a b C q0 {q0,q1} {q0,q2} {q0,q4} q1 {q1,q4} {q1} {q1} q2 {q2} {q2,q4} {q2} q3 {q3} {q3} {q3,q4} q4 ∅ ∅ ∅

a

b

b

a

q1

q2

q4

q3

a

b

a,b

q0

q3

q1

q2

q4

a,b,c a,b,c

a,b,c

a,b,c

a

b

c

a

b

c

Page 6: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Lemma 1 (fusione degli stati). Sia R ⊆ Q, allora δ (RQi∈

U Qi,a)= RQi∈

U δ(Qi,a)

Dimostrazione: Sia RQi

Q∈

= U Qi

δ( (RQi∈

U Qi,a)=δ (Q ,a)=Qq∈U δ(q,a)=

ii QqRQ ∈∈UU δ(q,a)=

RQi∈U δ(Qi,a)

• Lemma 2 (estensione di δ): δ(Qi, X)=iQq∈

U δ*(q,x)

Dimostrazione: Dimostriamolo per induzione sulla lunghezza di x: passo base: |x|=0 allora x=ε

δ*(Qi,ε)=Qi=iQq∈

U {q}=iQq∈

U δ*(q,ε)

passo induttivo: |x|≥1, x=ωa con ω ∈ ∑*, a ∈ ∑

δ*(Qi,x)=δ*(Qi,ωa)=δ(δ*(Qi,ω),a)=δ(iQq∈

U δ*(q,ω),a) per ipotesi induttiva

=iQq∈

U δ(δ*(q,ω),a) per definizione del lemma 1

=iQq∈

U),(* ωδ qr∈

U δ(r,a)= iQq∈

U δ*(q,ωa)= iQq∈

U δ*(q,x)

• Possiamo adesso dimostrare il teorema principale mediante questi due lemmi.

Se x ∈ L(m) allora x∈L(m) e viceversa. Infatti x∈L(m) ⇔ δ*({q0},x) ∈ F ⇔}{ 0qq∈

U δ*(q,x) ∈ F ⇔

δ*(q0,x) ∈ F ⇔ δ*(q0,x) ∩ F ≠ ∅ ⇔ x ∈ L(m)

• Automi a stati finiti con ε - transizioni. (Automi con archi etichettati dalla parola ε)

Def. Un automa finito con ε-transizioni, non deterministico è una quintupla: m=(Q,∑,δ,q0,F), dove Q,∑,δ,q0,F sono definiti come negli altri automi mentre δ: δ:Q x(∑ ∪ {ε})-> Pow(Q) definiamo poi δ:Q x ∑* -> Pow(Q) Es. Automa Precedente

• ε-chiusura. Sia q ∈ Q, definiamo ε-chiusura di q (ε-closure(q) l’insieme di tutti gli stati p tali che ∃ un cammino da q a p formato solo da archi etichettati con ε. A questo punto possiamo definire la funzione δ̂

1) δ̂ (q,ε) =ε-closure(q) ∀ q∈Q 2) δ̂ (q,xa)=ε-closure(p) ∀ x∈∑*, a∈∑, q∈Q con p={p:p∈δ(r,a) per qualche r∈ δ̂ (q,x)}

N.B. δ(q,ε) e δ̂ (q,ε) sono diversi

• Sia m un automa finito con ε-transizioni: Si dice che L=L(m) se L(m)={ω∈∑*: δ̂ (q1,ω)∩F≠∅} (Allora L è un linguaggio accettato dall’automa)

• Dato R ∈ Pow(Q) si ha per definizione:

δ(R,a)=RqεU δ(q,a)

δ̂ (R,a)=RqεU δ̂ (q,a)

Es.: ε-chiusura(q2)={q2, q3} ε-chiusura(q3)={q3}

δ̂ ( q2, ε)={ q1, q2, q3} δ̂ ( q2, 0)=ε-closure(δ( δ̂ (q1,ε),0))=ε-closure(δ({q1, q2, q3},0))=

ε-closure(δ({q1,0) ∪ δ(q2, 0) ∪ (q3,0))= ε-closure(δ({q1,0) ∪ δ(q2, 0) ∪ (q3,0))=ε-closure({q1} ∪ ∅ ∪ ∅ ) = ε-closure({q1})={ q1, q2, q3} δ̂ ({q1,01}={q2,q3}

δ 0 1 2 ε q1 {q1} ∅ ∅ {q2} q1 ∅ {q2} ∅ {q3}

q1 q2 q3 ε ε

0 1 2

Page 7: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Teorema. Se L è un linguaggio accettato da un AFN con ε-transizioni, allora è accettato anche da un AFN senza ε-transizioni: Dimostrazione: Sia m=(Q, ∑, δ, q0,F) un AFN con ε-transizioni tale che L=L(m). Costruiamo ora m’=(Q, ∑, δ’, q0, F’) un AFN senza ε-transizioni, dove F ∪ {q0} se ε-closure(q0) ∩ F ≠ ∅ F’= F altrimenti δ’(q,a)= δ̂ (q,a) ∀ q ∈ Q, a ∈ ∑ Si dimostra per induzione che δ’(q0,x)= δ̂ (q0,x) sulla lunghezza di x, infatti:

- passo base |x|=1 ⇒ x=a∈∑ e per def. δ’(q0,a)= δ̂ (q0,a) - passo induttivo |x|=n>1 ⇒ x=ωa con ω∈∑*, a∈∑ δ’(q0,ωa)=δ’(δ’(q0,ω),a)=δ’( δ̂ (q0,ω),a)= ponendo δ̂ (q0,ω)≡P ⊆ Q

(è uguale) = δ’(P,a) = PpεU δ’(p,a)=

PpεU δ̂ (p,a)=

),(ˆ0 ωδε qpU δ̂ (p,a) = δ̂ (q0,ωa)

Dimostriamo adesso che L(m)=L(m’) cioè δ’(q0,ω)∩F’≠∅ ⇔ δ̂ (q0,ω)∩F≠∅ Caso 1: ω=ε

δ’(q0,ε)∩F’≠∅ ⇔ δ̂ (q0,ε)∩F≠∅ e quindi {q0}∩F’≠∅ ma questo è vero per la definizione di F’

Caso 2: ω≠ε δ’(q0,ω)= δ̂ (q0,ω) (⇐) δ̂ (q0,ω)∩F≠∅ (HP) δ’(q0,ω)∩F≠∅ ma F ⊆ F’ e quindi δ’(q0,ω)∩F’≠∅ (TS) (⇒) δ’(q0,ω)∩F’≠∅ (HP) δ̂ (q0,ω)∩F’≠∅ F’=F∩{q0} se δ̂ (q0,ω) contiene un elemento di F diverso da q0 posso dire quindi: δ̂ (q0,ω)∩F≠∅

se δ̂ (q0,ω) contiene solo q0 ∉ F allora: δ̂ (q0,ω)≥ ε-closure(q0) ε-closure(q0)∩F≠∅ quindi δ̂ (q0,ω)∩F≠∅ Da questo risultato segue, allora:

• Teorema. Un Linguaggio L è AF-regolare se e solo se è accettato da un AFN con o senza ε-transizioni. I seguenti teoremi definisci le proprietà di chiusura dei linguaggi AF-regolari

• Teorema. Siano L1, L2, AF-regolari allora L1∪L2 è AF-regolare. Dimostrazione: Per definizione m1=(Q1,∑1,δ1, q’0, F’) AF-regolare: L1=L(m1) ∃ m2=(Q2,∑2,δ2, q’’0, F’’) AF-regolare: L2=L(m2). Posso supporre Q1∩Q2=∅ senza perdere generalità. Costruisco l’automa: m=(Q, ∑, δ, q0, F) AFN con ε-transizioni tale che L1∪L2=L(m) così definito: Q=Q1∪Q2∪{q0} ∑=∑1∪∑2

F=F’∪F’’ δ(q,a)= δ1(q,a) ∀q∈Q1 q0 δ(q,a)= δ2(q,a) ∀q∈Q2 δ(q0,ε)={q0’, q0’’}

E’ quindi dimostrata la tesi. E’ facile dimostrare che L(m)=L1∪L2. Quindi la classe dei linguaggi AF-regolare è chiusa per unione.

• Teorema. Se L è AF-regolare allora L è AF-regolare, dove L è il complementare di L (Se L ⊆

∑* ⇒ L =∑*-L). Dimostrazione: Sia m=(Q,∑, δ, q0, F) un AF tale che L=L(m) si dimostra facilemente che m =( Q,∑, δ, q0,Q-F) accetta L . Quindi la classe dei linguaggi AF-regolari è chiusa per complemento

• Teorema. Siano L1, L2 AF-regolari allora L1∩L2 è AF-regolare. Dimostrazione. Per la regola di

De Morgan si ha L1∩L2= 21 LL ∩ . Quindi, per il teorema precedente si ha la Tesi. Quindi la

classe dei linguaggi AF-regolare è chiusa rispetto alle operazioni di Unione, complementazione e Intersezione.

• Teorema. ∅ e {ε} sono AF-regolari. Dimostrazione Il seguente automa: accetta l’insieme vuoto perchè non ha transizioni Mentre l’automa: accetta la parola vuota {ε} Infatti δ(q0,ε)=q0

• Teorema. Sia n ∈ ∑*, allora {n} è AF-regolare (ogni insieme formato da un’unica parola è AF-

regolare). Dimostrazione: Se n=ε si ha la tesi per il teorema precedente. Se n≠ε allora n=a1a2…an con n≥1 ai∈∑ basta costruire l’automa seguente:

m1

m2

q’0

q’’0

Aggiungo ai due automi uno strato q0con du ε-transizioni che portono agli stati iniziali dei due automi e prendo come F=F’∪F’’

q0 qF

q0 qF ε

q0 qF q1 q2 a1 a2 … a3 an

Page 8: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Teorema Ogni sottoinsieme finito di ∑* è AF-regolare (oppure) Se L è AF-regolare, ogni sottoinsieme di L è AF-regolare. Dimostrazione L={x1,x2,…,xn} con xi∈∑* quindi L={x1}∪…∪{xn} e per I teoremi precedenti si ha la tesi.

• Teorema. Siano L1, L2 AF-regolari allora anche L1⋅L2 è AF-regolare. Dimostrazione:

Sia m1=(Q1,∑1,δ1,q’0,F1) AF tale che L1=L(m1) e m2=(Q2,∑2,δ2,q’’0,F2) AF tale che L2=L(m2). Costruisco un nuovo automa prendendo come stato iniziale quello di m1 e come stati finali quelli di m2. Quindi m=(Q1∪Q2, ∑1∪∑2, δ,q’0,F2) dove δ(q,a)=δ1(q,a) ∀ (q,a)∈ Q1 x ∑1

δ(q,a)=δ2(q,a) ∀ (q,a)∈ Q2 x ∑2

δ(q,ε)=contente q0’’ ∀ q∈F1 Allora L(m)=L(m1)⋅L(m2)=L1⋅L2.

• Teorema. Se L è AF-regolare allora L* è AF-regolare. Dimostrazione: Sia m=(Q,∑,δ,q0,F) AF-regolare (tale che L=L(m)) costruiamo m1=(Q∪{q0’,qF’},∑,δ’,q0’,{qF’}). Supponendo F1={qF’} senza perdere di generalità. Si può osservare che m1 accetta ε E 0,1 o più concatenzioni di parole di L quindi m1 accetta L*. δ’ è una piccola estensione di δ (di vede dal disegno)

• Teorema. Ogni linguaggio regolare è AF-regolare.

Dimostrazione: Se L è regolare, allora L=<α> con α espressione regolare. La dimostrazione si effettua per induzione sul numero n di operatori che compaiono in α.

o Passo base n=0 Allora α=∅ => L={∅} α=ε => L={ε} α=a => L={a} In tutti e tre I casi si ha la tesi

o Passo induttivo. Sia vera la tesi per espressioni regolari che contengono n-1 operatori e dimostraimolo per n. α può essere data da α = β ∪ γ, α = βγ, α = β* con β e γ espressioni regolari con meno di n operatori, quindi per ipotesi induttiva <β> e <γ> sono AF-regolari. Se α = β ∪ γ => <α>=<β ∪ γ> = <β> ∪ <γ> Se α = βγ => <α>=<βγ> = <β> ⋅ <γ> Se α = β* => <α>=<β*>=<β>* In tutti e tre I casi si ha la tesi.

• Esiste un modo per passare da un espressione regolare ad un automa che accetta il linguaggio

regolare denotato da espressione regolare.

Se dimostro questo ho dimostrato che tutti questi linguaggi sono equivalenti:

• Teorema di Kleene. Se L è AF-regolare allora L è regolare. Dimostrazione: Sia m=(Q,∑,δ,q0,F) AF tale che L=L(m) con Q={q0,q1,…,qn} Per dimostrare queso teorema definiscono k

ijR ⊆ ∑*, i,j,k ∈{0,1,…,n} definito come

{a: δ(qi,a)=qj} i≠s 0ijR k

ijR {x ∈ ∑*:δ*(qi,x)=qj} e per ogni prefisso proprio

{a: δ(qi,a)=qj}∪{ε} i=j y≠ε di x se δ*(qi,y)=qe allora e≤k

m1 m2

q0’ q0’’

ε

ε

q0’ q0’ε qf’

m

ε qf’ ε

ε

AF

AFN

AFN con ε-transizioni

Espr. Regol.

Page 9: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Posso scrivere allora: L=Fq j∈U 1+n

ojR =Fq j∈U n

ojR se Q={q1,q2,…,qn}

Gli nijR possono essere definiti in maniera ricorsiva come

kijR =

1−kijR ∪

1−kikR (

1−kkkR )*

1−kkjR

Dimostriano che ∀ i,j,k => kijR è regolare, cioè esiste un espressione regolare che mi

denota questo insieme. Dimostriamo per induzione su k. Passo base: K=0

0ijR è regolare infatti

0ijR ={a1,a2,…,ae} a1∪a2∪…∪ae è un espressione regolare

che denota 0ijR

Passo induttivo.

Supponiamo 1−k

ijR sia regolare. Sia kijr l’espressione regolare che denota

kijR (da trovare) per la definizione ricorsiva essa è data da:

1−kijr ∪

1−kikr (

1−kkkr )*

1−kkjr che denota

1−kijR ∪

1−kikR (

1−kkkR )*

1−kkjR =

kijR

L(m)=Fq j∈

U nijR =

nijR 1

∪nijnR ∪…∪

nijnR . L(m) è regolare perché è l’unione di insiemi

regolari(la classe di linguaggi regolari è chiusa rispetto all’unione)

Se F={qj1,qj2,…,qjn} quindi l’espressione regolare per L(m) sarà α=nijr 1

∪nijr 2

∪…∪nijnr

• Grammatica Regolare. Una grammatica G=(N,∑,δ,P) è regolare se ogni produzione di G è della forma A->aB oppre A->a con A,B∈N (non terminali) a∈∑ (terminali)

• Teorema. Sia L un linguaggio regolare, allora ∃ G, grammatica regolare, tale che L\{ε}=L(G). Dimostrazione: Poichè L è regolare ∃ m=(Q,∑,δ,q1,F) tale che L=L(m) costruisco G=(N,∑,δ,P) dove pongo: N=Q, δ=q1, P l’insieme delle regole così fatte: 1) qi->akqj se δ(qi,ak)=qj 2) qi->ak se δ(qi,ak)∈F G così costruita, è regolare. Dimostriamo che L(G)=L(m). Sia n∈L(m) con n=ai1,ai2,…,aik δ*(q1,n)∈F δ(q1,ai1)=qj1 => q1->ai1qj1 δ(qj1,ai2)=qj2 => qj2->ai2qj2 . . δ(qjk-1,aik)=qjk ∈ F => qjk-1.>aikqjk Quindi una derivazione della grammatca G è la segunete: q1=> ai1 qj1 =>…=> ai1 ai2… aik-1 aik=n Quindi n∈L(G). Il viceversa è banale (non si dimostra)

• Teorema. Se G è una grammatica regolare allora L(G) è un linguaggio regolare.

Dimostrazione: Siano G=(N,∑,δ,P) con N={V1,V2,…,Vk} δ=V1, una grammatica e m=(N ∪ {ω},∑,δ,V1,{ω}) dove : δ1(Vi,a)={Vj:Vi->aVj è in P} ω se Vi->aVj è in P δ2= ∅ altrimenti δ(Vi,a)=δ1(Vi,a)∪δ2(Vi,a) Dimostriamo allora che L(m)=L(G)

Sia n∈L(G) allora ∃ δ*

G⇒ n

Applicando le regole di produzione per derivare n dalla grammatica regolare G V1=δ

G⇒ a1Vi1

G⇒ a1a2Vi2

G⇒ …

G⇒ a1a2…an-1Vin-1

G⇒ a1a2…an

Ma allora, per come è stato definito δ in m si ha che Vi1∈δ(V1,a1) V12∈δ(Vi1,a2) . . ω∈δ(Vin-1,an) δ*(V1,a)=δ*(V1,a1,…,an) contiene ω∈F quindi n∈L(m). Osservazione: Da una grammatica regolare non posso derivare ε

• Def. Una Grammatica G è lineare a destra se ogni produzione di G è del tipo: U->xV oppure

U->X U,V∈N x∈∑*

Page 10: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Teorema. Se G è una grammatica lineare a destra allora L(G) è un linguaggio regolare. Dimostrazione: Dimostriamo che ∃ G’, grammatica regolare, tale che L(G)=L(G’). Per ogni produzione del tipo U ->a1…anV in G introduco in G’ n-1 nuove variabili Z1,Z2,…,Zn-1 e le seguenti produzioni:

U -> a1Z1 Z1 -> a2Z2 . . Zn-2->an-1Zn-1 Zn-1->anV

Analogamente per ogni produzione del tipo U->a1a2…an in G, introduco le nuove variabili V1,V2,…Vn-1 e le nuove regole di produzione: U –> a1V1 V1 -> a2V2 . . Vn-1 -> an Allora tutte le stringhe di L(G’) appartengono anche a L(G).

• Corollario. Sia G una grammatica lineare a destra, allora G è una grammatica regolare. • Corollario. Sia G una grammatica lineare a destra: allora L(G) è regolare. • Lemma di Iterazione PUMPING LEMMA.

Sia m un automa a stati finit con n stati. Allora ∀ x ∈ L(m) con |x|≥n ∃ u,v,ω ∈∑* con v≠ε tali che:

1) x=uvω 2) |uv|≤n 3) uviω∈L(m) ∀ i≥0

Dimostrazione: Partiamo da un esempio: es. n=5 x∈L(m) |x|=8 Questo automa riconosce x=|x|=8 passi. In pratica basta visitare alcuni stati più di una volta. In pratica si divide il cammino in 3 parti: una prima u, una seconda v che posso ripetere più di una volta, una terza ω che mi colleghi a qF. In generale si x∈L(m) con |x|≥n supposto |x|=k allora x=a1a2…ak. Indico con xi il prefisso di x di lunghezza i, xi=a1…ai. ∀ i=1…k pongo qi=δ*(q0,xi). Se considero gli stati q0,q1,…,qk si ha k+1>k≥n. Alcuni di questi stati allora coincidono. Esistono quindi due indici i0 e i1 con 0≤i0≤i1≤n. Tali che: qi0=qi1, qi0=δ(q0,xi0), qi1=δ*(q0,xi1) pongo xi0=u, xi1=uv, x=uvω. Proviamo per induzione che uviω∈L(m) ∀ i≥0: passo base:i=0 δ*(q0,uω)=δ*(δ*(q0,u),ω)=δ*(δ*(q0,xio),ω)=δ*(δ*(q0,xi1),ω)=δ*(δ*(q0,uv),ω)= δ*(q0,uvω)∈F passo induttivo: Sia uvi-1ω∈L(m) =δ*(q0,uviω)=δ*(q0,uvvi-1,ω)=δ*(δ*(q0,uv),vi-1ω)=δ*(δ*(q0,xi1),vi-1ω)= =δ*(δ*(q0,u),vi-1ω)=δ*(q0,uvi-1ω)∈F Poichè pei ipotesi induttiva uvi-1∈L(m). Si può usare questo lemma per dimostrare la non regolarità di un linguaggio, ma non per dimostrare la regolare in quanto non vale il viceversa.

• Pumping Lemma su m AF con n stati. Sia m un AF con n stati finiti. Allora x∈L(m) con |x|≥n

∃u,v,ω ∈∑* con v≠ε tali che: 1) x=uvω 2) |uv| ≤ n 3) uviω∈L(m) ∀i≥0

• Teorema. Sia m un automa finito con n stati, allora L(m)≠∅ se e solo se ∃ x∈L(m) con |x|<n.

Dimostrazione. In un senso è ovvia. Per il viceversa. Sia x∈L(m) strnga di lunghezza minimale. Supponiamo per assurdo che |x|≥n con x=uvω con v≠ε e uviω ∈ L(m) ∀ I allora uω ∈ L(m) ma |uω|<n assurdo! Ho trovato una parola più corta di quella supposta minimale.

• Corollario. ∃ un algoritmo di decisione per stabilire se L(m)≠∅. Esso consiste nel provare tutte le disposizioni degli elementi di ∑ ad n ad n e vedere se arrivano ad uno stato finale.

• Teorema. Sia m un AF con n stati, allora L(m) è infinito se e solo se ∃ x∈L(m): n≤|x|<2n. Dimostrazione Se ∃x∈L(m) con n≤|x|<2n, applicando il pumping lemma si ha che ∀ i≥o uviω ∈ L(m) e quindi L(m) è infinito. Viceversa se L(m) è infinito, sia x∈L(m) di lunghezza minimale fra le parole di lunghezza ≥ n. Supponiamo per assurdo che |x|≥2n, allora:

o x=x1x2 ∈ ∑*, |x1|=n

q1

q2 q3

q4

q5

1

2 3

4

7

8

5

6

Page 11: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

o allora |x2|≥n. o spezziamo x2:x2=uvω con v≠ε o allora x1uviω ∈ L(m) ∀ i≥0 o per i=0 ottengo x1uvω ∈ L(m) ma poichè v≠ε => |x1uω| < |x| assurdo perché x non è di

lunghezza minimale.

• Corollario. E’ possibile decidere se L(m) è infinito o no, analizzando tutte le parole x: n≤|x|<2n e vedere se appartengono a L(m).

• Corollario. E’ possibile stabilire se due automi sono equivalente . Dimostrazione: Siano m1 e m2 due automi tali che L1=L(m1) e L2=L(m2). Costruiamo l’automa che accetta il linguaggio L=(L1-L2)∪(L2-L1). Se L≠∅ allora L1 e L2 sono equivalenti e viceversa m1 ed m2 sono equivalenti se e solo se L(m)≠∅.

• Non regolarità di un linguaggio (utilizzando il pumping lemma). Dato L è possibile dimostrare la sua non regolarità utilizzando il pumping lemma. Bisogna provare che ∃ x∈L(m) con |x|≥n tale che ∀ u, v, ω ∈ ∑* con x=uvω esiste un indice i tale che uviω ∉ L(m)

• Sia dato un linguaggio L di cui voglio dimostrare la non regolarità (tecnica dell’avversario):

o L’avversario fissa n o Scelgo x (in funz. Di n) con |x|≥n o L’avversario spezza x in uvω o Trovo i per cui la condizione 3) del pumping lemma non è soddisfatta.

L={ambm:m≥0} non è regolare. Fissato n, pongo x=anbn ∈ L. Sia x=uvω v≠ε ho tre possibilità: 1) v ∈ a a* 2) v ∈ b b* 3) v ∈ a a* b b* Considero i=2 e dimostro che uvrω ∉ L(m). 1) caso: uv2ω e composta da un numero di a maggiore e un numero di b costante quidi le a sono

di più delle b. 2) caso: uv2ω contiene più b che a 3) caso: ci saranno a e b mischiate L={0i2 i≥1}={0,04,09,016,…} Dimostro che non è regolare, fissato n sia x=0n2 x=uvω v≠ε |uv|≤n 1≤|x|≤n scelgo i=2 e dimostro che: uv2ω ∉ L |uvω| < |uv2ω|=|uvω|+|v|=n2+|v|≤n2+n<(n+1)2 n2<|uv2ω|<(n+1)2 |uv2ω|∉ L(m) perchè la sua lunghezza non può essere un quadrato.

CARATTERIZZAZIONE DI TIPO ALGEBRICO DEI LINGUAGGI REGOLARI

• Sia L un linguaggio allora dico che x RL y se ∀ z ∈ ∑* xz ∈ L ⇔ yz ∈ L RL è una relazione riflessiva, simmetrica e transitiva, cioè è una relazione di equivalenza e quindi divide ∑* in classi di equivalenze disgiunte.

• Una relazione di Equivalenza E è detta invariante a destra se x E y impica ∀ z∈ ∑*, xz E yz. • Lemma. ∀ L ⊆ ∑* la relazione RL è invariante a destra. Dimostrazione: Sia x RL y e z ∈ ∑*,

allora ∀ ω ∈ ∑*, si ha: (xz)ω=x(zω) ∈ L ⇔ y(zω) ∈ L, per definizione di RL, ma y(zω)=(yz)ω quindi ∀ ω ∈ ∑* => (xz)ω ∈ L ⇔ (yz)ω ∈ L quindi xz RL yz.

• Definizione. Sia m un automa a stati finiti m=(Q,∑,δ,q1,F) ∀ x,y ∈ ∑* dico che x Rm y se

δ*(q1,x)=δ*(q1,y) Rm è un relazione di equivalenza.

• Definizione. Rm è invariante a destra. Dimostrazione. Siano x,y ∈ ∑*: x Rm y dimostriamo che ∀ z ∈ ∑* => xz Rm yz. Per ipotesi δ*(q1,x)=δ*(q1,y) quindi δ*(q1,xz)=δ*(δ*(q1,x),z)=δ*(δ*(q1,y),z)=δ*(q1,yz)

• Teorema di Myhill-Nerode. Le seguenti affermazioni sono equivalenti:

1) L è regolare; 2) L è unione di classi di una relazione di equivalenza invariante a destra di indice

finito. 3) RL è di indice finito.

Dimostrazione:

• 1) => 2). Per ipotesi ∃ m,(m=(Q,∑,δ,q0,F)) AF, tale che L=L(m). consideriamo Rm, che è invariante a destra. iRm = indice di Rm ⊆ |Q|. Dobbiamo dimostrare che L è

unione di alcune classi di equivalenza su ∑* individuate da Rm L=*Σ∈x

U [x]Rm con

δ*(q0,x) ∈ F. Considero cioè solo le classi di equivalenza che corrispondo agli stati finali dell’automa

• 2) => 3). Per ipotesi L è l’unione di classi di equivalenza di una relazione E, invariante a destra di indice finito. Cioè ogni classe di equivalenza di E è contenuto in qualche classe di RL cioè l’indice di RL non può essere superiore ad

Page 12: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

E. Per far vedere che RL p di indice finito dimostriamo che E è un raffinamento di RL, cioè dimostriamo che x E y => x RL y, oppure ∀ x ∈ ∑* [x]E ⊆ [x]RL. iE ≥ iRL . Per Ipotesi la relazione E aveva un indice finito quindi anche l’indice di RL è finito. Resta da dimostrare che effettivamente E è un raffinamento di RL cioè: se x E y allora ∀ x ∈ ∑* xz ∈ L ⇔ yz ∈ L. Poichè E è un’invariante a destra allora xz E yz. Per ipotesi L è unione di classi di equivalenze di E. Poicè xz E yz allora xz e yz stanno nella stessa classe di equivalenza, rispetto a E quindi o appartengono entrambe a L o non appartengono entrambe a L

• 3) => 1). Costruiamo una automa che riconosca L ),,,,( 0 FqQm δΣ= dove Q è

l’insieme dele classi di equivalenza di RL. 0q [ε]RL F ={[x]RL :x ∈ L}

δ ([x]RL,a)=[x,a]RL. Poiché RL è invariante a dx allora se cambio rappresentate nella classe di equivalenza arrivo sempre alla classe [x,a]RL . La definizione è ben posta perche se x RL y allora per l’invarianza a dx di RL xa RL ya ciop: se [x]RL=[y]RL => [xa]RL=[ya]RL. Devo dimostra che L(m )=L

x∈L(m ) ⇔ δ *(0q ,x) ∈ F ⇔ δ *([ε],x)∈ F ⇔ [x] ∈ F ⇔ x ∈ L, per definizione

di F . Dimostreremo che m è l’automa minimale che accetta il linguaggio L Esempio: Dall’automa m abbiamo costruito le clasis di equivalenza rispetto ad Rm. Il linguaggio riconosciuto da m è l’unione di Cc, Cd,Ce. L= 0*10* Se x RL y si possono verificare 3 casi: 1) x,y ∈ 0* c1=0* 2) x,y ∈ 0*10* c2=0*10* 3) x,y ∈ 0*0*1(0∪1)* c3=0*10*1(0∪1)* c1, c2, c3, sono gli stati dell’automa minimale m. Prendiamo dei rappresentanti per ongi classe:

Le classi di equivalenza dell’automa Originario sono state ridotte

La relazione RL è la meno fine fra le relazioni di equivalenza.

• Sia L un linguaggio regolare, allora l'automa minimale (rispetto al numero di stati) che accetta L è unico, a meno di isomorfismi ed è dato dall'automa m . Dimostrazione: Sia m=(Q,∑, δ, q0, F) un automa che accetta L. La relazione Rm e più fine di RL, quindi: iRm ≥ iRL = |Q|.

Quindi qualunque automa che riconosce L ha un numero di stati ≥ del numero di stati m . Facciamo vedere che è unico. Sia m un automa minimale che accetta L. Identifico uno stato di m con uno stato di m . Se q è uno stato dell'automa minimale m allora ∃ x ∈ ∑*: δ*(q0,x)=q, cioè è raggiungibile a partire dallo stato iniziale. Se q non fosse raggiungibile si potrebbe eliminare e questo va contro l'ipotesi di minimalità di m. Allora q è raggiungibile da q0

δ*(q0,x)=q -> [x]Rl ∈ Q.

Se ho un'altra stringa tale che δ*(q0,y)=q allora x Rm y => x RL y. ( è => perché Rm è più fine di Rl). Abbiamo visto che l'automa minimale è unico. Vediamo un algoritmo per costruire l'automa minimale. Sia m un automa, costruisco un'automa m' equivalente a m e dimostro che è minimale .

a b

c d

e f

0

0

0

0

0

1 1

1

1

1

0,1

Ca=(00)* Cb=(00)*0 -Cc=(00)*1 -Cd=(00)*01 -Ce=0*100* Cf=0*10*1(0 ∪ 1)* Cc Cd Ce (00)*1 (00)*01 0*100* 1 1 1

[ε] [1] [11]1 1

0 0,10

Cb Cc

Cd

Ce Cf

Ca

C1

C2

C3

Page 13: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Definizione, Dato un automa m, ∀ p,q ∈Q dico che p è equivalente a q (p≡q) se ∀ z ∈ ∑* => δ*(q,z)∈ F δ*(p,z) ∈ F. Se p non è equivalente a q dico che p e q sono distinguibili. Si ha inoltre che ∀ a ∈ ∑, detti δ(p,a)=t e δ(q,a)=u. Allora se u e t sono distinguibili anche p e q lo sono

P t Q \ F P t Q • Lemma. Sia m=(Q,∑,δ,q0F) AF siano p,q, ∈Q allora p e q sono distinguibili se e solo se la coppia

non ordinata (p,q) è marcata dalla seguente procedura. Begin 1) For p ∈ F, q ∈ Q-F do mark (p,q) 2) For tutte le coppie di stati distinguibili (p,q) in (FxF) o in (Q-F x Q-F) do

3) If per qualche a ∈ ∑, (δ(p,a), δ(q,a)) è marcato then BEGIN 4) Mark(p,q) 5) Mark ricorsivamente tutte le coppie non marcate nella lista di (p,q) e nella

lista di tutte le altre coppie che vengono marcate durante questo passo END 6) Else for ogni a ∈ ∑ do

7) Inserisci (p,q) nella lista (δ(p,a), δ(q,a)) a meno che δ(p,a)=δ(q,a) oppure (δ(p,a),δ(q,a))=(p,q) END

Dimostrazione. Supponiamo che p,q siano distinguibili allora ∃ x ∈ ∑*:δ*(p,x) ∈ F δ*(q,x) ∉(∈) F Dimostriamolo per induzione su |x| • Passo base: |x|=0

Allora x=ε, significa che uno stato è finale e l'altro no • Passo induttivo:

Supponiamolo vero per |x| < i e dimostriamolo per |x|=i > 0 Allora x=ay con a ∈ ∑, y ∈ ∑*. Siano u=δ(q,a) e t=δ(p,a). Siccome, per ipotesi induttiva, u e t sono marcati dalla procedura, se (p,q) viene marcato prima di considerare (t,u) allora (t,u) verrà marcata al passo 4 altrimenti (p,q) sarà marcata al passo 5 non appena si considera la coppia (t,u). Se (t,u) è marcata dopo che (p,q) è stato considerato, allora (p,q) era già marcato quando (t,u) è stato considerato o la coppia (p,q) è sulla lista associata con (t,u) nel quale caso marcata al passo (5). Se (p,q) è considerata dopo (t,), (p,q) è marcato quando viene considerato.

q t F P t Q \ F Si dimostra il viceversa del lemma per induzione sul numero di coppie. M =(Q,∑,δ,q0,F) :L=L(m) M'=(Q',∑,δ',q'0,F'):L=(m')=L M' automa minimale che accetta L Q'={[q]≡ | q ∈ Q, q è accessibile da q0} q0=[q0]≡ F'={[q]≡ | [q]∈Q', q∈F} δ'([q]≡,a)=[δ(q,a)] Es. a,e equivalenti b,h equivalenti d,f equivalenti il nuovo automa diventa

a xa x

a ya y

0 a b ac d

e f g h

[a,e]

[b,h]

[c]

[g]

[d,f]

δ'([a],0)=[δ(a,0)]=[b]

Page 14: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Dobbiamo dimostrare che δ' è ben definita cioè se cambio rappresentante della classe arrivo alla stessa classe: in questo caso δ'([e],0) deve essere [b] o [h] q≡p allora δ(q,a)≡δ(p,a) supponiamo per assurdo che δ(q,a) non è equivalente δ(p,a)

∃ x ∈ ∑* tale che: δ*(δ(q,a),x) ∈ F δ*(δ(p,a),x)∉F δ*(q,ax) ∈ F δ*(p,ax)∉ F

assurdo perché p e q sono equivalenti. Si può dimostrare per induzione su |x| che δ'*([q0],x)=[δ*(q0,x)] dimostro che: L(m')=L(m)

x∈L(m') δ'*(q'0,x) ∈ F' δ'*([q0],x)∈F' [δ*(q0,x)]∈F' δ*(q0,x) ∈ F x∈L(m). Dimostriamo che m' è minimale. Sia x RL y (∀ z ∈ ∑* xz ∈ L yz ∈ L) m =( FqQ ,,,, 0δ∑ ) è un

automa minimale definito secondo RL. Dimostro che il numero di stati di m' è uguale al numero

di stati di m . Associo ad ogni stato di m' uno stato di m : [q]≡-> [x]Rl

∃ x ∈ ∑* δ*(q0,x)=q perché q è accessibile da q0 associo a [q]≡ proprio [x]Rl se cambio rappresentante della classe [x]Rl è la stessa? Cioè δ*(q0,x)=q p≡q => x RL y δ*(q0,y)=p infatti p≡q significa: ∀ z ∈ ∑* δ*(q,z) ∈ F δ*(p,z) ∈ F che implica: ∀ z ∈ ∑* δ*(δ*(q0,x),z) ∈ F δ*(δ*(q0,y),z) ∈ F implica ∀ z ∈ ∑* δ*(q0,xz)∈F δ*(δ*(q0,y),z) ∈ F ∀ z ∈ ∑* xz ∈ L yz ∈ L x RL y Dimostriamo che a due classi di equivalenza doverse di m’ non può corrispondere la stessa classe di equivalenza in RL [q]≡

non eq. [x]Rl non può essere [p]≡ per assurdo ∃ p,q: o non eq. e δ*(q0,y)p, δ*(q0,x)= e x RL y (∃ ω ∈ ∑*:δ*(p,ω)∈F δ*(q,ω) ∉F )=>(∃ ω ∈ ∑*:δ*(δ*(q0,y),ω)∈F<=>δ*(δ*(q0,x),ω ∉F) =>(∃ ω ∈ ∑*:δ*(q0,yω) ∈ F δ*(q0,xω) ∉ F) => xω ∈ L yω ∉ L assurdo perché x RL y

• Grammatiche context-free Sia G=(N,∑,P,δ) una grammatica context-free. Una derivazione δ => α1 => α2 =>…=> αn si dice leftmost(sinistra) se ∀ i=1,…,n-1 si ha αi=nxβi e αi+1=nγβi con n∈∑*, x∈N, x->γ in P. Analogamente una derivazione si dice rightmost se ∀ i=1,…,n-1 si ha αi=βixn e αi+1=βiγn con n∈∑*, x ∈ N, x->γ in P. In simboli

αi

*

α⇒ αs i<s derivazione leftmost

αi

*

R⇒ αs i<s derivazione rightmost

Def. Sia G =(N,∑,P,δ) una grammatica questa è detta context-free se ogni produzione di G è del tipo A->α con A ∈ N, α ∈ (N ∪ ∑)* < statment> -> < block > < block > -> begin < list_of_statements> and … <list_of_statements> -> < statement >; …, < list_of_statements > | ε

• Data G =(N,∑,P,δ) una grammatica c-f un albero di derivazione Τ (albero di parsing) per G è

un albero tale che: 1. ogni vertice è etichettato con N ∪ ∑ ∪ {ε} 2. la radice è etichetatta con δ 3. ogni vertice intermedio è etichettato con un simbolo di N 4. se un vertice v è etichettato con A e v1,…,vn sono i discendenti diretti di v con

etichette x1,…,xn rispettivamente allora A => x1 => x2 => … => xn p in P 5. se il vertice v ha etichetta ε allora v è una foglia ed è l’unico discendente del proprio

genitore • Def. La parola che si ottiene leggendo le etichette sulle foglie di Τ da sinistra verso destra

si dice prodotto di Τ. Ci possono essere diversi alberi di derivazione, e ad ognuno corrisponde una diversa derivazione della gramatica. Es. G = ( {δ,A}, {a,b},P, δ) S -> aAδ | a A -> δbA | δδ |ba

Page 15: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

È un albero di derivazione della Grammatica G

• Teorema. Sia G=(N,∑,P, δ) grammatica c-f allora δ *

G⇒ α se e solo se esiste almeno un albero

di derivazione per G con prodotto α. Nell’esempio precedente il prodotto è aabbaa Allora

δ *

G⇒ aabbaa

infatti δ => aAδ => aAa => aδbAa => aabAa => aabaa Per ogni derivazione esiste un unico albero di derivazione ma per un albero di derivazione ci possono essere più derivazioni perché le regola si possono applicare in qualunque ordine Esiste invece un unica derivazione leftmost perché si può applicare ogni volta la regola corrispondente al ramo più a sinistra. δ => aAδ => aδbAδ => aabAδ => aabbaδ => aabbaa questa è l’unica derivazione sinistra dell’albero precedente: derivazione destra: δ => aAδ => aAa => aδbAa => aδbbaa => aabbaa Per un dato albero di derivazione vi possono essere diverse derivazioni. Esiste invece un’unica derivazione leftmost e rightmost. Quindi vi è una corrispondenza biunivoca fra albero di derivazione e derivazione sx(dx).

• Sia G =(N,∑,P,δ) grammatica c-f allora δ *

G⇒ α se e solo se c’è un albero di derivazione per

G con prodotto α. Inoltre esiste una corrispondenza biunivoca tra albero di derivazione e derivazione sx (dx).

• Una grammatica è ambigua se si possono avere più derivazione che portano allo stesso prodotto. Di solito le grammatiche ambigue possono essere trasformate in grammatiche equivalenti non ambigue, ma esistono grammatiche inerentemente ambigue per cui ciò è impossibile.

• Gli alberi di derivazione danno un idea più precisa di una derivazione ed è più facile lavorare sugli alberi. Es. per cambiare un passo di derivazione dovrei cambiare tutti i passi successivi mentre sull’albero basta sostituire il sotto albero corrispondente.

Per un linguaggio c-f possono esistere più grammatiche c-f (CFG)

• Semplificazione di CFG:

1. ogni variabile e ogni terminale di G appaiono in qualche derivazione di una parola x∈∑* 2. in G non ci sono produzioni unitarie del tipo A->B con AB ∈ N 3. se ε non è derivabile allora in G non ci sono produzioni del tipo A->ε con A ∈ N

3a. in questo caso si può fare in modo che tutte le derivazioni siano del tipo: A->BC, A->a con A,B,C ∈ N, a ∈ ∑ allora si dice che la grammatica è in forma normale di Chomsky

3b. si può fare in modo che tutte le produzioni siano del tipo: A->aα con A ∈ N, a ∈ ∑, α ∈ N* allora si dice che la grammatica è in forma normale di Greibach

• Def. Sia G=(N, ∑, P, δ) una grammatica c-f un simbolo x ∈ N∪∑ è detto utile se ∃ una

derivazione del seg. Tipo:

δ *

G⇒ α x β

*

G⇒ ω con αβ ∈ (N∪∑)*, ω∈∑*. Altrimenti il simbolo è detto inutile.

Per essere utile devono valere le seguenti condizioni: 1) δ => α x β α,β ∈ ∑* 2) x => n

δ

1

2 3 4

A

65 7

9

8

b

a δ

δ A a

10 11a b a

Page 16: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

ma non esiste una derivazione in cui ci sono entrambe infatti: δ => AB => aB non porta a una stringa di simboli terminali, per cui metto A in N al passo 5). G’ è sottogrammatica di G cioè tutte le regole di G’ compaiono in G L(G’) ⊆ L(G) Facciamo vedere che L(G)⊆L(G’) . Supponiamo che x∈L(G) e per assurdo x ⊄ L(G’) allora in

questa derivazione (δ*

G⇒x) ci sono simoli ∈ M \ M’ il che è assurdo perché ogni simbolo della

derivazione, dato che deriva una sottostringa deve stare in N’

• LEMMA 1. Data una c-f-g G=(N,∑,P,δ) con L(G)≠∅ c’è un metodo effettivo per trovare una

grammatica equivalente: G’=(N’,∑,P’,δ) tale che ∀ A ∈ N’ ∃ ω ∈ ∑*: A *

G⇒ ω tale medoto è

dato da: 1. OLD N <- ∅ 2. NEW N <- {A: A -> ω è in P, ω ∈ ∑*} 3. WHILE OLD N ≠ NEW N do

Begin 4. OLD N <- NEW N 5. NEW N <- OLD N ∪ {A: A -> α è un P per qualche α ∈ (OLD N ∪ ∑)*}

End 6. N’ <- NEW N 7. P’ <- {A -> α: A -> α è un P, con α ∈ (∑ ∪ N’)*} 8. G’ <- (N’,∑,P’,δ)

Si può dimostrare facilmente per induzione che, se A ∈ N’ allora A *

G⇒ ω per qualche ω ∈ ∑*.

Vicevesera, supponiamo che A *

G⇒ ω con ω ∈ ∑* e dimostriamo che A ∈ N’ per induzione sulla

lunghezza di ω. o Passo base: k = |ω| =1

A => ω è un P e quindi inserisco A in New N al passo 2 o Passo induttivo:

Hp.: vero per produzioni di lunghezza < k e dimostriamo che A k

G⇒ ω con k > 1

Sia A => x1,…,xn 1−

⇒k

G ω la derivazione A

k

G⇒ ω. Scrivo ω=ω1ω2…ωn.

Si nota subito guardando l’albero di derivazione che ogni xi *

G⇒ ωI è composto da un

numero di passi ≤ k-1 quindi < k. Segue per xi ∈ N’ per Hp induttiva per cui metto A in N’ al passo 5)

G’ è una sottogrammatica di G, cioe tutte le regole di G’ compaiono in G. L(G’) ⊆ L(G).

Dimostraimo che si ha anche L(G) ⊆ L(G’). Supponiamo per assurdo che x∈L(G) ma x∉L(G’),

allora nella derivazione δ *

G⇒ ci sono dei simboli ∈ N-N’. Ma ciò è assurdo, perchè ogni

simbolo della derivazione, dato che deriva una sottostringa, deve stare in N’.

• Lemma 2. Sia G una c-f-g. Si può effettivamente trovare una grammatica equivalente

G’’=(N’’,∑’’,P’’,δ): δ *

G⇒ α x β con α, β ∈ (∑’’ ∪ N’’)*

Dimostrazione. Per ogni simbolo A già posto in N’’ e per ogni produzione A -> α in P, pongo tutti i simboli terminali di α in ∑’’ e tutti i simboli non terminali in N’’ . P’’ sarà l’insieme delle produzioni di P che contengono soltanto simboli di N’’ e ∑’’.

• Teorema. Ogni linguaggio c-f generato da una grammatica c-f priva di simboli inutili.

Dimostrazione . Da G = (N, ∑, P, δ) una c-f-g tale che L=L(G) creo la grammatica G’

attraverso il lemma 1 G -> G’ tale che ∀ x ∈ (N’ ∪ ∑’) x *

'G⇒ ω, con ω ∈ ∑’*, attraverso il

lemma 2: G’ -> G’’ tale che: ∀ x ∈ (N’’ ∪ ∑’’) δ *

''G⇒ α x β

G’’ è priva di simboli inutili, infatti se x ∈ G’’ si ha che ∃ in G’’ una derivazione

δ *

''G⇒ αxβ, inoltre δ

*

'G⇒ αxβ

*

'G⇒ ω poiché G’ è più grande di G’’, Tutti i simboli di questa

derivazione sono accessibili da δ, quindi sono anche in G’’, δ*

''G⇒ αxβ

*

''G⇒ ω

Es. G=({δ,A,B},{a,b},P,δ) P δ -> a|A A -> AB B -> B

Page 17: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

G = ({δ,A),{a,b},{δ -> a|AB, A -> a},δ) G’ = ({δ,A},{a},{δ->a,A->a},δ) Lemma 1 G’’ = ({δ},{a},{δ->a},δ) Lemma 2 Se avessi applicato I lemmi al contrario avrei aottenuto il simbolo intuila A.

• Teorema. Sia G una c-f-g tale che L=L(G) allora L-{ε}=L(G’) dove G’ è una c-f-g senza ε-

produzioni ne’ simboli inutili

Dimostrazione. Un simbolo x ∈ N si dice annullabile se x *

'G⇒ ε. Se A -> ε è un P allora A è

annullabile. Analogamente se A -> α è un P e tutti i simboli di α sono annullabili. Consideriamo il seguente algoritmo: 1) OLD Nε <- ∅ 2) NEW Nε <- {A:A->ε è un P) 3) WHILE OLD Nε ≠ NEW Nε do

Begin 4) OLD Nε <- NEW Nε 5) NEW Nε <- {A:A -> α è un P con α ∈ (OLD Nε)*} 6) Nε = NEW Nε. Se A -> x1x2…xn è un P, allora aggiungo un P’ in tutte le produzioni del tipo: A->α1α2…αn dove:

1) Se xi non è annullabile allora αi=xi 2) Se xi è annullabile αi ∈ {xi, ε} 3) α1,α2, …, αn ≠ ε

Esempio A -> X1X2X3X4 con X1,X3 Annullabili

Ottengo A -> X1X2X3X4 Ottengo A -> X2X3X4 Ottengo A -> X1X2X4 Ottengo A -> X2X4

Sia G’’ = (N,∑,P’’,δ) dimostriamo per induzione che A *

''G⇒ ω se e solo se A

*

G⇒ ω con ω≠ε.

o Passo base. i=1 Per ipotesi A

G⇒ ω con ω≠ε

Allora in P c’è A -> ω e ω≠ε quindi A->ω è in P’’ allora A *

''G⇒ ω

o Passo induttivo: i>1

Per Ipotesi A i

G⇒ ω con ω≠ε

A G

⇒x1x2…xn 1−

⇒i

Gω dove ω=ω1…ωn con x1=

1−

⇒i

GωI

A -> x1x2…xn è un P xi se ωi≠ε Pongo βI= ε altrimenti

Chiaramente A -> β1β2…βn è un P’’, alora A ''G

⇒ β1β2…βn *

''G⇒ ω1β2…βn

*

''G⇒ ω1ω2…ωn=ω

Dimostriamo il viceversa: • Passo base. i=1

Per ipotesi A ''G

⇒ ω e A -> ω è in P’’ ω≠ε, A -> α è un P ed annullando alcuni

simboli annullabili da α ottengo ω. Allora in G avrò: A G

⇒ α*

G⇒ ω

• Passo induttivo: i>1

Per Ipotesi A i

G ''⇒ ω allora A

''G⇒ x1x2…xn

1

''

⇒i

Gcon xi

1

''

⇒i

GωI

A-> x1x2…xn è un Pii e quindi in P ho A -> α tale che annullando i simboli annullabili di α ottengo x1x2…xn

α *

G⇒x1x2…xn, allora in G ho: A

*

G⇒ α

*

G⇒x1x2…xn=> ω1ω2…ω=ω

• Teorema. Ogni linguaggio c-f- senza ε-produzioni può essere generato da una c-f-g priva di

simboli inuitili, di ε-produzioni e di produzioni unitarie (una prod. È detta unitaria se è del tipo A-> B con A,B∈N). Dimostrazione: Sia G = (N, ∑, P, δ) senza ε-produzioni e senza simboli inutili, tale che L(G)=L. Costruiamo G’ nel modo seguente:

Page 18: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Metto in P’ tutte le produzioni non unitarie di P

• ∀ A,B ∈ N, se A *

G⇒ B allora metto in P’ tutte le produzioni A->α dove β->α è

una produzione non unitaria di P

N.B. per vedere se A*

G⇒ B devo guardare tutte le forme proposizionali con lunghezza di 1

infatti mancano le ε-produzioni quindi da una forma sentenziale non posso derivare una forma più piccola. La procedura per fare ciò esiste ed è finita. Se ci fossero ε-produzioni,

potremmo anche non rinunciare a verifare che A*

G⇒ G

Dimostro che L(G’)⊆L(G) • Trasformazioni in forma normale di Chomsky (C.N.F.) Sia G=(N,∑,P,δ) c-f-g allora G si dice in c-

f-g- se ogni produzione di G è del tipo A -> BC oppure A -> a con A,B,C ∈ N e a ∈∑

• Teorema. Trasformazione in forma normale di Chomsky (CNF). Sia G una cfg senza ε-produzioni. Esiste un algoritmo che trasforma G in una CNF equivalente Dimostrazione. Sia G =(N,∑,P,δ) una cfg senza ε-produzione, simboli inutili e produzioni unitarie. ∀ α ∈ ∑ introduco una variabile Xa e una produzione Xa->a, per ogni produzione A -> α in P, con c α∈∑, sostituisco i simboli terminali di α con le variabili corrispondenti. Es. A -> aBbD diventa A -> XaBXbD Tutte le produzioni diventano del tipo: A -> A1A2...An A -> a Se n>2 la produzione A -> A1A2...An non va bene, quindi ∀ produzione A -> A1A2...An con n>2 introduco n-2 produzioni nuove variabili Z1,Z2,...,Zn-2 e sostituisco la produzione con la seguente: A -> A1Z1 Z1 -> A2Z2 . . Zn-2 -> An-1An Es.: S -> aXbY X -> aX|Z Y -> bY|b Z -> a Passo 1 elimino le produzioni unitarie S -> aXbY X -> aX|a Z -> a Passo 2 Xa -> a Xb -> b S -> XaXXbY X -> XaX|a Y -> XbY|b Z -> a Passo 3 S -> XaZ1 Z1 -> XZ2 Infatti S => XaZ1=>XaZ2=>XaXXbY Z2 -> XbY le altre regole (tutte tranne la terza) sono uguali. La nuova grammatica è in CNF E’ chiaro

che L(G)⊆L(G’), dimostriamo che L(G’)⊆L(G) cioè che A⇒G '

*ω implica che A⇒

G

- passo base: banale - passo induttivo:

Per ipotesi A⇒G

k

'ω con k>1

A⇒G '

B1...Bn ⇒−

G

k

'

1ω con ω=ω1...ωn per cui Bi ⇒

< −

G

k

'

1ωi per ip. induttiva Bi ⇒

G '

*ωi. Se ho la

derivazione A =>B1...Bn allora in G ho la produzione A->C1C2..Cn in P, dove Ci=Bi oppure Ci=a

A ⇒G

c1...cn ⇒G

*ω1ω2...ωn=ω

L’equivalenza tra il passo 2) e il passo 3) si dimostra allo stesso modo per induzione.

• Una grammatica c.f.g. si dice in forma normale di Greinback (CNF) se ogni produzione è del tipo A -> aα con a∈∑, α∈N*, A∈N

Page 19: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Definizione. Un simblo non terminale A di G si dice ricorsivo se A ⇒G

*αAβ per qualche α e β.

Se α=ε allora A si dice ricorsivo a sx. Se β=ε allora A si dice ricorsivo a dx. Un grammatica in CNF non può essere ricorsiva a SX (non contiene simboli ricorsivi a sx.)

• Lemma1: Sia G=(N,∑,P,δ) una c.f.g., sia A->α1βα2 una produzione di P e siano B->β1/β2/.../βr le β produzioni di G. Sia G1=(N,∑,P’,δ) la grammatica ottenuta da G eliminando le produzioni A->α1βα2 e introducendo le produzioni A ->α1β1α2/.../α1βrα2 allora L(G1)=L(G). Dimostrazione

Dimostriamo che L(G1)⊆L(G)

A⇒G1

α1βiα2 ∀ i=1..r

A⇒G

α1βα2 ⇒G

α1βiα2

Dimostriamo che L(G) ⊆ L(G1) Il problema nasce quando utilizzo la produzione A -> α1βα2, in questo caso ci sarà un posto

in cui uso la regola β->βi,ì quindi ottengo lo stesso risultato se uso la regola A -> α1βiα2

• Lemma2: Sia G=(N,∑,P,δ) Una CFG e siano A->Aα1/Aα2/.../Aαn le A produzioni di G e siano A->β1/.../βn le rimanenti A produzioni. Sia G1=(N∪{β},∑,P,δ) la CFG ottenuta da G sostituendo le A produzioni del tipo: A->Aα1/Aα2/.../Aαn con le seguenti: A -> βiB 1≤i≤n B -> αs 1≤s≤n B -> αsB Allora L(G1)=L(G) Dimostraz.: La sequenza di derivazioni di G è:

A⇒G

Aαi1⇒G

Aαi2αi1⇒G

...⇒G

Aαip...αi1⇒G

βsαip...α1i che in G’ diventa:

A⇒G '

βsB⇒G '

βsαipB⇒G '

βsαip-1B... ⇒G '

βsαip...αirB⇒G '

βsαip...αi1⇒G '

βiαip...αi

• Teorema. Sia L un linguaggio cf senza ε. Allora L può essere generato da una gramamtica in

GNF (Greyback). Dimostrazione. Sia G=(N,∑,P,δ) una gramm. in CNF tale che L=L(G). Sia N={A1,A2,..,An}. Facciamo in modo che tutte le produzioni del tipo Ai->Asγ abbiamo s>i cioè siano sistemate quindi sistemo la k-esima se Ak -> Asγ s<k, applicando il lemma 1 al posto di As metto il lato destro della As produzione e se lo faccio al più k-1 volte arrivo a sistemare tutto. Es.: A1 -> A2A3

A2 -> A3A1 | b A3 -> A1A2 | a che diventa

A3 -> A2A3A2 A3 -> A3A1A3A2 Applico il Lemma 2 e ottengo prod. Ai -> Ajγ j>i . Scriviamo l’alg. che fa questo lavoro per N={A1,A2,...,An} begin

for k:=1 to n do begin for s=1 to k-1 do for tutte le produzioni Ak->Asα do

begin for tutte le produzione As->B do aggiungi le produzioni Ak->Bαs elimina le produzioni Ak->Asαi end

for tutte le produzioni Ak->Akα do begin aggiungi le produzioni Bk->α e Bk->αBk elimina le produzioni Ak->Akαi end

for tutte le produzione Ak->B con B non iniziante per Ak do aggiungi le produzioni Ak->BBk end

end Alla fine dell’alg. avrò tutte la produzioni del tipo:

Ai -> Asγ con s>i Ai -> aγ con a ∈∑ Bk -> γ con γ∈(N∪{B1,B2,...,Bk})*

Es.: A1 -> A2A3 A2 -> A3A1|b A3 -> A1A2|a

Page 20: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Trasformiamo questa produzioni: A3 -> A2A3A2|a A1 -> A2A3 A2 -> A3A1|b

A3 -> A3A1A3A2|bA3A2|a A1 -> A2A3 A2 -> A3A1|b

A3 -> bA3A2B3|aB3|bA3A2|a B3 -> A1A3A2|A1A3A2B3 A1 -> A2A3 A2 -> A3A1|b

applico il lemma 1 sostituisco ad A3 il lato destro della prozuione

A3 -> A2 -> bA3A2B3A1|bA3A2A1|aA1|b A1 -> bA3A2B3A1A3|aB3A1A3|bA3A2A1A3aA1A3|bA3

Applico il lemma 1 anche per le B produzioni.

• Def. Una grammatica G si dice ambigua se ∃ ω∈L(G) per cui vi sono due distinte derivazione sinistre. Es. E -> E+E|E-E|a

è ambigua perché la parola a+a-a ha due diverse derivazioni sinistre. 1) E -> E+E => a+E => a+E-E => a+a-a 2) E -> E-E => E+E-E =>a+a-a

• Def. Sia L un linguaggio cf. L si dice inerentemente ambiguo se ogni gramm. cf che genera L

è ambigua. Es. L={anbncmdm: m≥1, n≥1}∪{anbmcmdn: n≥1,m≥1} è inerentemente ambigua. Questo linguaggio si usa per determinare se un linguaggio è inerentemente ambiguo è indecidibile

• Teorema (PUMPING LEMMA [BUS-HILLEL])

Sia G una grammatica in CNF avente n variabili. Sia L=L(G). Allora ∀ x ∈ L(G). |x|≥2n si ha x=r1q1rq2r2 cion 1) |q1rq2| ≤ 2n 2) q1, q2 ≠ ε 3) ∀ i ≥ 0 r1 q1i qri r2 ∈ L(G)

Questo lemma è di iterazione e mi dice che se x soddisfa l’ipotesi questa si può spezzare dandomi ancora una parola di G. Dimostrazione.Se considero l’albero di derivazione, Τ, di x c’è un cammino di lunghezza > n. Se in un albero di derivazione di una stringa z ogni cammino ha Lunghezza ≤ i allora |z|≤2i-1. Dimostriamo il teorema per induzione su i: . passo base: i=1 in questo caso l’albero sarà quindi |a|=1=2i-1. . passo induttivo: supponiamolo vero per i-1 e dimostriamo per I l’albero sarà: ogni cammino di T1 e T2 avrà lunghezza ≤ i-1 Quindi |z1|≤2i-2 |z2|≤2i-2 |z|=|z1|+|z2|≤2i-2 + 2i-2=2i-1 cioè la tesi. Se tutti i cammini avessero lunghezza ≤ n alora |x|≤2n.1 < 2n quindi si ha un assurdo, e nell’albero di derivazione avrò almeno un cammino di lunghezza ≥ n+1 n+1 vertici hanno per etichette dei simboli non terminali ma per ipotesi ho n varibili. Quindi ci sono 2 vertici con la stessa etichetta.

S

A B

S -> AB

S | A

S

≤i-1≤i-1

T1 T2

z1 z2

Page 21: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Disegnamo meglio l’albero di derivazione di x r è il prodotto dell’albero di radice v2 q e rq2 p è il prodotto dell’albero di radice v1 quindi |q1 rq2| ≤ 2n inoltre q1 e q2 non possono essere contemporaneamente ε.

1) S x

⇒ r1 X r2

S => B1C1 => B2 X C1 x

⇒ r1 X r2

2) X x

⇒ q1 X q2

3) Xx

G⇒ r

Ts. S x

G⇒ r1q1i rq2i r2 ∀ i ≥ 0

Applicando le 1), 2), e 3) posso trovare la Ts ∀ i ≥ 0 Il pumping lemma serve per dimostre che un linguaggio non è c.f. Es. L={ambmcm | m>0} no è c.f. Se per ass. Fosse c.f. ∃ b con n variabili tale che L=L(G) k ≥ 2n/3 e considero x=akbkck ∈ L |x|=3k ≥ 3 2n/3 = 2n x=akbkck=r1q1rq2r2 |q1rq2| ≤ 2n q1q2≠ε r1q1irq2ir2 ∈ L ∀ i ≥ 0 q1,q2 ∈ a* ∪ b* ∪ c* (q1 e q2 sono formate da solo a, solo b oppure solo c) consideriamo r1q12rq22r2 una fra le tre lettere non compare in q1 e q2 quindi r1q12rq22r2 ∉ L Es.: ∃ L1, L2 linguaggi c.f. tale che L1∪L2 non è c.f. L1={anbncm | n,m >0} c.f. Grammatica: S -> Sc S->Xc X->aXb | ab L2={ambncn | m,n > 0} è c.f. Ma L1∪L2 = L non è c.f. come abbiamo dimostrato prima. Pumping Lemma: Sia G in CNF con n variabii. Sia x∈L(G) |x|≥2n allora si ha x=r1q1rq2r2 con: 4. |q1rq2|≤2n 5. q1q2≠ε 6. r1q1irq2ir2 ∈ L(G) ∀ i≥0 Es. : L={aibjckdh | i=0 oppure j=k=h} Questo non è c.f. ma non si può dimostrare con il pumping lemma: |aibjckdh| ≥ 2n Supponendo per assurdo che non sia c.f. non riesco a trovare l’assurdo infatti: 1) |q1rq2|≤2n è soddisfatta 2) q1q2≠ε è soddisfatta 3) r1q1irq2ir2 ∈ L(G) ∀ i≥0 è soddisfatta

SB1

B2

r1

C1

r2

q1r

v1X

v2 X

C

q2

B

≤ n+1

Page 22: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• Per dimostrare che il linguaggio non è c.f. devo usare il lemma di Ogden. Sia G .c.f in CNF con esattamente n variabili e sia L=L(G). Allora ∀ x∈L in cui sono state contraddistinte un numero di posizioni ≥ 2n si ha x=q1r1rq2r2 dove: 1) |q1rq2| contiene 2n o più posizioni distinte 2) q1q2 contiene almeno una posizione contraddistinta 3) r1q1irq2ir2 ∈ L ∀ i≥0 Il pumping lemma si può ottente da lemma di Ogden marcando tutte le posizioni della stringa x. Usiamo il lemma per dimostrare che un linguaggio non è c.f. L={aibjck | i≠j, j≠k, i≠k} L=L(G) per G CNF con n variabili Sia p≥2n x=xpbp+p!cp+2p! ∈ L(G) x= r1q1rq2r2

- q1,q2 ∈ a* ∪ b* ∪ c* - almeno una tra q1 e q2 deve contenere una “a”

distinguiamo due casi: 1) q2 ∈ b* ∪ c* (non contiene una a)

q1 ∈ a+ 2) q2 ∈ a+

q1 ∈ a+ (perché r1q1rq2r2 deve essere formata da per come è fatto il linguaggio aibjck)

1) q2∈b*, q1 ∈ a+ q1∈as 1≤s≤p s|p! (s divide p!) p!=st z1=r1q12t+1 rq22t+1r2 q12t+1=as(2t+1)=a2st+s=a2p!+s in r1r ci sono p-s a e le vedo da x quindi in z2 ci sono rp!+s+p-s a

• Proprietà di chiusura dei linguaggi C.F. Teorema: Se L1 e L2 sono c.f. allora L1∪L2 è c.f. Dimostrazione. ∃ G1G2 g.c.f. tali che L1=L(G1) e L2=L(G2) G1=(∑1,N1,P1,δ1) G2=(∑2,N2,P2,δ2) Con N1 ∩ N2 = ∅ Considero G=(∑1∪∑2,N1 ∪ N2 ∪{δ}, P1∪P2∪{δ->δ1 δ2},δ)

Dim. Che L(G)=L1∪L2 sia ω ∈ L(G) S *

G⇒ ω

1) δ G

⇒ δ1*

G⇒ ω δ1

*

G⇒ ω ω∈L

2) δ G

⇒ δ2*

G⇒ ω ω∈L

viceversa:

1) ω∈L1 δ1*

G⇒ ω δ

G⇒ δ1

*

G⇒ ω

2) ω∈L2 δ1*

G⇒ ω δ

G⇒ δ1

*

G⇒ ω∈L(G)

• Corollario. La classe dei linguaggi c.f. non è chiusa per complementazione. Un esempio ci

dice che non è chiusa per l’intersezione quini dnon può essere chiusa per complementazione: Dim. L1,L2 c.f. L1∩L2 = L1∪L2 Se fosse chiusa per complementazione, anche L1∩L2 sarebbe c.f. L’intersezione non regolare di linguaggi c.f. è ancora c.f. Problema dell’appartenenza per gramamtiche c.f. Dato G=(∑, N, P, δ) e ω∈∑* 1) Algoritmo

x=ε caso già esaminato x≠ε ∃ G1 in GNC tale che L(G1)=L(G)\ε |x|=n Es. δ=>aBCD=>aaBBCD (derivaz. In GNF) una derivazione di x prende n passi perchè ad ogni passo aggiungo un simbolo terminale

δ n

G1

⇒x

posso applicare le regole della grammatica in un numero finito di modi: k|x| con K=num. Di produzioni.

Ci sono a Non ci sono a

Page 23: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

2) Algoritmo CYK (cocke-Yanger-Kasoni) Parto da G in CNF, |x|=n definisco xij come la sottoparola di x di lunghezza j che parte dall i-esima posizione Es. x=abcde X23=bcd

Se riesco a fare A=>xij per tutti i valori possibili di I e j, arrivo a δ *

G⇒ x1n=x

Dimostraz. Per induzione su j j=1 xij ∈∑ basta vedere se A->xij p in G

j>1 A*

G⇒ xij

A=>BC B*

G⇒ xik 1≤k≤j-1

C*

G⇒ xi+k,j-k

A=>BC=>xi,kxi+k,j-k=xij Bisogna vedere se esiste una regola di produzione A=>BC ed è possibile derivare da B e C una sottostringa di xij Algoritmo: Begin For i=1 to n do Vi1={A|A->xi1 è una produz. Di G} For j=2 to n do For i=1 to n-j+1 do Begin xij≠0 for k=1 to j-1 do Vij=Vij∪{A|A->BC è in P e B è in Vik e C è in Vi+k,j-k} End End Esempio δ -> AB|BC B->CC|b A -> BA|a C->AB|a x=baaba x∈L(G)? Voglio calcolare V24: Faccio variare k: B ∈ V21 Per k=1 prendo C ∈ V32

B ∈ V22 Per k=2 prendo

C ∈ V12 B ∈ V23 Per k=3 prendo C ∈ V51

Dato che δ ∈ V15 => x∈L(G)

{B} A,C A,C B A,c S,A B S,C S,A ∅ B B ∅ S,A,C S,A,A

1 2 3 4 5

1 2 3 4 5

i j

A S A A S A,S A S A,S A,S S A,S A,S, A,S A,S

1 2 3 4 5

1 2 3 4 5

i j

Esempio: S -> AA|AS|B S -> SA|AS|A x=abaab

Page 24: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

• AUTOMI A PILA.

Sono corrispondenti alle grammatiche c.f. La memoria è costituita da uno stack l’azione compiuta dall’automa dipende dalla posizione della testina sul nastro e la prola che c’è in cima allo stack. Def. Un Automa a Pila M p un sistema M=(Q,∑,Τ,δ,q0,Z0,F) dove:

- Q è l’insieme finito di stati - ∑ è l’alfabeto di input (finito) - Τ è l’alfabeto di stack (finito) - δ è la funzione di transizione - q0∈Q è lo stato iniziale - Z0 è il simbolo di stack iniziale - F ⊆ Q è l’insieme degli stati finali

δ:Q x (∑∪{ε} x Τ -> Pow (Q x Τ*) interpretazione: δ(q,Q,Z)={(q1,γ1),(q2,γ2),…,(qn,γm)} q1,q2 ∈ Q a ∈ ∑ z ∈ Τ γi∈ Τ* ∀ i=1…n operazioni eseguita: 1) passa allo stato qi 2) sostituisce z con γI 3) avanza di una posizione

Il comportamento dell’automa è non deterministico. Interpretazione di: δ(q,ε,Z)={(q1,γ1),…,(qn,γn)} Se l’automa si trova allo stato q e z è la parola in cima allo stack, indipendentemente dal simbolo che c’è sul nostro (stack) effettuo le seguenti operazioni: 1) passo allo stato qi i lo può scegliere come vuola da 1 a n 2) sostituisco Z con γi Una descrizione istantanea (DI) è una tripla (q,ω,γ) con q∈Q, ω∈∑*, γ∈Τ*. Diciamo che dalla DI:

(q,aω,Zγ) Ma (p,ω,βγ) se (passo a ) δ(q,a,z) contiene (p,β) Passo a Diciamo che:

(……) *

Ma (……)

I i

Ma k

Con gli stessi significati usati per la grammatica. Linguaggi accettato per stati finali.

L(M)={ω∈∑*|(q0,ω,Z0) *

Ma (p,ε,γ)

*Γ∈γcon p∈F}

ω∈L(M) se arrivo ad una configurazione finale avendo consumato tutto ciò che c’è sullo stack

q

q

a z

γ

p

a β

γ

Page 25: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Linguaggio accettato per stack vuoto.

N(M)={ω∈∑*|(q0,ω,Z0) *

Ma (q,ε,ε) per q∈Q}

Se ho un automa M1e considero il linguaggio accettato per stati finali, posso costruire un automa M2 tale che lo stesso linguaggio sia accettato per stack vuoto: M1 -> L(M1)=L M2 -> N(M2)=L Es. M=({q1,q2},{0,1,c},{RGB},δ,q1,R,∅) δ(q1,0,R)={(q1,BR)}| δ(q1,1,R)={(q1,GR)} δ(q1,0,B)={(q1,BB)}| δ(q1,1,B)={(q1,GB)} δ(q1,0,G)={(q1,BG)}| δ(q1,1,G)={(q1,GG)} δ(q1,c,R)={(q2,R)} | δ(q2,0,B)={(q2,ε)} δ(q1,C,B)={(q2,B)} | δ(q2,1,G)={(q2,ε)} δ(q1,C,G)={(q2,G)} | δ(q2,ε,R)={(q2,ε)} N(M)={ωcωR:ω∈{0,1}*} Lo stato q1 riempie lo stack mettendo (immaginiamo con i piatti colorati) un piatto blu se trova zero e verde se trova 1. Lo stato q2 rimuove i piatti dallo stack se trova le stesse lettere in ordine inverso. N(M) non è regolare.

• Definizione Un automa a pila M=(Q,∑,Τ,δ,q0,z0,F) è deterministico se: o 1) ∀ q∈Q, z∈Τ, se δ(q,ε,z)≠∅ allora δ(q,a,z)≠∅ ∀a∈∑ o 2) δ(q,a,z)≤1 ∀ q ∈ Q, z∈Τ, q∈∑∪{ε}

Deve essere soddisfatta anche la 1) perché altrimenti potrebbe eseguire un movimento leggendo il simbolo dal nastro oppure un altro movimento senza leggere il simbolo (leggendo ε). L’automa precedente è deterministico.

• Teorema. 1) Sia L=L(m2); allora L=N(m1) per un opportuno automa a pila m1

2) Se L=N(m1) allora L=L(m2) per un opportuno automa a pila m2 Dimostrazione: 1) Sia m2=(Q1,∑,Τ,δ,q0,z0,F) m1=(Q∪{q0’,qe},∑,Τ∪{X0},δ’,q0’,X0,φ} con: - δ’(q0’,ε,X0)={(q0,z0,X0) - δ’(q,a,z) contiene δ(q,a,z,) {∀q∈Q, a∈∑∪{ε}, z∈Τ - δ’(q,ε,z) contiene (qe,ε) {∀ q∈F, z∈Τ∪{X0} - δ’(qe,ε,z) contiene (qe,ε) ∀ z ∈ Τ ∪{X0} faccio quello che fa il vecchio automa; quando arrivo ad uno stato finale passo allo stato qe e cancello lo stack. Può succedere che nel vecchio automa si svuota lo stack senza arrivare ad uno stato finale. Nel nuovo automa aggiungo X0 che non posso eliminare in nessun modo se non in modalità erase(qe) Dimostriamo che: x∈L(m2) ⇔ x∈N(m1) 1) =>

(q0,x,z0) *

2ma (p,ε,γ) con p∈F, γ∈Τ*

nel nuovo automa (q0’,x,X0)

1ma (q0,x,Z0X0)

tutto ciò che facevo in m2 lo posso fare in m1 con la differenza che sullo stack ho ZeX0 anziché Z0 (questo non ha importanza).

(q0,x,Z0X0)*

1ma (p,ε,γX0)

*

1ma (qe,ε,ε) x∈N(m1)

2) <= x∈N(m1)

(q0’,x,X0) *

1ma (qe,ε,ε)

spezzo la computazione

(q0’,x,X0) 1ma (q0,x,Z0X0)

*

1ma (q,ε,γ)

*

1ma (qe,ε,ε) (q∈F)

nel vecchio automa posso fare la stessa cosa senza X0 in basso

(q0,x,z0) *

2ma (q,ε,γ) q∈F => x∈L(m2)

Page 26: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Dimostrazione parte 2) m1=(Q,∑,Τ,δ,q0,z0,φ) costruisco m2 : m2=(Q∪{q0’,qf},∑,Τ∪{X0},δ0’,q0’,X0,{qf}) 1) δ’(q0’,x,X0)={(q0,z0,X0)} 2) δ’(q,a,z) contiene δ(q,a,z) ∀q∈Q, a∈∑∪{ε},z∈Τ 3) δ’(q,ε,X0) contiene (qf,ε) ∀ q∈Q è ovvio che il linguaggio L(m2)=N(m1) Introduciamo il linguaggio per stack vuoto perché per dimostrare l’equvilavenza con le grammatiche mi viene meglio con quest’ultima definizione. La potenza di calcolo di automi deterministici e non è diversa: N(M)={ωωR| ω∈{0,1}*} Si dimostra che non esiste un automa a pila deterministico che accetta questo linguaggio. M=({q1,q2},{0,1},R,B,G,0,q1,R,φ) M non è deterministico: δ(q1,0,R)={(q1,BR)} δ(q1,0,B)={(q1,BB),(q2,ε)} δ(q1,0,G)={(q1,BG)} δ(q1,1,R)={(q1,GR)} δ(q1,1,B)={(q1,BG)} δ(q1,1,G)={(q1,GG),(q2,ε)} δ(q1, ε,R)={(q2,ε)} δ(q2, 0,B)={(q2,ε)} δ(q2, 1,G)={(q2,ε)} δ(q2, ε,R)={(q2,ε)} Se sul nastro ho 0 e sullo stack ho blu posso decidere se continuare a caricare o cominciare a scaricare. Stessa cosa per o su nastro e verde sullo stack seguire la computazione per l’input ω=0,0,11,00

• Teorema: Se L è c.f. allora ∃ un automa a pila M tale che L=N(M). Dimostrazione ∃ G=(N,∑,P,δ) in GNF tale che L=L(G) A -> aγ A∈N, a∈∑, γ∈N* costruiamo M: M=({q},∑,N,δ,q,S,φ) con δ(q,a,A)={(q,γ)|A->aγ è in P)

Dimostreremo che S *

G⇒uα con derivazione sinistra se e solo se (q,u,S)

*

Ma (q,ε,α) u∈∑*,α∈N*

cioè passo da: dopo aver dimostrato questo avrò che:

x∈L(G) δ*

⇒ x (q,x,)Ma (q,ε,ε) x∈N(M)

Dimostrazione per induzione su i

<=) (q,u,δ) i

Ma (q,ε,α)

Base i=0, u=ε, α=δ, δ*

⇒ εδ=δ Induttivo i≥1 u=ya, y∈∑*, a∈∑

(q,y,a,δ) 1−i

Ma (q,a,β)

Ma (q,ε,α)

non considerando la a

(q,y,δ)i

M

<

a (q,ε,β) cioè :

passo da :

u q

s stack

α stack

uq

S

a

u

a

β

Page 27: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

e per Hp. Induttiva ottengo :

δ *

G⇒ yβ

supposto β scrivo : passo a cancellando A e aggiungendo η

Ovvero δ(q,a,A) contiene (q,η) Quindi, per come è definita la grammatica, A -> aη è in P

δ*

G⇒ yβ=yAγ => yaηγ=uα

=>) δi

G⇒ uα i=0 banale

passo induttivo i≥1 spezzo u=ya y∈∑* a∈∑

δ1−

⇒i

Gyβ=yAγ0>yaηγ=uα

poichè A -> aη, per HP induttiva ho :

(q,y,δ)*

Ma (q,ε,Aγ) quindi :

(q,ya,δ) *

Ma (q,a,Aγ)

Ma (q,ε,ηγ)=(q,ε,α)Ts.

Perchè se A -> aη, per come è definita δ, δ(q,a,A) contiene (q,η)

Oss. Un automa a pila può fare una serie di operazioni con certi simboli anche se c’è in più un simbolo alla fine. Abbiamo sfruttato questo fatto nella dimostrazione del teorema.

• Teorema. Sia L=N(M) con M automa a pila, allora L è c.f. Dim. M=(Q,∑,Τ,δ,q0,Z0,φ) G=(N,∑,P,δ) N={[p,A,q]|p,q∈Q,A∈Τ}∪{δ}

1) δ -> [q0,Z0,q] ∀q∈Q 2) [q,A,qn+1]->a[q1,B1,q2][q2,B2,q3]…[qm,Bn0,qn+1] ∀ q0,q1,…,qm+1 ∈Q A1B1…Bm∈Τ, a∈∑ tal che (q1,B1, B2,…Bm) ∈ δ(q,a,A) 2’) nel caso in cui m=0 : [q,A,q1]->a Es. M=({q0,q1},{0,1},{X,Z0},δ,q0,Z0,φ) δ(q0,0,Z0)=(q0,XZ0) la produzione che posso ottenere sono del tipo : [q0,Z0]->o[q0,X, ][ ,Z0, ] variando tra (q0,q1) tutti gli spazi cioè : [q0,Z0,q0]->0[q0,X,q0][q0,Z0,q0] [q0,Z0,q0]->0[q0,X,q1][q1,Z0,q0] [q0,Z0,q1]->0[q0,X,q0][q0,Z0,q1] [q0,Z0,q1]->0[q0,X,q1][q1,Z0,q1]

[q,A,p]*

G⇒ x se e solo se (q,x,A)

*

Ma (p,ε,ε)

x∈N(M) (q0,X,Z0) *

Ma (p,ε,ε) [q0,Z0,p]

*

G⇒ x δ

*

G⇒ [q0,Z0,p]

*

G⇒ x x∈L(G)

<=) (q,x,A)ia (p,ε,ε) dimostrazione per induzione su i

i=1 (q,x,A) Ma (p,ε,ε) x=a ∈ ∑

δ(q,a,A) contiente (p,ε) e dalla regola 2’) ottengo : [q,A,p]->a

A γ

β

u q

η γ α

eta

Page 28: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Passo induttivo :

(q,x,A) i

Ma (p,ε,ε) i>1 a∈∑ y∈∑* x=ay

(q,ay,A) Ma (q1,y,B1B2…Bn)

1−ia (p,ε,ε)

y=y1y2…yn La y è stata spezzata in modo che quando leggo y1 avrò sullo stack n-1 elementi, quando leggo y2, sullo stack ci sono n-2 elementi. Dallo stato qj, leggendo yj, e prendendo βj dallo stack posso arrivare a :

(qj,yj,Bj)*

Ma (qj+1,ε,ε)

il numero di passi sono <i quindi posso applicare l’ipotesi induttiva

[qj,Bj,qj+1] *

G⇒ yj ∀ j=1,…,n

δ(q,a,A) contiene (q1,B1B2…Bn) quindi nella grammatica G ho la produzione. [q,A,qn+1]-> a[q1,B1,q2][q2,B2,q3]…[qn,Bn,qn+1] [q,A,p] -> a[q1,B1,q2]…[qn,Bn,p] posso derivare

[q,A,p]G

⇒ a[q1,B1,q2]…[qn,Bn,p] => a y1y2…yn=ay=x

=>) [q,A,p]i

G⇒ x x=ay a∈∑ y∈∑*

passo base: banale Passo induttivo : [q,A,p] => a[q1,B1,q2]…[qn,Bn,p]=>

1−

⇒i

ay y=y1…yn

[qj,Bj,qj+1] i

G

<

⇒ yj

per ip. Induttiva :

(qj,yj,Bj) *

Ma (qj+1,ε,ε)

(qj,yj,Bj,Bj+1,…,Bn) i

Ma (qj+1,ε,Bj+1,…,Bn)

δ(q,a,A) contiene (q1,B1,…,Bn)

(q,ay1,…,yn,A)Ma (q1,y1,…,yn,B1,…,Bn)

*

Ma (p,ε,ε)

Es. ({q1,q1},{0,1},{X,Z0},δ,q0,Z0,p) δ(q0,0,Z0)={(q0,XZ0)} δ(q0,0,X)={(q0,XX)} δ(q0,1,X)={(q1,ε)} δ(q1,1,X)={(q1,ε)} δ(q1,ε,X)={(q1,ε)} δ(q0,ε,Z0)={(q1,ε)} P. δ->[q0,Z0,q0]|[q0,Z0,q1] [q0,Z0,q0]->0[q0,X,q0][q0,Z0,q0]|……q1][q1… [q0,Z0,q1]->0[q0,X,q0][q0,Z0,q1]|……q1][q1… [q0,X,q0]->0[q0,X,q0][q0,X,q0]|0[q0,X,q1][q1,X,q0] [q0,X,q1]->0[q0,X,q0][q0,X,q1]|0[q0,X,q1][q1,X,q1]|1 [q1,X,q1]->1|ε [q1,Z0,q1]->ε

y1 y2 …………………… yn Bn .

.

B2

B1

Page 29: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

per alcune regole di produzione non ci sono regole che producono simboli terminali quindi si possono eliminare.

• MACCHINA DI TOURING

limitato a sx Il nastro contiene simboli di un alfabeto che include il simbolo B (block) La testina può andare a dx o a sx tranne che si trova sulla prima cella.

• Def. Una macchina di Touring è un sistema M=(Q,∑,Τ,δ,q0,B,F) dove :

o Q è l’insieme degli stati o ∑⊆Τ alfabeto di input o Τ alfabeto di nastro o δ funzione di transizione o q0∈Q stato iniziale o B∈Τ blank B∉∑ o F⊆Q stati finali

Definiamo la funzione di transizione. δ :QxΤ -> Q x {R,L}xΤ

• Descrzione istantanea. ID

ID è una stringa α1qα2 α1,α2 ∈Τ* q∈Q Supp. Che H=X1X2…Xi-1qXi…Xn ID Se δ(q,Xi)=(p,y,L) allora passo a qust’altra ID. K=X1X2…Xi-2pXi-1Y…Xn

H Ma K

Se invece si ha : δ(q,Xi)=(p,Y,R) allora X1X2…Xi-1YpXi+1…Xn

H *

Ma K significa che esiste una successione di ID che da H partono a K.

L(M)={ω ∈∑*|q0ω*

Ma α1qα2, α1α2∈Τ* q∈F

Esempio : L={0n1n|n≥1]

La macchina cambia lo zero in X, cerca l’uno e lo cambia in Y poi torna indietro e cerca lo zero. Se alla fine tutti gli zeri vengono cambiati in X e Y e trova B, allora la parola viene accettata. Q={q0,q1,q2,q3} q0=stato iniziale, q1=salta al 1° disponibile q2=salta al 1° zero disponibile q3=cerca il blank alla fine (o un 1)

stat 0 1 X Y B q0 (q1,X,R) - - (q3,Y,R) - q1 (q1,Q,R) (q2*,Y,L) - (q1,Y,R) - q2 (q2,0,L) (q0,X,R) (q2,Y,L) q3 - - - (q3,Y,R) (q4,B,R) q4 - - - - -

Es. q00011 a Xq1011 a X0q111 a Xq20Y1 a q2X0Y1 a Xq00Y1 a XXq1Y1 a XXYq11

a XXq2YY a Xq2XYY a XXq0YY a XXYq3Y a XXYYq3 a XXYYBq4 La stringa è stata accettata perchè la macchina si è fermata in q4, se si ferma in uno stato diverso, la stringa non viene accettata.

• Def. Un linguaggio che è accettato da una macchina di Touring è detto ricursivamente

enumerabile. (Si chiama così perchè si può costruire una macchina di Touring che mi spara (enumera) tutte le stringhe del linguaggio).

0 0 … 0 1 … … 1 B

∝ a dx

n n

Page 30: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Se esiste una macchina di Touring che accetta il linguaggio e dopo ogni input si ferma, il linguaggio si dice ricorsivo. Per linguaggio ricorsivamente enumerabili potrei, per qualche input, non terminare mai la computazione.

• Def. La classe dei linguaggi ricorsivi è formata da quei linguaggi accettati da almeno una

macchina di Touring che si ferma su ogni input. Altri modelli di TM. Nastro infinito. Tutto ciò che posso fare col nastro infinito, lo posso fare anche con il nastro semi infinito. Per trasformare la macchina, spezzo il nastro e lo ricompongo sotto un pezzo e sotto l’altro. Macchina di Touring a K nastri. Per ogni nastro c’è una testina che si comporta per i fatti suoi. Tutto ciò che posso fare con K nastri lo posso fare con 1 nastro anche se con un maggiore spreco di tempo. Per dimostrare ciò utilizzo un nastro a più tracce (2 k tracce se k=num. nastri)

Le traccie pari indicano la posizione della testina. • Grammatica senza restrizioni • Def. Una grammatica G=(N,∑,P,δ) è detta senza restrizioni se ogni produzione è del tipo :

α -> β con α≠ε

L(G)={ω ∈ ∑* | δ *

G⇒ ω}

Es. L={ai : i è una potenza di 2 } 1) S -> Acab 2) Ca -> aaC 3) CB -> DB 4) CB -> E 5) aD -> Da 6) AD -> AC 7) aE -> Ea 8) AE -> ε

.

.

.

.

. . . . .

.

.

.

.

α

β

γ

Page 31: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Si può dimostrare per induzione sul numero di passi della derivazione che se la produzione 4) non viene mai usata, allora ogni forma sentenziale ottenuta a partire da δ è die tre tipi : 1) δ 2) AaiCajB con i+2j = potenze di due 3) AaiDajB con i+j = potenze di due Quando applico la regola 4 avrò : AaiCB con i potenza di due

AaiE i

⇒ Aeai => ai usando le regole 7 Nota : Si può dimostrare usando il Pumping Lemma che L non è c.f. La classe di linguaggi accettati da grammatiche senza restrizioni è la classe dei linguaggi ricursivamente enumerabili.

• Teorema : Un linguaggio L è ricursivamente enumerabili se e solo se può essere generto da

una grammatica senza restrizione. Dimostrazione. Sia L=L(G) con G unrestricted. Consideriamo una TM a tre nastri.

Nastro di input

Forme sentenziali

Lavoro

Partendo da δ scrivo sul nastro apposito tutte le forme sentenziali applicando in tutti i modi possibili le regole della grammatica. Ogni volta confronto la forma sentenziale con ω attraverso il nastro di lavoro. Questa macchina ha un comportamento non deterministico. M esegue ripetutamente le seguenti azioni : 1) sceglie non deterministicamente, una posizione in α (α forma sentenziale) 2) sceglie non deterministicamente, una produzione β -> γ in G 3) se β compare in α a partire dall i-esima posizione, sostituisce β con γ 4) confronto la forma sentenziale ottenuta con ω. Se conincidono, accetta, se no torna al

passo 1).

Si può dimostrare che partendo da una TM si può creare una grammatica unrestricted che ne simula il comportamento.

• Def. Un linguaggio è detto context-sensitive se è generato da una grammatica G. c.s. cioè

tale che tutte le produzioni sono del tipo : α -> β con |α|⊆|β| α,β ∈(M∪∑)* si capisce che ε ∉ L c.s.

es. L={ira | i≥i} è c.s.

può essere generato da una grammatica senza restrizioni : δ -> AcaB Ca -> aaC CB -> DB

*CB -> E *AE -> E sostituisco quindi le regole con : [Aea] -> a [aCB] -> [aE] dove […] sono nuovi simboli non terminali della grammatica.

Le regole di una grammatica context-sensitive possono essere trasformte in regole di questo tipo : βAγ -> βαγ α,β,γ ∈ (M∪∑*) α≠ε, A∈N

• Def. Una produzione di G=(M,∑,P,δ) è veramente c.s. se è del tipo :

βAγ -> βαγ α,β,γ ∈ (M∪∑)* α≠ε A∈N

• Teorema. Data una G. c.s. si può costruire una grammatica G’ equivalente a G tale che tutte le produzion siano veramente c.s. Dim. ∀ a∈∑ introduco la variabile Xa e la regola Xa->a 1) α -> β α,β ∈ N* |α| ≤ |β| 2) A -> a A ∈ M, a∈∑ per ogni produzione del tipo A1A2…An -> B1B2…Bm con 2 ≤ n ≤ m effettuo una trasformazione introducendo nuove variabili : B1PB2P……Bn-1P e sostituendo la regola con queste :

ω

δ

Non sono accettate dalla G. c.s.

Page 32: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

A1A2…An -> B1PA2…An B1PA2…An -> B1PB2PA3…An . . B1PB2P……Bn-1PAn -> B1PB2PBn-1PBnBn+1…Bm B1PB2P……Bn-1PBn…Bm -> B1PB2PBn-2PBn-1Bm . . B1PB2……Bm -> B1B2…Bm Linear bounded automation (LBA) Sono automi non deterministici con una particolarità : Hanno questi simboli sul nastro. Non possono leggere e scrivere e muoversi dal nastro limitato da quei simboli e non posso scrivere quei simboli.

• Def. Un LBA è una macchina :

M=(Q,∑,Τ,δ,q0,⊄,$,F) dove : Q,∑,Τ,q0,F sono def. In NTM 1) ⊄,$ simboli ∈∑ 2) δ definita come nelle NTM con le seguenti restrizioni :

a. δ(q,∉) contiene solo elementi del tipo (P,⊄,R) con P∈Q b. δ(q,$) contiene solo elementi del tipo (P,$,L) con P∈Q c. ∀q∈Q, ∀a∈Τ-{⊄,$) si ha : (p,b,R)∈δ(q,a) => b≠⊄,$ e (p,b,L)∈δ(q,a) => b≠⊄,$

Si può dimostrare che la potenza di calcolo di questa macchina è la stessa di….

L(M)={ω∈(∑-{⊄,$}* :q0⊄ω$ *

Ma αpβ} α,β∈Τ* p∈F

Un linguaggio accettato da LBA è un linguaggio ricorsivamente enumerabile.

• Teorema. Un linguaggio L senza ε, è context-sensitive se e solo se∃ un LBA M tale che L=L(M) (Landweber-Kuroda) Dim. Sia L c.s. consideriamo un automa a due tracce : Se ω=ε l’automa si ferma senza cancellare Se ω≠ε sostituisco γ con β se alla stringa α esiste la produzione β->γ e poi confronto α con ω. Con questa macchina mi posso accorgere se entro in un ciclo perchè la porzione di nastro utilizzata è finita

• Proprietà dei linguaggi context-sensitive :

Si può dimostrare che la classe dei linguaggi c.s. è chiusa rispetto all’unione, intersezione e complemento. La chiusura rispetto a complemento è stata dimostrata da Imm… nell’87 partendo dagli automi LBA. Non si sa se esiste equivalenza tra LBA non deterministici e deterministici.

• Teorema : Ogni linguaggio c.s. è ricorsivo. Dim. Data G0(M,∑, P,δ) c.s. che genera L e ω∈∑*.

Costruisco un grafo con vertici etichettati con stringhe di (M∪∑) di lunghezza ≤|ω|=n metto un arco tra α e β se e solo se c’è una produzione α->β. Costruito il grafo, per vedere se ω∈L basta vedere se ∃ un cammino che da δ mi porta a ω.

• Teorema. ∃ un L ricorsivo che non è c.s.

Teorema non dimostrato (dimostrabile)

• Gerarchia di Chomsky. Teoremai. (a) I linguaggi regolari sono contenuti propriamente nei c.f. ; (b) i c.f. senza ε sono contenuti propria nei c.s. (c) i c.s. sono contenuti propriamente nei r.e. Dimostriamo che le inclusioni sono proprie :

(a) L={0n1n :n≥1} è c.f. e non è regolare

(b) L={ira |i≥1} è c.s. non è c.f. (si puo dimostrare che non è c.f. col pumping

lemma) L={anbncn|n≥1} non è c.f. (dimostrato prima) è c.s. perchè è generato da : S->aSBC|abc ; CB->BC ; bB->bb ; bC->bc ;aC->ac.

(c) Segue dal teorema (precedente)

$C

⊄ $ω

ricorsivo

c.s.

Page 33: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

COMPILATORI

Front End : analizza il codice sorgete ; Back End : crea il codice destinazione ; Vi è anche una parte che otitmizza il codice : ES. y=f(x) y=f(x) . . codice che non modifica x e y diventa . z=f(x) z=y Considereremo linguaggi imperativi (es. pascal, c). I linguaggi a oggetti sono particolari lingaggi imperativi. Nella fase di analisi viene prodotto un albero di parsing. Es. : Position := initial + rate *60 L’albero sintattico sarà il seguente : L’interprete salva la parte Back End Ma esegue riga per riga .

ANALISI Consiste di 3 fasi :

- analisi lessicale (lineare) - analisi sintattica (gerarchica) - analisi semantica

Durante l’analisi lessicale, il compilatore elimina tutti i caratteri superflui (es. spazi) e raggruppa i caratteri restanti in Token. I Token vengono messi nella tavola die simboli, alcuni Tocken sono già nella tavola dei simboli (Es. : :=,+,*). L’analisi sintattica avviene secondo certe regole che vengono espresse normalmente attraverso grammatiche context-free. Per esempio in formato BMF. Es : expr <- id=expr|…. Stat <- while (expr) do stat ; If (expr) then stat Per esempio le regole del pascal sono : S -> program (X) body X -> Input|Output|Input,Output|ε Body->begin stat end.

r.e. c.s. c.f. regolari

Front End Back End

Compilatore

Programma

Target

Errori

Analisi Sintesi

:=

position +

initial *

rate 60

Page 34: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

I simboli sottolineati sono simboli terminali. L’albero di parsing usa i Token e i lexema. Es. Questo è l’albero di parsing corrispondente all’albero sintatti visto prima. L’albero di parsing del programma ha come radice S e racchiude tutte le istruzioni del programma. Si possono avere dei problemi connessi all’ambiguità delle grammatiche. Es. if A then if B then C else D Quest’istruzione produce due alberi di parsing diversi. Esempio di tavola dei simoli : Token lexema tipo scoping Id1 position real … Id2 initial … … Id3 rate … …

Pos := initial + rate * 60 Lessicale Id1 := id2 + id3 * 60 sintattica

A.S

Id := espr

position Expr + expr

id Expr * expr

id

rate

member

60

PROGRAMMA

lessicale

sintattica

semantica

Cod. intermedio

Ottimizzazione codice

Generazione codice

Output

Tavola dei Simboli

Gestione degli errori

:=

Id1 +

Id2 *

Id3 60

Page 35: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Temp1 := int o real (60) Temp2 := id3 * temp1 Temp3 := id2 + temp2 codice intermedio a tre indirizzi Id1 := temp3 Temp1 := id3 * 60.0 Codice ottimizzato Id1 := id2 + temp1 MOVF id3,R2 MULF #60.0,R2 MOVF id2,R1 codice assembler ADDF R2,R1 MOVF R1,id1

CARATTERISTICHE DEI LINGUAGGI IMPERATIVI Una variabile è composta da : 1) un indirizzo ; 2) un valore ; Dichiarazione esplicita del controllo di flusso del programma. Le istruzioni per il controllo del flusso sono i cicli e i costrutti (es. if..else)

MACCHINA P E’ simile al pascal, usa il suo linguaggio è limitato Tipi : M (numerico) T (arbitrario) i (intero) r (reale) b (boolenano) a (indirizzo) Gli operatori possono essere indicizzati : « <i significa confronto tra interi » Loop della macchina P : do PC := PC+1 Esegui istruzione CODE [PC-1] Od Il significato di « add N » è il seguente STORE[SP-1] := STORE [SP] +M STORE[SP-1 Add N SP := SP-1 Entrambe le variabili sono di tipo M e il risultato è di tipo N STORE[SP-1]:=STORE[SP-1] -N STORE[SP] Sub M SP:=SP-1 Mul N analogo a add DIV N analogo a sub Neg N STORE[SP]:=-STORE[SP] STORE[SP-1]:=STORE[SP-1] and STORE[SP-1] And SP:=SP-1 STORE[SP-1]:=STORE[SP-1] OR STORE[SP-1] OR SP:=SP-1 NOT STORE[SP]:=not STORE[SP] STORE[SP-1]:=STORE[SP-1] =T STORE[SP] Eq T SP:=SP-1 Geq T ≤T Leq T ≥T Les T <T Grt T >T Neq T <>

store SP

Max STR+1

code PC

E’ composta da due nastri

Page 36: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

=T ≤T ≥T etc. sono funzioni di test che indicano se è vera l'eguaglianza o la diseguaglianza. Voglio trasformare un istruzione o un programma in pascal in sequenze di istruzioni della macchina P:

a=(b+(b*c)) code[…]ρ function fun var x; begin x=x+1 end

altre istruzioni: SP:=SP+1 ldo T q STORE[SP]:=STORE[q] SP:=SP+1 ldc T q STORE[SP]:=q ind T STORE[SP]:=STORE[STORE[SP]] STORE[q]:=STORE[SP] sro T q SP:=SP-1 STORE[STORE[SP-1]]:=STORE[SP] sto T SP:=SP-2 Es. codifica dell'istruzione x:=y Altra istruzione : l1=l2 CodeR(l1=l2)ρ=codeRl1ρ;codeRl2ρ;equ T CodeR(l1*l2)ρ=codeRl1ρ;codeRl2ρ;mul N CodeRcρ=ldc T c c=costante CodeL xρ=ldc a ρ(x)

Es. a :=(b +(b * c )) ρ( a )=5 ρ(b )=6

ρ( c )=7

code( a :=(b+(b* c )))ρ=codeL a ρ;codeR(b +(b * c ))ρ;

sto i;=ldc a 5; codeR b ρ; codeR (b * c )ρ; add i; sto i = ldc a 5; ldo i 6; vediamo come trattare if-then-[else] fi while cond do stat od repeat stat until cond if cond then stat 1 else stat 2 fi

L-value X R-value Y sto i

Faccio un salto se cond non è vero quindi devo introdurre delle istruzioni di jump

Page 37: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

ujp q pc:=q; salto incondizionato fjp if store[SP]=false then PC:=q; salto condizionato SP:=SP-1 Quindi per compilare il codice tra () devo fare code()ρ che diventa if cond then stat fi Esempio if a>b ρ(a)=5 Code then c:=a ρ ρ(b)=6 else c:=b ρ(c)=7 fi store Per semplificare la trattazione sto assumendo implicitamente che tutte le variabili occupano lo stesso spazio che è una istruzione nella tabella di store; è la ⇒ che mi specifica la grandezza di ogni variabile ρ: var -> N lo spazio nello store viene quindi occupato all'inizio in maniera statica while cond do code stat ρ od

CodeR cond ρ fjp code stat1 ρ ujp code stat2 ρ

CodeR cond ρ fjp code stat1 ρ

5 a: 6 b: 7 c:

dopo codeR a dopo codeR b

a: b: c: 1 se a>b 0 altrimenti

Dopo grt i

CodeR a>b ρ ⇒ codeR aρ; codeR bρ; grt i ldo 5; ldo 6; grt i ldc a c; ind i; ldc a 6;

ind i; grt i fjp ⇒ fjp code (c:=a)ρ⇒ codeL c ρ; codeR a,ρ;

sto i; ldc a 7; ldc a 5; ind i;

sto i; ujp ⇒ ujp code c:=b ⇒ ldc a 7; ldc a 6; ind i;

sto i

5 a: 6 b: dopo sto i 7 c: a: dopo ldc 7 a>b dopo ind i 5 -> a:

Dopo ldc

code cond ρ fjp code stat ujp

Oppure usando le etichette code(while cond do stat od)ρ=l1:codeR cond ρ; fjp l2 code(stat)ρ; ujp l1; l2z

Page 38: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Esempio While x>y do calcola la dimensione di x,y Code z:=z+1 ρ ρ(x)=5 X:=x-1 ρ(y))=6 Od ρ(z)=7 Code(x>y)ρ => ldc a 5; ind i; ldc a 6; ind i; grt i; fjp => fjp code(z:=z+1,x:=x-y) => ldc a 7; ldc a 7; ind i; ldc i 1; add i; sto i ldc a 5; ldc a 5; ind i; ldc a 6; ind i; sub i; sto i ujp => ujp x:=x-y Esercizio: codice che mi dice se il numero è primo. Var n,j,:integer; P:boolean; ρ(n)=5 j:=2 ρ(j)=6 p:=true; ρ(ρ)=7 while (j<= n-1) and p do code if (n/j)*j=n ρ then p.=false else j:=j+1 fi od code(j:=2)ρ => ldc a 6; ldc i 2; sto i code(p:=true)ρ => ldc a 7; ldc b true; sto i code((j<=n-1) and p)ρ => codeR (j<=n-1); codeR p; and

ldc a 6; ind i; ldc a 5; ind i; ldc i 1; sub i; geq b; ldc a 7; ind i; and

fjp => fjp if (n/j))*j=n => codeR((n/j)*j=n); fjp; then p:= false => code(p:=false);ujp; code else j:=j+1 => code(j:=j+1) fi

codeR((n/j)*j)ρ; codeR n; equ N; fjp; ldc a 7; ldc b false; sto i; ujp; ldc a 6; ldc a 6; ind i; ldc i 1; add i; sto i; =>

codeR(n/j); codeR j; mul N; ldc a 5; ind i; equ M; fjp; ldc a 7; ldc b false; sto i; ujp; ldc a 6; ldc a 6; ind i; ldc i 1; add i; sto i =>

ldc a 5; ind i; ldc a 6; ind i; div i; ldc a 6; ind i; mul N; ldc a 5; ind i; equ N; fjp; ldc a 7; ldc b false; sto i; ujp; ldc a 6; ldc a 6; ind i; ldc i 1; add i; sto i

COME CALCOLARE GLI INDIRIZZI DI ARRAY

Es A[i,j]:=x Dobbiamo trovare l'indirizzo dell'elemento dello array. ρ ci dice l'indirizzo del primo elemento Supponiamo di avere Var x: array[-5..5,1..9] of integer Il numero di elementi è 99 (11x9) Un modo di rappresentare l'array sullo stack è quello di scrivere le righe una dopo l'altra: x(-5,1),x(-5,2),…,x(-5,9),x(-4,1)… In generale in un array k-dimensionale si ha:

x:array[U1..01,U2..02,Uk..Ok] la j-esima dimensione dell'array vale:

0j-Uj+1 se vogliamo indirizzare il seguente elemento

x[i1,i2,..ij,0j+1,0k] il successivo elemento è:

x[i1,i2,..ij+1,Uj+1,…Uk]

5 5->x: 6->y:

5 x:-y:

Page 39: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

il numero totale di elementi della matrice:∏=

+−k

jjj U

1

)10(

Introduciamo nella macchina P una funzione size in questo modo: size(t)=1 se t è un tipo base (intero booleano, etc) size(t)=dim se t è un array è dim e la sua dimensione var n1:t1,n2:t2,…nk:tk

ρ(n1)=5+∑−

=

1

1)(

i

jjtsize

se voglio mettere=0 il primo elem. Dello array devo fare: ldc a ρ(x) ldc i,0 sto i Supponiamo di voler codificare l'istruzione : x[i,j]=1 se gli elementi vanno da [-5..5,1..9] il nuovo indirizzo è (i-(-5))*9+j-1 da sommare a ρ(x) x[-3,3] si trova alla posizione (-3+5)*9+3-1=20 in generale per calcolare la posizione di x[i1,i2,…,ik] devo fare:

(i1-u1)*∏=

k

jjd

2

= primo elemento del sottoarray che va da i2…ik dove dj=dimensione in direzione j

(i1-U1)* ∏=

k

jjd

2

+

+(i2-U2)* ∏=

k

jjd

3

+

+……… +(ik-Uk)

r=(i1-U1)* ∏=

k

jjd

2

+(i2-U2)* ∏=

k

jjd

1

+…+(ik-1-Uk-1)* dk+(ik-Uk) =[i1*∏=

k

jjd

2

+i2+∏=

k

jjd

3

+…+ik-1*dk+ik]-

[U1*∏=

k

jjd

2

+U2*∏=

k

jjd

3

+…+Uk-1*dk+Uk]=h-d

È costante che viene calcolata a tempo di compilazione e la chiamiamo d

d(j)= ∏+=

k

jjd

h=∑=

k

j

jjdi

1

)(

Se ogni elemento ha dimensione diversa da uno (per es. è un altro array),basta moltiplicare per g=grandezza elementare r=(h-d)g Introduco una funzione matematica col seguente significato: i x a q STORE[SP-1]:= STORE[SP-1]+ STORE[SP]*q indirizzazione

Introduciamo due nuove istruzioni inc T q STORE[SP]:=STORE[SP]+q dec T q STORE[SP]:=STORE[SP]-q Code2 x[i1,…,ik]gρ=ldc a ρ(x); codeI[i1,…,ik]gρ; add i codeI[i1,…,ik]g ρ=codeR i1ρ; ixa g⋅d(1) =codeR i2ρ; ixa g⋅d(2) . . = codeR ikρ; ixa g⋅d(2) = dec a d*g devo introdurre una nuova funzione check per vedere se ogni indice sia all’interno del range: uj≤ij≤0j chk p q if STORE[SP]<P or STORE[SP]>q then “error” fi

Page 40: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

si dovrebbe mettere chk dopo ogni istruzione del tipo codeR ijρ: codek ijρ; chk uj 0j; var i,j :integre x :array[-5..5,i..9] of integer ρ(i)=5 d1=11 d(1)=11 ρ(j)=6 d2=9 d(2)=11*9 ρ(x)=7 d=u1d2+u2=-44 compiliamo l’istruzione: x[i+1,j]:=0 code(x[i+1,j]:=0)ρ=codeL(x[i+1,j])ρ;ldc I o; sto i = = ldc a 7; codeIx[i+1,j]ρ; add i; ldc i o; sto i= = ldc a 7; codeR(i+1)ρ;chk-5 5; i x a 11;codeR(j)ρ; chk 1 9; ixa ?; dec –44; add i; ldc i o; sto i; = ldc a 7; ldc a 5; ind i; ldc i 1; add i; chk-5 5; ixa 11; ldc a 6; ind i; chk 1 9; ixa …; dec a –44; add i; ldc i o; sto i;

Quando all’interno di un programma chiamo una procedura, lo stack viene riempito con determinati parametri che sono:

nome (della procedura, cioè l’identificatore della funzione) specifica dei parametri formali dichiarazione di variabili locali sequenza di istruzioni della procedura

Esempio: sotto forma di albero program main var x proc p proc q var x

Oggetti creati in maniera dinamica La memoria utilizzata per questi si chiama heap (significato di catasta) Definiamo delle istruzioni col seguente significato: New if NP-STORE[SP]≤ E P then error else NP:=NP-STORE[SP] STORE[STORE[SP-1]]:=NP SP:=SP-2 Code(new(x)) = ldc a ρ(x); ldc i size(t); new; Dove x è stato dichiarato come: x:^t Gestione del case: case x of 1: …. 2: …. 3: …. end. Questa struttura generalizza il costrutto if then else perchè si possono avere più di due case:

main p q

main

p q

heap SP NP

5 4 3 2 1 ujp 0 ujp

I salti sono alla fine in ordine inverso ai casi:

Page 41: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Procedure Un programma è strutturato così: program h p1 q1 r1

p2 q2 r2 Possiamo rappresentare il tutto con due alberi:

Albero delle chiamate: program h var X proc p var z proc q var z z:=1 r . . proc r var y proc s x+y q s z:=r r p Per far lavorare correttamente le procedure devo caricare alcune cose nello store: Relativamente ad MP, per ogni chiamata di una procedura, la posizione delle variabili è sempre la stessa. La cosa che varia è MP SL punta alla procedura definita a livello più alto.

Es. per la procedura r, SL punta a P DL punta alla procedura chiamante Es. per la proc. r, nel primo caso DL punat a P, nel secondo punta a q SL serve a valutare le variabili. La valutazione di esse è statica. Se la valutazione delle variabili fosse dinamica, dentro r, z avrebbe due significati diversi nelle due chiamate. Quando viene chiamata da p, la z è la r di p; quando r viene chiamata da q, la z è quella di q. Alcuni linguaggi (es. lisp) hanno una valutazione dinamica delle variabili. EP espansione massima della procedura serve a vedere se sto sconfinando RA indirizzo di ritorno Dopo RA ci sono i parametri formali le variabili locali etc. NESTING DEPTH: profondità di annidamento serve a calcolare il numero di SL che devo percorrere per trovare la definizione della variabile:

1) il programma principale ha profondità di annidamento ∅ 2) se una proc. ha profondità di annidamento n, la sottoproc. hanno profondità n+1

NDD di funzione NDC di chiamata p=1 p=1 q=2 q=4 r=2 r(in q)=3 s=3 r(in p)=2

h | p | r | s | q | r | s

MP

MP

SL DL EP RA

Valore della funzione

Link statico

Si calcola partendo da q e da p (di funzione) (non di chiamata)

Page 42: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

posso definire un albero di link statici dove il padre di ogni nodo è la proc. puntata da SL Supp. di avere una proc.: q NDD n

• questa può essere chiamata dalla procedura in cui è definita: NDC = n • da procedure a qualsiasi livello di profondità dentro p m ≥ n a meno che p non sia

ridefinita a livello più alto ldc lda str base (p, a) = if p=0 then (0,a)=q else base (p-1, STORE[a+1]) ldc p a SP:=SP+1 STORE[SP]:=STORE[base(p,MP)+a] Se p=0 viene preso l’indirizzo MP+a perchè base ritorna MP Se p=1 devo seguire lo static-link e fare un passo indietro: base(1,MP)=base(0,STORE[MP+1]) al posto MP+1 ho il link statico caso p=2

base(2,MP)=base(1,STORe[MP+1])=base(1,MP )=

base(0,STORE[MP +1]) lda p a SP:=SP+1 STORe[SP]:=base(p,MP)+a Str p a STORE[base(p,MP)+a]:=STORE[SP] SP:=SP-1

AMBIENTI DI INDIRIZZAMENTO A – E = NAME -> address x ST f :A->B f’:A->B f’≡f[b/a] significa: f’(x)= introduciamo tre funzioni: elab – specs elabora le specifiche elab – vdecls elabora le dichiaraz. di variabili elab – pdecls elabora le dichiaraz. di procedure elab – specs prende in input delle specifiche, in ambiente di inzirizzamento ρ, l’indirizzo relativo e una profondità statica: elab – specs (var x:t;spec)ρ addr ST= = elab – specs specs ρ[(na,ST)/X](na+1)ST Vediamo come viene sistemato lo store durante la chiamata a procedura Nuove istruzioni macchina p: nst p imposta lo store cup q chiama la proc.

h

p

r q r

s s

Profondità statica

Dove MP è la proc. puntata dal link statico

Funzione che ad ogni nome associa un indirizzo ad una profondita

statica

Indirizzo relativo

Profondità statica

x se x≠a b se x=a

Chiamiamolo na

MP

SL DL EP RA

Page 43: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

mst p significa STORE[SP+2]:=base(p,MP) STORE[SP+3]:=MP STORE[SP+4]:=EP SP:=SP+5 cup q significa MP:=SP-(p+4) STORE[MP+4]:=Pc PC:=q ss p SP:=MP+p-1 se p EP:=SP+p sono le prime istruzioni della procedura chiamata Per mantenere una matrice tipo: A[U1..01,..,Uk..0K] dobbiamo mantenere le specifiche cioè: U1,01,..,Uk,0K e le dimensioni. Es.: elab-specs(value x:array[U1,01,..,Uk,0k] of t;specs)ρ na st = = elab-specs specs ρ[(na,st)|x][(na+2i+1,st)/u1]ki=1 [(na+2i+2,st)/0i]ki=1 code(..)o= ssp na code vdecls . . vjpl procedure e:code stp

CHIAMATE A PROCEDURA La procedura chiamante deve eseguire: mst p cup la chiamata esegue: ss p e se p Quando la procedura ritorna deve fare: SP:=MP PC:=STORE[MP+4] ret f EP:=STORE[Mp+3] (ret. da funzione) if EP ≥ MP then error ret p SP :=MP-1 (ret. da procedura) .. il resto è uguale. Il valore di ritorno non serve perchè .. non è una funzione ma una procedura.

ANALISI LESSICALE

Analizzeremo tre concetti: Token Lexema Pattern Es.: variabile:=2+5 L’analizzatore lessicale inizia a leggere il primo token finchè non trova un nuovo token (es. l’operatore di assegnamento).

MP

SL DL EP RA param. Statici var. locali memoria statica

SP EP

SOURCE ANALISI LESSICALE ANALISI STATICA

SYMBOL TABLE

parser

V|a|r|i|a|b|i|l|e| |:|=|2|+|5|;

Token

Token

Page 44: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Es.: if (x 1 <= x 2) Then Il pattern da i criteri per specificare correttamente un operazione relazionale. Def. di costanti: Es. const p := 3,1416; quando l’analizzatore lessicale legge p, ritorna al parser il token id perché sa che le sequenze di carattaeri alfanumerici che iniziano per lettere sono id. Il lexema di questo token è p. Quando legge const, l’analizzatore lessicale ritorna il token const perché const è una parola riservata. Stat -> if cond_stat (in questi casi il token e il lexema coincidono con if,then,else) then stat_list else sta_list stat_list -> stat | begin stat_list end 3.1416 viene riconosciuto dall’analizzatore sintattico perchè è un sequenza di numeri con un punto. Il pattern capisce tutti i possibili lexema che corrispondono ad un token. Ad esempio, al token if potrei associare tre lexema: if, ifi, fi. In questo caso, scrivendo fi, verrebbe interpretato dall’analizzatore lessicale come token if. L’analizzatore lessicale non può capire subito se c’è un errore. Es.:

const p:=3,1415; quando legge const non sa che è un errore perché potrei avere const : integer; che è corretto. Per l’analisi lessicale viene usato un buffer diviso a metà: l’analizzatore legge il buffer in maniera circolare. Appena finisce di leggere una delle due parti, il sistema la riempie con i nuovi dati. Possiamo vedere i token come insiemi, i lexema come elementi degli insiemi e i pattern i modi di descrivere gli insiemi. Questi insiemi vengono descritti attraverso grammatiche. Consideriamo ∑={simboli alfanumerici + punteggiatura e confronto}. Un identificatore è specificato dalla seguente espressione regolare: id -> lettera(lettera|cifra)* lettera -> a|A|b|B|…|z|Z digit -> 1|2|..|9|0 Regole per definire i numeri: (senza segno) num -> digits optional_fraction optional_exp digits -> digit + digit -> 0|1|2|..|9 optional_fraction -> .digits|ε optional_exp ->(E(+|-|ε)digits)|ε l’espressione 1. non viene associato a nessun toekn. Es. x=1.; è errata; x=1.0; è corretta; x := 3 y := 1 il token corrispondente è sempre id. L’analizzatore lessicale passa al parser una coppia: < token, attributo > nel caso particolare l’analizzatore pass: < id, puntatore > puntatore punta ad iun entry nella tavola dei simboli corrispondente all’id. L’analizzatore usa due funzioni: get_token() build_token() cerca l’elemento nella tav. dei simboli e lo ins. se non c’è. Es. di grammatica Stmt -> if expr then stmt if expr then stmt else stmt ε expr -> term relop term | term

term -> id | num relap -> < | <= | = | > | >= id -> letter(digit|letter)* num -> digit+(.digit+)?(E(+|-)?digit+)?

Ci riferiamo a questa grammatica per l’analizzatore lessicale. L’analizzatore lessicale deve eliminare i blanck, i tab, i ritorno a capo, etc. ws –> delim+ delim -> blanck | tab | newline pattern per riconoscere white space (caratteri superflui)

Il token associato all’operatore è relap.

Page 45: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Expr regolare Toekn attributo if if - then then - else else - ws - - id id ⇑ tavola simboli num num ⇑ tavola simboli < relop LT > relop GT potrei ritornare il valore numerico oppure costruisco un elem. nella tavola dei simboli LT, GT significano less to e greater to Il problema del riconoscimento dei token si risolve con gli automi a stati finiti. Useremo quindi i digrammi di transizione Questo è un diagramma di transizione relativo a <=. Se non riconosco <=, fallisco e devo provare un altro diagramma di transizione. Si ha un problema col riconoscimento dei numeri. Es. 1 intero

1.2 reale 1.3E5 con esponente (sono tutti accettati)

Se provo il riconoscimento di intero nel terzo caso, riconosco 1, ritorno il token e poi .3E5 non so come interpretarlo. Per risolvere il problema devo provare a riconoscere prima il terzo caso, poi il secondo e poi il primo. L’analizzatore deve trovare il token massimale (quello con massima lunghezza). In questo caso l’analizzatore tenta di passare nello stato 1, se riesce, tenta di passare allo stato 2, se riesce ha finito altrimenti prova con lo stato 3, e poi con lo stato 4. L’ordine delle prove ha importanza. L’analizzatore, quindi, non si ferma quando trova un matching ma solo quando non può trovare un matching più grande. Definizione delle parole chiave: Posso fare un diagramma di transizione che riconosce le parole chiave oppure è più semplice considerare le parole chiave come identificatore e inizializzare la tabella dei simboli con i token relativi alle parole chiavi. Esempio di automa per il riconoscimento die numeri: num -> digit+(.digit+)?(E(+|-)?digit)? Operatore di look ahead Serve a fare il matching con parole chiave dipendentemente dal contesto: r1 / r2 significa fai un matching con r1 se a seguire c’è r2 Es. if / letter (letter|digit)* then do / ({letter}|{digit})*=({letter}|{digit})* look ahead ( contesto) Se dopo la parola non c’è il contesto giusto, il matching non avviene e la parola può essere vista come una variabile: Es.: do i non avviene il matching Consideriamo la seguente espr. regolare: (a|b)*abb Voglio costruire un automa finito non deterministico che abbia lo stesso linguaggio. Per fare ciò costruisco l’albero di parsing dell’espressione regolare

0 1 2

6

< =

other

get_token() fallimento

*

0 1 2

3

< =

>

4

other

Return (relop, LE) Return (relop, NE) Return (relop, LT)

0 1 2 3 4 5 6 7 digit

digit

. digit E + / -digit digit

digit other

Page 46: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Dobbiamo mettere assieme diverse automi secondo la struttura dell’albero di parsing Riconosce ε Riconosce a Riconosce b Automa che riconosce ρ1 | ρ2 dove r1 è l’automa che riconosce ρ1 e r2..ρ2

Costruiamo l’automa che riconosce r* Automa che riconosce r1 r2 Gli stati finali dei vecchi automi non sono più stati finali. Nel nuovo automa ho un nuovo stato finale. Adesso posso costruire il mio automa ricursivamente attraverso l’albero di parsing costruito prima. Vogliamo passare da un automa finito non deterministico ad un automa deterministico Introduciamo il concetto di ε-chiusura ε-cl(q)=insieme di stati a cui posso arrivare attraverso ε-transizioni

.

b.

b.

* a

a b

1 i i

2 f f

ε a b

i f

r1

r2

ε ε

ε ε

Nel caso in cui voglio un solo stato finale

i r f ε ε

ε

i i r1 f i r2 f f ε ε ε

0 1 6 7

2 3

4 5

8 9 10

a b b

Page 47: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

ε-cl(0)={0,1,2,4,7} ε-cl(T)=UTs

scl∈

− )(ε per T insieme di stati

Definisco un’altra funzione: move(T,a)=T’ T’ è l’insieme degli stati che posso raggiungere da uno stato di T leggendo a Devo calcolare ε-cl(move(T,a)) La nuova funzione di transizione δ(T,a) è proprio quest’insieme: Es-.: T=ε-cl(0)={0,1,2,4,7} move(T,a)={3,8} ε-cls(move(T,a))=ε-cl({3,8})=3,6,7,1,2,4,8} Dstates={stati dell’automa D} Dstates={ε-cl(0)}; while ∃ T ∈ Dstates unmarked do mark T; for each a ∈ A do U:=ε-cl(move(T,a)); if U ∉ Dstates then add U to Dstates unmarked Dtrans[T,a]:=U End-while

L’analizzatore lessicale si occupa anche di risolvere le macro. Le due fasi vengono trattate separatamente perche la prima fase fa uso di automi per il riconoscimento, mentre la seconda fa uso di grammatiche. Inoltre la separazione delle fasi garantisce la portabilità. I token sono i simboli terminali della grammatica. Es. nel pascal i token sono gli id, i numeri, gli operatori. I lexemi di un token sono tutte le possibili stringhe che identificano un token. Es. Token Lexeami Relation <,>=,<=,=,>,≠ L’insieme dei lexemi associata ad un identificatore costituiscono un linguaggio regolare. Token Es. di lexema Descrizione lexemi if if if num 3.14 6.02E12 qualunque numero L’analizzatore lessicale passa all’analizzatore sintattico il token con tutti gli attributi. Per specificare tutti i lexemi associati ad un token utilizzo le espressioni regolari oppure definizioni regolari. Definizioni regolari: sono una serie di definizioni di questo tipo: d1 -> r1 d2 -> r2 d3 -> r3 .

.

dn -> rn

d1,d2,…,dn = simboli non terminali r1,r2,…,rn = sono espressioni regolari che utilizzano l’alfabeto dei simboli terminali + simboli non terminali già definiti. Es. lettera -> A|B|..|Z|a|b|..|z cifra -> 0|1|2|..|9 id -> lettera(lettera|cifra)* E’ possibile scrivere la definizione regolare attraverso un’unica espr. regolare ma questa notazione è più “friendly”. Definizione regolare di un numero: digit -> 0|...|9 digits -> digit(digit)* op_fraction -> .digits|ε op_exp -> (E(+|-|ε)digits)|ε num -> digits op_fraction op_exp [a-z] si possono usare per indicare [0-9] una lettera o un numero Es. digits -> [0.-9]+

Analizzatore lessicale

Analizzatore lessicale

INPUT

GetNextToken

Fasi Successive

Tabella dei Simboli

Page 48: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

lex.l Lex yy.c a.out Lex è un generatore di analizzatori lessicali. Regole lessicali Regola Grammaticali Input Alfabeto Sintattico Lex crea l’analizzatore lessicale. Yacc crea l’analizzatore sintattico e i due analizzatori creano l’albero sintattio. Un programma lex è fatto così: 1) dichiarazioni; 2) regole di produzione; 3) procedure ausiliarie 1) vi sono le dichiarazioni di variabili e costanti e delle definizioni regolari che mi serviranno

per le regole di produzione. Es letter [A-Z,a-z] quando dovrò usare lettere metterò letter 2) E’ una tabella con due colonne a sx metto le espressioni regolari che denotano i token a dx metto

l’azione corrispondente. Es. letter (letter|digit)* return (id) id return (if) 3) sono procedure ausiliarie scritte in C Vengono eseguite le istruzioni relative ad un solo pattern, cioè il primo trovato, quindi ha importanza l’ordine con cui si scrivono le regole. Espressioni regolari in lex: “ “ : testo; \ : elimina il significato speciale dei simboli riservati agli operatori. [ ] : indica un range ^ : denota inizio linea ⊄ : fine linea [^ ] : classe complementare (tutto escluso …) ? : zero o un occorrenza * : zero, un o più occorrenze + : una o più occorrenze | : unione / : contesto a destra: Es. ab/cd ab solo se seguito da cd < > : contesto a sinistra {} : range di ripetizione di una stringa. Es a{1,5} significa a, aa, …, aaaaa L’algoritmo di ottimizzazione degli automi. Es. expr. Regolare (a|b)*abb Vogliono costruire direttamente l’automa deterministico. Metto un # alla fine dell’espressione e costruisco un albero sintattico dell’espressione regolare (a|b)*abb# . . # 6 . b 5 . b 4 a 3 * | | a b 1 2 Considero alcune funzioni associate alle posizioni: nullable first pos last pos follow pos

Compilatore lex

Compilatore C

LEX yacc

Page 49: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

followpos(i) è l’insieme delle posizioni che seguono la posizione i j ∈ followpos(i) se ∃ una stringa xy tale che x è in pos i e y è in posizione j Es. vediamo che è il follow pos (1) nell’esempio precedente: i ∈ followpos (1) se ab ∈ L del sotto albero aventi foglie a e b followpos(1)={1,2,3} first pos sono le posizioni che possono iniziare una parola: Es. . * a (3) (a|b)*a | | a(1) b (2) firstpos ={1,2,3} perché la stringa può iniziare per a, per b o per a alla posizione 3 in questo caso: firstpos={1,2} . + a (3) | | a(1) b (2) lastpos è analogo ai firstpos nullabel significa che può non comparire nella stringa nodo (n) nullable(n) firstpos(n) lastpos n è una foglia ε true ∅ ∅ n è una foglia i false {i} {i} n | nullable(c1)or first pos(c1)∪ lastpos(c1) ∪ c1 c2 nullable (c2) first pos(c2) lastpos(c2) n . nullable(c1)and if nullable(c1) then semantica: bisogna c1 c2 nullable (c2) first pos(c1)∪ scambiare c1 con c2 first pos(c2) else first pos(c2) * | true firstpos(ci= lastpos(ci) c1 Applicando ricursivamente le definizioni della tabella possiamo calcolare il first pos e il last pos di tutto l’albero. First pos = {1,2,3} Last pos = {6} Calcolo del followpos(n) 1) n nodo . . c1 c2 se i ∈ followpos(c1) followpos(i)⊇ firstpos(c2) 2) n nodo * * | c1 se i ∈ lastpos(c2) followpos(i)⊇firstpos(c1) applicando la definizioni trovo che: followpos(1)={1,2,3} followpos(2)={1,2,3} followpos(3)={4} followpos(4)={5} followpos(5)={6}. Costruisco l’automa mettendo come nodi tutte le posizioni e mettendo gli archi da j a i se i ∈ followpos(j)

Page 50: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Costruzione dell’automa partendo da first pos, last pos, follow pos

1 {1,2,3} 2 {1,2,3} 3 {4} 4 {5} 5 {6} 6 –

Algoritmo: utilizziamo D e δ D:=∅ D:={firstpos(radice)} ‘lo inserisco non marcato’ While ∃ S ∈ D, S non marcato Begin Mark S For each simbolo x calcolo il followpos delle posizioni associate al simbolo, se non è in D

lo aggiungo e aggiungo una relazione in δ tra S e il nuovo insieme (nuovo stato)

Supponiamo di avere due stati q1 e q2 in un automa. Si dicono distinguibili se partendo da q1 e q2 leggendo ω arrivo in uno stato accettante o non accettante a secondo dello stato q1 o q2. Vogliamo trovare le classi di stati non distinguibili. Supponiamo che δ(q,x) è definita per ogni simbolo x. Se non è definita per qualche x pongo

δ(q,x)= q , aggiungendo lo stato q (detto stato sink). Input DFM Q ≥ F 1) ∏={F,Q/F} partizione

prendo due stati di F e vedo se sono distinguibili. In caso affermativo divido F in due classi. Continuo fino a quando le classi non sono più spezzabili. Alla fine creo un partizione: ∏={a1,Q2,..,Qn}

(*) Se trovo due stati nello stesso Qi, che attraverso una stringa arrivano in due stati, Qi,Qk diversi, so che lo stato Qi va spezzato perché esisterà una stringa che porta ad uno stato finale da un lato e non finale dall’altro.

La prova di distinguibilità la faccio solo con simboli dell’alfabeo. Se due stati non sono distinguibili con simboli dell’alfabeto, potranno essere distinti ad un passo successivo usando la proprietà (*). Il processo si ferma quando si ha la seguente condizione: ∀ j Qj ∀ q, q’ ∈ Qj, ∀ x∈A A=Alfabeto δ(q,x)∈Qi δ(q,x)∈Qi VARIABILI E PROCEDURE IN LEX yytext nome dell’identificatore yylval attributo del token yylen lunghezza dell’ultimo lexema yymove concatena il lexema riconosciuto col successivo yyless(n) conserva i primi n caratteri letti in yytext e restituisce il resto al file di input input( ) restituisce il successivo carattere di input output(c) scrive c sul file di output input(c) restituisce il carattere c al file di input yylex( ) richiesta di token dall’analizz. Sintattico OPERATORE DI LOOK AHEAD Es. IF(i,j(=3 iin fortran significa assegna 3 all’elemento i,j dell’array IF

3 4 5 6

1 2

B b #

a

a

a a

a

a

{1,2,3} {1,2,3,4}

a

Page 51: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

IF può essere un istruzione se è eseguta da un certo contesto. IF /\(.*\){letter} quando c’è questo contesto a destra, if rappresenta la parola chiave (*) yywrap () ritorna 1 per default che significa che quando termina il file deve terminare la compilazione Ridefinendo questa funzione e facendo tornare 0 significa che la compilazione deve continuare. Voglio creare un analizzatore lessicale che riconosce delle parole e via via avere la possibilità di aggiungere nuove parole da riconoscere. Si utilizza un unico pattern e man mano si aggiungono tutti i lexemi nella tabella dei simboli. Viene fatto un matching se viene trovata una parola che si trova nella tabela dei simboli. Vogliamo limitare lo scoope di certe regole, cioè le regole valgono solo se mi trovo in un certo stato. Es. %s CMMT %x CMMT (dichiarazioni di stati iniziali) CMMT deve precedere le regole di cui si vuole limitare lo scoope %s -> stati inlcusivi %x -> stati esclusivi Begin (CMMT) significa inizia la condizione iniziale CMMT (stato CMMT) Es. “/*” Begin CMMT; Regole <CMMT>. relative <CMMT>\n; allo stato CMMT ><CMMT>”*\” BEGIN INITIAL; Gli stati inclusivi sono quelli in cui si applicano le regole con l’etichetta dello stato e senza etichetta. Esclusivi: valgono solo le regole etichettate. L’analizzatore sintattico controlla che la sintatti del programma sia corretta. Esempio di regola grammaticale per la costruzione di una frase: frase -> soggetto verbo [compl. Oggetto] (opzionale) Alcune regole si possono scrivere in questa forma cioè con grammatiche context free. Es. Non è possibile controllare che una variabile sia stata dichiarata prima 0 che il numero di parametri passati ad una funzione sia quello giusto. Cosa fa il compilatore quando trova un errore? Dovrebbe continuare la compilazione. Per fare ciò dovrebbe risolvere l’errore ed andare avanti. Nel fare questo potrebbe introdurre altri errori perché ciò che capisce il compilatore non è ciò che pensava il programmatore. Per continuare il compialatore potrebbe ad esempio, buttare via un parte di programma e continuare in seguito (strategia del panico) Es. supponiamo che c’è un errore: if ……….. then if ………….. . . else . . in questo caso se if-then-else è una sola istruzione (come in pascal) il compilatore butta tutto il costrutto e continua dal primo punto e virgola. Alcuni errori vengono risolti banalmente: Es. x=a*(b+c ; manca la partensi e viene aggiunta If x:=y l’operatore di assegnazione viene sostituito con = Se statisticamente si conoscono degli errori comuni, si può introdurre una grammatica per la generazione degli errori comuni. In questo caso, l’errore viene esattamente identificato. GRAMMATICHE CONTEXT-FREE

A -> α1|α2|α3|..|αa in BNF E -> E op E E –> (E) | - E | id grammaticha per definire le espressioni op-> +|-|*|/|⇑| Esempi di derivazioni: A -> BaC -> baC -> bac

Analizzatore Lessicale

Analizzatore Lessicale

Front End

Tavola dei Simboli Linguaggio intermedio

Page 52: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Albero di parsing A | B a C | | b c Con l’albero di parsing perdo l’ordine di derivazione ma, data una derivazione, l’albero di parsing è unico. Non in tutti i casi è così: Es. stmt -> if expr then stmt | | if expr then stmt else stmt | | other espr. if E1 then if E2 then S1 else S2 Albero di parsing stmt if exp then stmt | E1 If expr then stmt else stmt | | | E2 S1 S2 Altro possibile albero: s t m t if exp then stmt else stmt | E1 if expr then stmt | | E2 S1 I due significati sono diversi.

1) S2 è eseguita se E1 = vera e E2=falsa 2) S2 è eseugita se E1=falsa

Il problema è che la grammatica è ambigua cioè una stringa ha due alberi di parsing diversi. Potrei risolvere il problema così: stmt -> m_stmt | u_stmt m_stmt -> if expr then m_stmt else m_stmt | other u_stmt -> if expr then m_stmt | if expr then m_stmt else u_stmt Problema della derivazione da sinistra verso destra (ricorsione sinistra). Si ha quando ci sono regole del tipo: A -> aα|β Per eliminare la ricorsione sinistra riscrivo la regola in questo modo: a -> βA’|β A’ -> αA’|α In questo caso la soluzione è immediata. Vediamo un altro caso: S -> Aa|b A -> Ac|Sd|t S -> Aa -> Sda (ricorsione sinistra) Per eliminare la ricorsione sinistra devo fare in modo che le regole siano del tipo Ai -> Ajα con j>i Fattorizzazione a sinistra Es. stmt -> i E t st

st -> i E t st e st st -> i E t st

per eliminare il problema fattorizzo la parte comune trattegiata ep -> i E t SS’ S’ -> E S | E L={ω c ω : ω ∈{a,b}*} non è context-free (si dimostra col pumping lemma) Si capisce che non si può controllare la definizione delle varibili usate con grammatiche context-free. L={anbmcndm} nenache questo è c.f. ci dice che posso contare i parametri di una funzione. TOP-DOWN PARSER Supponiamo di voler cercare l’albero di parsing dell’istruzione “cad” dove le regole grammaticali sono: S -> cAd A -> ab|a Procedo così: creo l’albero utilizzando la 1a regola di produzione

Nastro di input

Regole di produzione

c a d

Page 53: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

S c A d confronto le foglie con la stringa di input e vedo che la prima lettera è uguale. Devo portare avanti la tesina e applicare la seconda regola di produzione

S c A d a b controllando le foglie vedo che c’è un errore e quindi devo fare il back-tracking cioè tornare indietro e utilizzare la 3a regola di produzione, anziché la seconda. Voglio evitare il back-tracking In base alle regole di produzione costruisco degli automi (diagrammi di transizione) Es. E -> TE’ E’-> + TE’|E T -> FT’ T’-> *FT’|E F ->(E)|id E: E’: T: T’: F: Questi diagrammi possono essere semplificati: es. il secondo potrebbe venire così Il secondo diventa così perché c’è un arco E’ che sostanzialmente ripete la stessa procedura Relativa ad E’ cioè lo stesso diagramma di transizione. Concatenando E con E’ (perè il diagramma E contiene un arco E’): 1)

Questo lo posso semplificare ulteriormente Lo stesso lo ottengo mettendo insieme i diagrammi 3) e 4)

Grammatica di costruzione delle espressioni aritmetiche

0 T 1 E’ 2

3 + 4 T 2 E’ 6

E

7 F 8 T’ 9

10 * 11 F 12 T’ 13

E

14 ( 15 E 16 ) 17

id

3 4

5

T +

0 T 3 + 4

T

5

E

0 T 3 E 5

+

7 E 9 E 13

*

Page 54: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Sistema per il parsing All’inizio sullo stack c’è il simbolo S seguito da $ Se il simbolo sul nastro e quello sullo stack sono coincidenti e terminali, sposto in avanti la testina del nastro. Altrimenti devo gestire i nastri (nuovi) casi: 1) x=a=$ mi fermo OK 2) x=a≠$ sposto la testina e tolgo il simbolo dallo stack. 3) x≠a guardo M[X,a]. Se ho una derivazione X->uvω metto sullo stack al posto di X Esempio id + * ( ) $ E E->TE’ E->TE’ E’ E’->TE’ E’->E E’->E T T->FT’ T->FT’ T’ T’->E T’->FT’ T’->E T’->E F F->id F->(E) Lo stack varia così Man mano che applico le regole le scrivo da qualche parte in quanto mi servono per costruire l’albero: Ho applicato queste regole E -> TE’ ottenendo il seg. l’albero di parsing T -> FT’ F -> id T’ -> E E’ -> +TE’ Costruzione della tavola di parsing: Consideriamo: first (α) α stringa follow(A) A simb. non terminali first(X) (da tutte le stringhe che posso derivare da X) di cui il primo simbolo è terminali prendo

il 1° seimbolo X) 1) X è un terminale first(X)=X 2) X -> E E∈first(X)

Stack

$

Input buffer Fine stringa

$

OUTPUT

Tavola dei Simboli

M

E’ una matrice Simb. Terminali Simb. Non terminali

U v ω

id + id * id $ E $

T E’ $

F T’ E $

id T’ E’ $

E’ $

+ T E’ $

Incremento la pos. della testina e tolgo id

E

T E’

T E’+ T E

id E

Page 55: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

3) X -> X1X2..Xn first(X)⊇first(X1) se X1*E⇒ devo mettere un first(X) anche first(X2) e così

via. Devo però togliere E a meno che X1,X2,..Xn si annullano tutti Definizione di follow(A) 1) $ $∈follow($) 2) A->αBβ follow(B) <= first(β)/E

3) A->αBβ|αB β*

⇒ E (vedi giù) potrei avere una derivazione del tipo $ => α’Aβ’ => α’αBβ’ quind first(β’) deve andare nel follow di B cioè follow(B) <= follow(A) Es. E -> TE’ E’ -> +TE’|E T -> FT’ T’ -> *FT’|E E -> (E)|id first(E) first(T) first(F)={(,id)} first(E’)={+,E} first(T’)={*,E} follow(E)={$,)} follow(T)={+}∪follow(E’) follow(E’)={$,) contiene follow(E) follow(F)={*,+,),$} contiene follow(E’) follow(T)={+,$,)} Costruiamo la tabella di parsing vogliamo calcolare M[A,a] A -> α a ∈ first(α) Prendo una produzione A -> α e prendo un terminale che sta nel first(α) e aggiungo la regola alla posizione M[A,α]. Se α è annullabile e a ∈ follow(A) posso aggiungere la regola A->α tabella Se E ∈ first(α) e $ ∈ follow(a) metto la regola in M[A,$] in tutti gli altri casi la tabella non viene riempita e se pero qualche input finisce in qualche casella vuota, l’input non viene accettato. Id + * ( ) $ E E->TE’ E->TE’ E’ E’->E E’-> T T’ F In ogni casella ho una sola produzione. La grammatica si dice LL(1) (left(leggo da sx->dx), left (direzione sx)). In alcuni casi potrei avere più produzioni per casella. In questo caso la grammatica è ambigua: posso avere più alberi di parsing per un espressione. Condizioni affinchè una grammatica sia LL(1) 1) se A -> α|β ∃ a tale che a∈first(α), a ∈ first(β)

c.n.s. 2) se A -> α|β non possono essere α *

⇒ E, β*

⇒ E

3) β*

⇒ E ∃ a ∈ first(α), a ∈ follow(A) Non tutte le grammatiche possono essere trasformate in LL(1). Es. grammatiche che genera if-then-else. Quando costruisco la tavola di parsing per questa grammatica, devo eliminare alcune produzioni. ERROR RECOVERY Se il seimbolo sullo stack è divero dal simbolo sul nastro, c’è un errore In questo caso il compilatore segnala l’errore, fa il pop dle “;” e continua (l’utente si è dimenticato il ;) altro esempio: M[A,if]=∅ in questo caso non è possibile trovare una derivazione per if ma il compilatore non sa che fare per continuare

; if

A if

Page 56: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Procediamo per due direzioni: 1) se ho una derivazione del tipo “B->αA if β” posso fare il pop di A e sperare di poter

continuare la derivazione 2) vado avanti col nastro di input buttando token (stato di panico) finchè non trovo un token

con cui si riesce a sincronizzare. La sincronizzazione può avvenire cercando il primo token che sta nel follow(A), quindi si butta via A e si prosegue, oppure fino al primo token che sta nel first di A e quindi proseguo senza togliere A. Es. E -> TE’ E’ -> +TF’|E T -> FT’ T’ -> *FT’|E F ->(E)|id first(E)=first(T)=first(F)={(,id} first(E’)={+,E},first(T’)={*,E} follow(E)=follow(E’)={),$} follow(T)=follow(T’)={+,),$} follow(F)={+,*,),$} (vedi tabella lez. Precedente) Esempi di Errori $ E sullo stack, se tralascio la ) e continuo con E id riesco a proseguire il parsing: E E’ T -> $ E’ T’ F -> $ E’ T’ id (porto avanti il nastro) $ E’ T’ E * tolgo * e sposto la testina $ E’ T’ F non posso proseguire noto che + è nel follor(F) e quindi elimino F e proseguo supponendo che l’utente si è dimenticato un id. Ho costruito un albero di parsing per la string

id * id + id token mancante ) non considerata

Visto che abbiamo eliminato la F: “F -> (E) | id” allora abbiamo supposto che manca un espressione del tipi (…) oppure un id BOTTOM-UP parser Vogliamo costruire un albero di parsing che ci dia una derivazione destra partendo dalla stringa a1,..,an risolvendo fino a S Es.: A -> aABe A -> Abc|b B -> d Sentenza abbcde Procediamo da dx verso sx ho 3 possibilità: sceldo “d” e sost. Con “B”, sost. la seconda “b” con “A” e sost. la prima “b” con “A”. Supponiamo di scegliere “d” abbcde sostituisco b con A abAcBe sostituisco b con A aAAcBe non posso più sostituire nessuna sottostringa coincide con la parte dx di una produzione dobbiamo fare back-traking: Dato che dobbiamo fare una derivaizone dx, l’ultima derivazione considera le sottostringe più a sx quindi dobbiamo procedere sempre da sx verso dx. abbcde aAbcde in questo caso non devo prendere “d” perché è più a dx ma non so se ridurre Abc solo b riduco Abc: aAde aABe $ Qunando facciamo una riduzione facciamo a ritroso quest’operazione.

αAγ β>−

⇒A

αβγ γ è sempre una stringa di terminali perché riduciamo da sx a dx.

) id * + id $ Potrei notare che ) ∈ follow(E) e quindi togliere E dallo stack ma in tal caso ho finito e non proseguo il parsing

Page 57: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Vediamo l’albero di parsing che se ne deduce: S a A B e A b c d | b Handle: sottostringa uguale alla parte destra di una produzione che possono essere ridotte. Es. b a a a dove A -> aa Sono due Handle diversi (anche se la sottostringa è uguale) Il segreto del Bottom-up parsing è individuare tutte le maniglie (Handle) giuste cioè che portono a S. Es. id1 + id2 * id3

E E -> E + E E –> E * E E + E E -> (E) | E -> id id1 E * E | | id2 id3 E => E + E => E + E * E => E + E * id3 => E + id2 * id3 => id1 + id2 + id3 E’ come se ad ogni istante taglio una parte dell’albero fino alla radice. Questo procedimento di può implementare con uno stack: Reduce-Shift: L’operazione di Shift consiste nello spostare la testina, Reduce Elimina i caratteri dallo stack e mette il simbolo a sx della produzione. Es. $ id sullo stack ho un maniglia devo decidere se fare lo shift o il reduce. Decido di fare reduce. ho continuato a fare shift $ E + id reduce $ E + E ho una maniglia e quindi un conflitto shift-reduce Per risolvere il conflitto dovrei andare avanti e vedere se c’è un operatore con predenza maggiore $ E + E * id trovo $ e quindi posso fare solo riduzioni $ E + E * E anche se ho una maniglia E E id E * E Non ho conflitti perché si considera sempre la maniglia che parte dal top dello stack. Potrei avere due handle che partano dal top: conflitto reduce-reduce. Es.: di conflitto shift-reduce: stmt -> if E then stmt | if E then stmt else stmt Es.: di conflitto reduce-reduce stmt -> id (par_list) par_list -> par_list,parameter | parameter parameter -> id expr -> id(expr_list) expr_list -> expr_list,expr|expr expr -> id $ id ( id stack , id nastro in questo caso non so se ridurre id con expr o con parameter. Non c’è modo di risolvere questo problema con questo parser. Parsing basato sulla precedenza operatori Operator grammars: non hanno ε-produz. E nella parte destra non ci sono simboli non terminali attaccati. Ogni forma sentenziale sarà del tipo:

id + id * id $

id + id * id $

Page 58: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

ε

S*

⇒ a1β1..aiβi..akβk βi= 1 non terminale Es. E -> EAE | (E) | -E | id A -> +|-|*|/|⇑ Quest non è op-grammar ma si può trasformare: E -> E + E | (E*E) E – E | E / E | E ⇑ E| (E)| - E | id Questa gr. soddisfa le condizi.

Definiamo operatori <⋅, ⋅

=, ⋅> (precedenza) dove si puo avere a <⋅ b b <⋅ a Es. a – b + c per l’associatività è a sx si ha -⋅>+ altrimenti (a dx) -<⋅+ Tavola di precedenza: (Es.) simboli a dx simboli a sx id + - * $ id ⋅> ⋅> ⋅> ⋅> + <⋅ ⋅> ⋅> <⋅ ⋅> - <⋅ ⋅> ⋅> <⋅ ⋅> * <⋅ ⋅> ⋅> ⋅> ⋅> $ <⋅ <⋅ <⋅ <⋅ <⋅ Es. $ id + id * id $ trasf. la stringa $ <⋅ id ⋅> + <⋅ id ⋅> * <⋅ id ⋅> $ ho fatto seguire tutti i simboli dall’operatore corrispondente. Quando trovo un <⋅ faccio lo shift. Quando trovo un ⋅>+ faccio il reduce fino al primo <⋅ che incontro Es.: stack $ <⋅ id ⋅> faccio un reduce $ E + <⋅ id ⋅> $ E + E * <⋅ id ⋅> $ E + E * E $ posso isolare la string delle precedenze ocn la stringa delle variabili $ <⋅ + <⋅ * ⋅> $ rappresenta una maniglia che è : E*E e diventa E alla fine trovo $ E $ ho trovato la derivazione bottom-up nel libro c’è un algoritmo che esegue queste operazioni: l’esponenziazione è associativa a dx: E ⇑ E ⇑ E si intende E ⇑ (E⇑) Parentesi: ( <⋅ ( ) ⋅> )

(⋅

=) ) ( non è definita (è un errore) Supp. di voler generare questa stringa: id * (id + id)*id E * (E+E)* id E * (E)*id Considero gli operatori:

$ <⋅ * <⋅ (⋅

=)⋅> * ⋅> $ è come se non ci fosse maniglia: faccio la riduzione Una tavola di precedenza occupa n2 caselle di memoria. Posso ottimizzare la cosa usando delle funzioni: l(x) ed r(x) (l=left r=right) x <⋅ y se l(x) <⋅ r(y) con questa notazione usa r⋅n caselled di memoria anziche nr ma ogni casella è più grande perché deve

contenere un intero anziché un operatore (<⋅,⋅

=,⋅>) che può essere memorizzato in due bit.

Page 59: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

LR PARSING Viable prefix: (prefisso fattibile/possibile)

$ *

⇒ ..=> αβ è qualcosa che posso trovare sul top dello stack che non val al di la della maniglia che stò costruendo Es. id + id * id F -> id E + F * è qualcosa che posso trovare sul top dello stack. Questo tipo di parser riconosce i prefissi possibili e poi decide quale prendere. Come funziona? Posso avere stati e simboli della grammatica Ad ogni istante guardo uno stato o un simbolo e un input. Action(Sj,a) può avere uno dei seguenti valori:

1) shift Sj 2) reduce by A->γ

se |γ|=r, quando faccio reduce devo togliere 2r caselle dello stack (perché ad ogni simbolo corrisponde una coppia stato simbolo). goto (Sn,a) mi dice in quale stato devo andare quando trovo lo stato Sn e il simbolo di input a. vi sono altre due possibilità:

3) accpet 4) errore

Es.

1) E->E+T 2) E->T 3) T->T*F 4) T->F 5) F->(E) 6) F->id

N.B le gr. che possono essere computate da un LR si chiamano grammatiche LR non tutte le c.f. sono

LR ma gran parte di esse si. Stati id + * ( ) $ GOTO(E) GOTO(T) GOTO(F) 0 shift5 shift 1 2 3 1 s6 acc. 2 r2 s7 r2 r2 3 r4 r4 r4 r4 4 s5 s4 2 2 3 5 r6 r6 r6 r6 6 s5 s4 9 3 7 s5 s4 10 8 s6 s11 9 r1 s7 r1 r1 10 r3 r3 r3 r3 11 r5 r5 r5 r5 N.B. s4 significa shift con lo stato 4 r4 significa riduce con la produzione 4 “id * id + id” stack: o id 5 guardo la table e faccio uno shift

o id 5 reduce (r6) OF3 r4 OT

OT2 OT2 * 7F10 riduci r3 (6 volte il pop)->OT 0T2 0E1+6T9 reduce r1 0E1 accetto (stato 1 e leggo $)

Si xi LR OUTPUT

Tabella action

TabellaGoto

Page 60: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Se creo l’albero con le produzioni che ho usato in ordine inverso ottengo l’albero di parsing della derivazione.Tutte le caselle della tavola vuota sono caselle di errore. Come si costruisce la tavola action e goto? Consideriamo grammatiche SLR. item LR(0): è una produzione che corrisponde a una produzione della grammatica più un puntino: Es.: T -> T * F Ottengo T -> .T*F T -> T.*F T -> T*.F T -> T*F. Il puntino divide la parte destra a metà e significa: ho derivato la parte prima del punto, mi aspetto di derivare attraverso la parte dopo il puntino. Considero un insieme di items I. Considero la chiusura di I closure(I) Se la produzione A ->α.Bγ ∈ closure(I) inserisco B -> .β in closure(I) dove B->β è nella grammatica. Es. E’-> E -> considero questa produz. In più E -> E + T | T T -> T * F | F F -> (E) | id I={E’ -> .E} Closure (I)={ E’ -> .E E -> .E+T|.T T -> .T*F|.F F -> .(E)|.id } Per considerare l’insieme posso memorizzare solo le variabili a sx. Es. E’,E,T,F Significa tutte le produzioni che iniziano per E’, T, ed F I viene chiamato Kernel o cuore o base perché da esso (insieme massimale) è possibile generare l’insieme più grande che mi serve (la chiusura) Considero: goto(I,x) x simbolo di gr. A -> αX.β A->α.xβ ∈ I Es. E’ -> E. E -> E.+T goto(I,x)=tutti gli item di closure(I) dove c’è il . seguito da + poi sposto il . e continuo c:={closure({S’->S’S})} repeat I ∈ C, x ∈ G: goto(I,x)≠∅ goto(I,x) ∈ C (lo aggiungo) until Es. grammatica precedente I0= { E’ -> .E E -> .E * T | . T T -> .F | .T * F | .F F -> .(E)| .id } goto(I0,+)=∅ goto(I0,E)= I1={ E -> E. + T E’ -> E. } goto(I1,+) = I2 ={ E -> E + . T T -> .T*F|F F -> .(id)|.id.} Alla fine troveremo esattamente 12 stati goto(I,x) funziona così: se A -> α.xβ ∈ I allora metto nell’insieme di ritorno la chiusura di A -> αx.β Partendo dalla gramamtica creiamo la successione di stati: I0={ E’ -> .E E -> .E + T | .T T -> .T * F | .F F -> .(E) | .id} I simboli della grammatica sono $ E’ E T F ( ) + * id I1=goto(I0,E)=closure({E’ -> E., E->E.+T})= { E’ -> E., E –Z E. + T} non ci sono prod. che hanno . seguito da simbolo non terminale. I2=goto(Io,T)=closure({E->T.,T->T.*F})=E->T.,T->T.*F} I3=goto(I0,F)=closure({T->F.})={T->F.} I4=goto(I0,()=closure({F->(.E)})={F->(.E), E->.E+T|.T, T ->.T*F|F,F-> .id}

Page 61: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

goto(I0,))=∅ non ci sono produc. Con .) goto(I0,+)=∅ goto(I0,*)=∅

I5=goto(I0,id)=closure({F->id.)={F->id.} Noto che in I1, l’unico simbolo che si trova alla dx di . è il + e quindi goto (I1,x) per x≠+ è ∅ I6=goto(I1,+)=closure({E->E+.T})={ E -> E + .T T -> .T * F T -> .F F -> .(E) F -> .id } Ogni volta controllo che lo stato non esiste già, in tal caso non lo ripeto I7=goto(I2,*)=closure({T -> T * .F})={T -> T * .F, F -> .(E), F -> .id} goto(I4,( )I4 (sviluppando si vede ciò) I8=goto(I4,E)=closure({F->(E.),E->E.+T})={F->(E.),E->E.+T} goto(I11,T)=I2 goto(I4,F)=I3 I9=goto(I6,T)=closure({E->E+T.,T->T.*F})={E->E + T., T -> T. *F} Usando I6 con gli altri simboli trovo stati esistenti. I10=goto(I7,F)={T->T*F.} I11=goto(I8,) )={F->(E).} Non ci sono più stati. Costruiamo il DFA Computando la stringa utilizzando quest’automa, arriverò ad uno stato. Gli item di quello stato sono gli item validi per la stringa. Es. E + T * Arrivo in I7={ T -> T * .F 1), F -> .(E) 2), F -> .id 3) }

1) E’ => E => E + T => E + T * F 2) E’ => E => E + T => E + T * F => E + T * (E)

Adesso passiamo a costruire la tavola di parsing: faccio corrispondere ad Ii lo stato i. Supp. di avere in un item ∈ Ii del tipo: A -> α.aβ ∈ Ii metto nella tabella al posto (Ii,a) lo shift dello stato, corrispondente a goto(Ii,a) se A->α.aβ ∈ Ii allora: action[i,a]=goto(Ii,a) shift dello stato Es. action[r,*]=goto(I2,x) Se ho invece: A -> α. devo fare il reduce: action[i,a]=reduce(follow(a)) per tutii gli a che sono nel follow(A) Se nella costruzione della tavola ho più scelte possibili mi trovo in una situazione di conflitto. I0={ S->.S S->.L=R S->.R L->.*R L->.id R->.L} I1={ S’->S.} I2={ S->L=R R->L.} I3={ S->R.} I4={ L->*.R R->.L L->.*R L->.id} I5={ L->id.} I6={ S->L=.R R->.L

I0 E I1 + I6 T I9

I2 * I7 F I10

I3

I4 E I8 ) I11

I5

T

F

(

id

T

F

*

)

Page 62: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

L->.*R L->.id} I7={ L->*R.} I8={ R->L.} I9={ S->L=R.} Creando la tabella (e quindi calcolando i follow) si vede che c’è un conflitto shift-reduce. Per avviare a questi problemi possiamo agli item LR1 che sono una accoppiata tra un LRO e un simbolo [A->α.β,a] β≠ε [A->α., a] in questo caso riduco solo se nel nastro di input ci sta’ a. Questo perché il follow di a era troppo grande e quindi scelgo solo alcuni simboli per cui fare la riduzione. Non possiamo avere conflitti shift-shift perché l’automa che viene fuori è deterministico. Non posso avere conflitti reduce-reduce perché la grammatica SLR non è ambigua. Prendo la produzione B->.η e metto nella chiusura l’item:

[B->.η,b] closure({A->α.Bβ,a})=

aggiungo l’item [B->.η,b] dove b ∈ first(β,a) goto(I,x)= [A->α.xβ,a] metto la chiusura di: {[A->αx.β,a]} Creo l’insieme di stati C mettendo I0 e poi introducendo tutti i nuovi stati. Esempio: S’->S S ->CC C ->cC|d Il linguaggio generato è c*d c*d costruizione degli stati: closure([S’->.S,$]) confrontando con [A->α.Bβ,a] ottengo A=S’, α=ε, β=ε, B=S, a=$ ottengo le seguenti produzioni I0= [S’->.S,$] [S ->.CC,$] first(C.$)={c,d} [C ->.cC,c|d] [C ->.d,c|d] goto(Io,S)=closure({S’->S.,$]})={[S’->S.,$]}=I1 goto(I0,C)=closure({[S->C.C,$})={[S->C.C,$],[C->.cC,$],[C->.d,$]}=I2 goto(I0,c)=closure({[C->c.C,c],[C->c.C,d]})={[C->c.C,c],[C->c.C,d],[C->.d,c|d],[C->.cC,c|d]}=I3 goto(I0,d)={[C->d.,c|d]}=I4 I5={[S->CC.,$]} I6=closure({[C->c.C,$]})={[C->.cC,$],[C->.d,$],[C->c.C,$]} I6 differisce da I3 solo per il simbolo di look-ahead I7={[C->d.,$]} I8={[C->cC.,c|d]} I9={[C->cC.,$]} Costruzione della tavola di parsing: Se [A->α.bβ,x] ∈ Ii faccio goto(Ii,b) e tolgo il simbolo dallo stack Se [A->α. ,b] ∈ Ii faccio una riduzione Accetto quando in Ii c0p [S’->S.,$] La grammatica LR(!) ha in generale molti più stradi di una LR(0) si possono semplificare le cose non distinguendo (mettendo insieme) gli stati con look-ahead diverso. Supponiamo di avere uno stato: Ij ho un conflitto shift-reduce ma in tal caso il conflitto doveva esistere prima A->α.,a B->β.aγ,b Stato ottenuto mettendo due stati. Può succedere, invece che si venga a creare un conflitto reduce-reduce. Es. S’->S S -> aAd|bBd|aBe|bAe A -> c B -> c {[A->c.d,],[B->c.,e]} è uno stato valido per il prefisso ac e ciò si può verificare costruendo l’automa {[A->C.,e],[B->c.,d]} valido per bc

Ii i X

Page 63: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Linguaggio generato: acd, bcd, ace, bce unedo i due stati ottengo {A->c.,d|e],[B->c.d|e]} Se trovo d sul nastro, posso ridurre con la prima o con la seconda: conflitto reduce-reduce. Anziché creare la tabella e poi semplificarla si può creare direttamente la tabella semplificata. Ad esempio, si può utilizzare solo il Kernel anziché la chiusura. Ciò porta ad una costruzione più semplificata (i dettagli li studierà chi vuole fare il progretto). Se considero la stringa HORSE AND CART succede questo: HORSE deve essere sostituito da un porta sx non so se prendere CART-ANIMAL oppure WORK_ANIMAL. Per sapere quale usare dovrei andare due token avanti. “yacc” risolve i conflitti shift-reduce, in favore dello shift, mentre i conflitti reduce-reduce li risolve a favore della prima regola incontrata. In un programma “yacc” vi sono tre serzioni:

1) sez. delle definizioni 2) sez. delle regole 3) sez. delle procedure

es. di sez. 1 % token NAME NUMBER N.B. vanno dichiarati i token che sono costitutite da più di un carattere Sez. delle regole: es statement:<NAME=expression> oppure left_site: alt1 {sem. act. 1} |alt2 {sem. act. 2} sem.atc. = azione semantica: insieme di azioni da eseguire quando viene usata la produzione corrispondente. “yacc” E’ un generatore di analizzatori sintattici. Grammatica: statemet -> NAME = expression expression -> NUMBER + NUMBER | NUMBER – NUMBER Es. fred=12+3 produce quest’albero di parsing: Il parser generato da “yacc” è di tipo shift-reduce LALR. “yacc” non tratta alcuni tipi di grammatiche:

- ambigue - gramma. con più di unsimbolo di look-ahead

Es. phrase -> cart_animal AND CART | Work_animal AND PLOW cart_animal -> HORSE | GOAT work_animal -> HORSE | OX Questa grammatica non è ambigua. Si può definire la precedeza e l’associatività degli operatori: % left “+” “-“ % left “*” “/” % non assoc. UMINUS hanno la precedenza gli op. dichiarati più in basso e l’associtatività e a sx, le precedenze delle regole invece sono maggiori per le regole più in alto. P1=token da shiftare P2=regola da ridurre p1 > p2 shift p1 < p2 reduce p1 = p2 assoc. a sx reduce p1 = p2 assoc. A dx shift altrimenti erore

statement

NAME fred

_ _

expression

NUMBER12

+ NUMBER3

Page 64: Alfabeto (sigma). ={0,1} allora ‘0101’ è una parola nell’alfabeto. … · 2003. 1. 27. · LINGUAGGI FORMALI E COMPILATORI Introduzione: Stringhe, Alfabeti e Linguaggi •

Definizione tabella dei simboli. Questa viene acceduta sia dal parser che dal lexer e quindi la definizione va nel “.h” - yyparser fa partire il parser - yylex fa partire il lexer partendo dall’ultima posizione - yyerror (char * message) {printf (strden, “%s\n”,message);} - yyabort interrurzione della compilazione - yyclearin elimina il simbolo di look-ahead se è stato già letto - yydebug da la possibilità di generare il codice traccia per il debug - t nella riga di comando fa la stessa cosa - % { #DEFINE yydebug 1 } % - yyerrok per appensatire i messaggi di errore, dopo un errore i messaggi non vengono

disabilitati per tre token