Pumping lemma per i linguaggi Context-free

Transcript

Pumping lemma per i linguaggi Context-free
Pumping lemma per i linguaggi Context-free
Sia L un linguaggio context-free. E’ possibile determinare una costante k, dipendente da
L, tale che qualunque stringa z ! L con | z | > k si può esprimere come z= u v w x y in
cui:
1. | v w x | ! k
2. | v | + | x | > 0
3. u vi w xi y ! L per i " 0
Es1: mostrare che il linguaggio
L = {an bn cn con n " 0 } non è context-free
Es2: mostrare che il linguaggio
2
L = {am m " 1 } non è context-free
Pumping lemma per i linguaggi Context-free
Per la dimostrazione del Pumping Lemma si fa uso delle grammatiche in forma
normale di Chomsky, per le grammatiche in tale forma si ha che ogni nodo interno
ha due figli, ad esclusione di quelli che hanno foglie come sottoalberi.
Lemma - Sia w una stringa terminale prodotta dall’albero sintattico se h è l’altezza
dell’albero si ha che | w | ! 2h-1
Si consideri ora una grammatica G = < VN,VT,P, S > tale che L(G) = L-{#} e sia m = | VN |, si sceglie
k= 2m e quindi per le condizioni poste dal pumping lemma la stringa z sarà lunga almeno k. Per il
lemma precedente un albero sintattico il cui cammino più lungo sia di lunghezza m, o meno, ha un
prodotto di lunghezza 2m-1 = k/2 o meno. Un tale albero sintattico non può quindi produrre la stringa
z, cioé ogni albero sintattico che produce z deve essere di altezza almeno pari a m+1.
Ora, essendo m = | VN | , si ha che nel percorso dalla radice alle foglie ci saranno almeno due simboli
non terminali uguali (per il principio della piccionaia).
Supponiamo che A = Ai = Aj
Pumping lemma per i linguaggi Context-free
S
L’albero di derivazione avrà la seguente struttura
dove ciascun triangolo rappresenta il sottoalbero generante le varie
parti della stringa.
A
Essendo che A = Ai = Aj possiamo costruire altri alberi sintattici
tramite sostituzione a partire dall’albero originale.
A
S
S
A
A
A
u
x
v
v
w
u
A
y
x
albero di derivazione per la stringa
uv2wx2y
w
u
x
albero di derivazione per la stringa
uwy
v
w
z
x
y
Proprietà di chiusura dei linguaggi Context-free
Teorema: I linguaggi context-free non sono chiusi rispetto all’intersezione e alla
complementazione
Intersezione
considerato che i due linguaggi L = {anbncm | n,m" 0} e L = {ambncn | n,m" 0} sono context
1
2
free, il linguaggio intersezione è costituito da {anbncn | n,m" 0} che sappiamo non essere contextfree.
Complementazione
Se L1 e L2 sono context free allora anche L = L # L dovrebbe essere context free in virtù
1
2
dei risultati sulla unione, ma siccome per la legge di De Morgan L= L1 " L2 questo
equivarrebbe a dire che i linguaggi context-free sono chiusi rispetto all’intersezione,
contrariamente a quanto detto prima.
Tuttavia
L’intersezione di un linguaggio Context-free ed un linguaggio regolare è ancora
un linguaggio context-free
Determinismo e linguaggi context-free
Un automa a pila è in genere non deterministico. Il non determinismo viene espresso da una o
entrambe delle seguenti condizioni:
1. esistono più transizioni del tipo che iniziano con (q, a, A)
2. (q,a,A) e (q,$,A) sono entrambe definite, cioè l’automa può scegliere di leggere il simbolo a
$ oppure di non leggere niente.
A differenza del caso delle grammatiche regolari in cui il non determinismo non aggiungeva potere
computazionale all’automa riconoscitore. L’automa a Pila non deterministico è computazionalmente
più potente dell’automa deterministico. Ciò in virtù del seguente Teorema:
Teorema: La famiglia dei linguaggi riconosciuta dagli automi a pila deterministici è contenuta
strettamente in quella dei linguaggi context- free.
Per dimostrarlo basta considerare la classe dei linguaggi inerentemente ambigui (cioé ogni grammatica
equivalente è ambigua) per cui necessariamente l’automa riconoscitore è non deterministico.
Es. L = {an bn c* | n "0 } # {a* bn cn | n "0 } è un linguaggio Context free inerentemente ambiguo e
quindi non riconoscibile da un automa a pila deterministico
Determinismo e linguaggi context-free
Si dimostra anche che esistono linguaggi non ambigui che sono non deterministici
n n
n 2n
Es1: L = L’# L’’= {a b | n "1 } # {a b | n "1} è non deterministico
intuitivamente l’automa dovrebbe collocare nella pila i primi n caratteri a e cominciare a
disimpilare un a appena trova il primo b se la stringa ! L’ , mentre se la stringa ! L’’ sono due i b
che devono essere letti prima di disimpilare un a.
Es2: il linguaggio L = {w wR} è non deterministico.
Proprietà di chiusura dei linguaggi deterministici
La classe dei linguaggi deterministici è chiusa rispetto alla complementazione.
La classe dei linguaggi deterministici non è chiusa rispetto all’unione
l’esempio 1 della precedente slide
$$
L = L’# L’’= {an bn | n "1 } # {an b2n | n "1}
ne è un esempio, infatti a partire da due linguaggi deterministici si costruisce con la loro unione un
linguaggio non deterministico.
La classe dei linguaggi deterministici non è chiusa rispetto all’intersezione.
Es: L = L1 " L2 = {an bn c* | n "0 } " {a* bn cn | n "0 } = {an bn cn | n "0 }
dove L1 , L2 sono certamente linguaggi deterministici
Analisi sintattica
Obiettivi
! verificare la correttezza sintattica del programma dato in ingresso
! costruire l’albero sintattico
Strategie
! top down (discendente), si parte dal simbolo distinto e si cerca di applicare in qualche
! ordine le produzioni in modo da verificare se la stringa x è generabile dalla grammatica. Cioé
!!
!
S !* x
! Bottom-up, si parte dalla stringa x, si cerca una sottostringa che sia parte destra di una !
! produzione e la si sostituisce con la corrispondente parte sinistra. Questa operazione
viene denominata di Riduzione e viene iterata fino a quando la stringa non si riduce al
simbolo distinto S.
Un cenno sui riconoscitori top-down
Un riconoscitore sintattico top-down basato su grammatiche LL(k) implementa
un automa a pila push-down che attua un riconoscimento per pila vuota.
In genere gli automi che vengono impiegati fanno riferimento all’uso di una
marca di fine stringa $.
Per ottenere il determinismo si sfrutta la conoscenza che proviene dall’avere sotto
considerazione la stringa in ingresso. In caso di indecisione di quale transizione
attuare si considerano i prossimi k simboli che l’automa dovrà leggere per prendere le
opportune decisioni.
Derivazioni Canoniche
E ! E + T | E - T | -T | T
T! T * F | F!
F! (E) | i
E "* a + b*c ?
Derivazione canonica sinistra [sostituzione del primo simbolo non terminale a sinistra]
E" E +T "T + T " F + T" i + T" i + T*F " i + F*F
"i+ i*F " i +i*i
Derivazione canonica a destra [sostituzione del primo simbolo non terminale a destra]
E " E + T" E + T*F " E +T*i " E + F*i " E + i*i
"T + i*i " F + i*i" i + i*i
Albero sintattico risultante
E
E
T
+
T
F
F
i
T
*
F
i
i
c
a
b
Grammatiche LL(k)
Per costruire riconoscitori deterministici occorre agire sulle grammatiche in due direzioni:
1. Porre dei vincoli alla grammatica sì da renderla adatta all’analisi top-down.
2. Utilizzare l’informazione fornita dai simboli successivi alla parte già riconosciuta per guidare
l’analizzatore nella scelta della parte destra con cui espandere il simbolo non terminale corrente.
Grammatiche LL(K)
L - indica che la stringa in ingresso è esaminata da sinistra (Left) verso destra.
L - indica che viene costruita la derivazione canonica a sinistra (Left).
K - indica il numero di simboli di lookahead che l’analizzatore considera per scegliere la parte
!
destra con cui espandere il simbolo non terminale corrente.
Regole euristiche
L’approccio di traguardare un certo numero di simboli per prendere una decisione in merito alla
sostituzione della parte destra impone una trasformazione della grammatica mediante regole
euristiche.
1. Fattorizzazione sinistra per far sì che non si abbia un prefisso comune in due o più parti destre
! di regole associate allo stesso simbolo non terminale.
A ! y v| y w
A ! y A’
A’ ! v | w
2. Eliminazione della ricorsione sinistra per evitare la generazione di cicli infiniti di !
generazione all’applicazione della derivazione canonica a sinistra
! E " E + T " E + T +T " E + T + T + T "..................
Eliminazione della ricorsione sinistra
E ! E + T | E - T | -T | T
T! T * F | F!
F! (E) | i
Una regola del tipo Y ! Yx implica necessariamente l’esistenza di una regola
Y !v dove Y non è un prefisso di v. Ciò per far sì che il linguaggio generato sia non vuoto.
si ha allora:
Y ! Yx!
Y!v
Y ! v Y’
Y’ ! x Y’
Y’ ! #
E’ facile vedere come i due schemi generino lo stesso linguaggio
Y " Yx "Y x x" Y x x x " v x x x
Y " vY’ "v x Y’ " v x x Y’ " v x x x
Esempio
Nella grammatica vista in precedenza esistono tre regole ricorsive a sinistra. Si ha perciò
E!E+T!
E !T
!!
!
!
!
T!T*F!
!!
T !F
!!
!
!!
!
!
E
!
E
-T
!
!
E
!! ! -T !
!!
!
!!
!
pertanto:
!
!!
E
!! ! E + T | E - T | -T | T !
T! T * F | F!
!
F! (E) | i !
!E ! T E’
!E’ ! + T E’!
! E’ ! #
!
!
T ! FT’
T’ ! * F T’
T’ ! #
E ! - T E’
E’ ! - T E’
E’ ! #
E ! T E’ | - T E’
E’ ! + T E’ | - T E’ | #
T ! F T’
T’ ! *F T’ | #
F ! (E) | i
Si osservi come la ricorsione sinistra sia stata cambiata in ricorsione destra
Grammatiche LL(1)
Una grammatica si dice LL(1) se la scelta per quale simbolo non terminale da espandere dipende
unicamente dal prossimo simbolo della stringa da riconoscere.
Per ricavare informazioni collegate ai simboli terminali che saranno presenti nelle stringhe si
opera una sorta di preprocessing della grammatica calcolando una volta per tutte le informazioni
necessarie.
Lo scopo di questo preprocessing è quello di ricavare due insiemi di simboli terminali
denominati FIRST e FOLLOW, costruite nel seguente modo.
1. Costruzione dell’insieme dei FIRST - data una generica stringa X $ V* l’insieme FIRST (X)
! comprende tutti i simboli terminali che sono prefissi di una qualsiasi stringa derivabile da X.
!!
FIRST (X) = { t | t $ VT e X "* t v con v $ V*}
2. Costruzione dell’insieme dei FOLLOW - dato un simbolo non terminale Y l’insieme dei !
FOLLOW (Y) comprende tutti i simboli terminali che possono seguire immediatamente Y
!
FOLLOW(Y) = { t | t $ VT e S"+ u Y t v con u, v $ V* }
Definizione dell’insieme dei FIRST
1. Se t ! VT allora FIRST (t) = {t}
2. Se X! VN allora FIRST (X) = { t | t ! VT, X "+ t u, con u ! V* }
! per cui se siamo in presenza della regola
X # x | x ...... | x
1 2
n
! il FIRST di X è definito come FIRST (X) = $1n FIRST (x )
i
3. Se y = y y ,.....,y con y ! V
1 2
n
i
(a) Se y "+ % allora FIRST (y) = FIRST (y )
1
1
(b) Se yi "+ % con i = 1,2,...k < n e yk+1 "+ % allora FIRST (y) = $1k+1FIRST (yi)
(c) Se yi "+ % con i= 1,...,n allora FIRST (y) = $1n FIRST (yi)
Costruzione dell’insieme dei FIRST
!E ! T E’ | - T E’
E’ ! + T E’ | - T E’ | #
!T ! F T’
T’ ! *F T’ | #
F ! (E) | i
L’insieme dei FIRST (X) comprende tutti i simboli terminali che siano prefissi in una
stringa qualsiasi che sia derivabile da X.
si ha:
FIRST (E) = {-, (, i }!
FIRST (E’) = {+, -}
FIRST (T) = { (, i }
FIRST (T’) = { * }
FIRST (F) = { (, i }
per esempio
E " - T E’
E " T E’ " F T’ E’ " ( E ) T’ E’
E " T E’ " F T’ E’ " i T’ E’
Costruzione dell’insieme dei FIRST cont.
Si costruisce un grafo i cui nodi sono rappresentati da tutti
i simboli terminali e non terminali della grammatica
Sia X, Y, W $ VN
t $ VT
u, v $ V*
gli archi del grafo sono costruiti secondo le seguenti due regole
R1 - per ogni regola della grammatica avente la struttura X ! t u
%
si traccia l’arco X ! t
E ! T E’ | - T E’
E’ ! + T E’ | - T E’ | #
!T ! F T’
T’ ! *F T’ | #
F ! (E) | i
R2 - per ogni regola della grammatica avente la struttura X ! Y u
%
si traccia l’arco X ! Y
Ogni nodo relativo ad un simbolo terminale
R1
+
raggiungibile tramite un cammino a partire
R1
E
E’
da un simbolo non terminale farà parte dei
R1
FIRST di quel non terminale pertanto:
R2
-
T
F
(
R2 R1
T’
R1
)
i
R1
*
FIRST (E) = { -, (, i }!
FIRST (E’) = { +, - }
FIRST (T) = { (, i }
FIRST (T’) = { * }
FIRST (F) = { (, i }
1. Si osservi come il carattere ) non fa parte di alcun FIRST non essendo raggiunto da nessun non terminale
2. Si osservi che il grafo è aciclico in quanto non sono presenti regole ricorsive a sinistra