26.04.2013 Views

Analisi sintattica

Analisi sintattica

Analisi sintattica

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

<strong>Analisi</strong> <strong>sintattica</strong><br />

<strong>Analisi</strong> <strong>sintattica</strong><br />

programma<br />

scanner<br />

tokens<br />

parser<br />

Errori sintattici<br />

albero<br />

sintattico<br />

Un parser deve riconoscere la struttura di una stringa di ingresso,<br />

la cui struttura è fornita in termini di regole di produzione di una<br />

CFG, BNF, o diagrammi sintattici. Un parser è una macchina<br />

“astratta” che raggruppa input in accordo con regole<br />

grammaticali.<br />

1


Sintassi<br />

La sintassi è costituita da un insieme di regole che<br />

definiscono le frasi formalmente corrette<br />

permettono di assegnare ad esse una struttura (albero<br />

sintattico) che ne indica la decomposizione nei costituenti<br />

immediati.<br />

Ad es. la struttura di una frase (ovvero di un programma) di<br />

un linguaggio di programmazione ha come costituenti le<br />

parti dichiarative e quelle esecutive. Le parti dichiarative<br />

definiscono i dati usati dal programma. Le parti esecutive si<br />

articolano nelle istruzioni, che possono essere di vari tipi:<br />

assegnamenti, istruzioni condizionali, frasi di lettura, ecc. I<br />

costituenti del livello più basso sono gli elementi lessicali<br />

già considerati, che dalla sintassi sono visti come atomi<br />

indecomponibili. Infatti la loro definizione spetta al livello<br />

lessicale.<br />

Grammatiche non contestuali:<br />

vantaggi<br />

La teoria formale dei linguaggi offre diversi modelli, ma<br />

nella quasi totalità dei casi il tipo di sintassi adottata è<br />

quello noto come sintassi libera o non-contestuale (contextfree)<br />

che corrisponde al tipo 2 della gerarchia di Chomsky.<br />

I metodi sintattici per il trattamento del linguaggio sono<br />

semplici<br />

efficienti<br />

la definizione del linguaggio attraverso le regole delle sintassi<br />

libere dal contesto è diretta ed intuitiva<br />

gli algoritmi deterministici di riconoscimento delle frasi sono<br />

veloci (hanno complessità lineare) e facili da realizzare<br />

partendo dalla sintassi.<br />

Tutti questi vantaggi hanno imposto le grammatiche libere<br />

dal contesto come l’unico metodo pratico per definire<br />

formalmente la struttura di un linguaggio.<br />

2


Grammatiche non contestuali:<br />

svantaggi<br />

Non tutte le regole di un linguaggio di programmazione possono essere<br />

espresse in termini nel modello noncontestuale. Ad esempio non puo<br />

espremere le seguenti regole:<br />

in un programma ogni identificatore di variabile deve comparire in<br />

una dichiarazione<br />

il tipo del parametro attuale di un sottoprogramma deve essere<br />

compatibile con quello del parametro formale corrispondente<br />

Un altro limite del modello sintattico libero tocca le esigenze di calcolo<br />

della traduzione. Se si guardano le traduzioni definibili con i soli metodi<br />

sintattici, esse sono<br />

decisamente povere<br />

inadeguate ai problemi reali (compilazione di un linguaggio<br />

programmativo nel linguaggio macchina)<br />

Da queste critiche sarebbe sbagliato concludere che i metodi sintattici<br />

sono inutili: al contrario essi sono indispensabili come supporto<br />

(concettuale e progettuale) su cui poggiare i più completi strumenti della<br />

semantica.<br />

Parser<br />

• Il parser lavora con stringhe di tokens<br />

Programa<br />

sorgente<br />

Scanne<br />

r<br />

token<br />

get next token<br />

Parse<br />

r<br />

Albero sintattico<br />

3


Grammatiche di tipo Context-Free<br />

Le strutture ricorsive presenti nei linguaggi di<br />

programmazione sono definite da una grammatica<br />

context-free.<br />

Una grammatica context-free (CFG) è definita come<br />

Un insieme finito di terminali V (sono i token prodotti<br />

dallo scanner)<br />

Un insieme di non terminali V.<br />

Un assioma o simbolo iniziale (uno dei non terminali.<br />

Un insieme di produzioni (dette anche “rewriting rules”<br />

con la seguente forma<br />

A → X1 ... Xm dove X1 to Xm possono essere terminali o non<br />

terminali.<br />

Se m =0 si ha A → ε che è una produzione valida<br />

Grammatiche di tipo Context-Free<br />

Le strutture ricorsive presenti nei linguaggi di<br />

programmazione sono definite da una grammatica<br />

context-free.<br />

Una grammatica context-free (CFG) è definita come<br />

Un insieme finito Esempio di terminali V (sono i token prodotti<br />

dallo scanner) T = { (, ), id , + , * , / , -}<br />

Un insieme di non terminali V = {E} V.<br />

E → E + E<br />

Un assioma o simbolo<br />

E →<br />

iniziale<br />

E – E<br />

(uno dei non terminali.<br />

Un insieme di produzioni E → E (dette * E anche “rewriting rules”<br />

con la seguente forma E → E / E<br />

E → -E<br />

A → X1 ... XmE → ( E )<br />

dove X1 to Xm possono E →essere id<br />

terminali o non<br />

terminali.<br />

Se m =0 si ha A → ε che è una produzione valida<br />

4


E ⇒ E+E<br />

Derivazioni<br />

E+E deriva da E<br />

Possiamo sostituire E con con E+E<br />

Perr poter fare questo deve esistere una produzione E→E+E nella<br />

grammatica.<br />

E ⇒ E+E ⇒ id+E ⇒ id+id<br />

Una sequenza di sostituzioni di non-terminali e chiamata una derivazione di<br />

id+id da E.<br />

In generale un passo di derivazione è<br />

αAβ ⇒αγβ se ∃ A→γ in G α ∈(V ∪ T)* e β ∈ (V ∪ T)*<br />

α 1 ⇒α 2 ⇒ ... ⇒ α n<br />

⇒ one step<br />

⇒* zero o più steps<br />

⇒+ 1 o più steps<br />

V = { Prog, Seq, Istr, Expr}<br />

T = {; , id , + }<br />

Assioma = Prog<br />

Prog → { Seq }<br />

Seq →Seq ; Istr<br />

Seq → ε<br />

Istr →Seq<br />

Istr →id = Expr<br />

Expr →id<br />

Expr →Expr + id<br />

(α n deriva da α 1 o α 1 è derivato da α n )<br />

V = { Prog, Seq, Istr, Expr}<br />

T = {; , id , + }<br />

Assioma = Prog<br />

Prog → { Seq }<br />

Seq →Seq ; Istr | ε<br />

Istr →Seq | id = Expr<br />

Expr →id | Expr + id<br />

5


Per esempio partendo da Prog possiamo generate,<br />

aplicando ripetutamente le produzione le seguenti<br />

stringhe:<br />

Prog<br />

{ Seq }<br />

{ Seq ;Istr}<br />

{Istr;Istr}<br />

{ id = Expr ;Istr}<br />

{ id = id ;Istr}<br />

{ id = id ; id = Expr }<br />

{ id = id ; id = Expr + id}<br />

{ id = id ; id = id + id}<br />

Le produzioni devono avere le seguenti<br />

proprietà:<br />

no produzioni inutili o ridondanti (i.e., A → A),<br />

no non-terminali senza produzioni<br />

(e.g., A → Ba dove B non è definito),<br />

no cicli infiniti (e.g., A → Aa senza altre<br />

produzioni per A),<br />

no ambiguita’: una grammatica con più alberi<br />

sintattici per la stessa espressione è ambigua<br />

descrivere correttamente il linguaggio.<br />

6


CFG - Terminologia<br />

L(G) è il linguaggio generato da G.<br />

Una frase di L(G) è una stringa di simboli terminali di G.<br />

Se S è l’assioma o start symbol di G allora<br />

+<br />

ω è una frase di L(G) iff S ⇒ ω dove ω ∈T*.<br />

• Se G è una grammatica context-free, L(G) è un<br />

linguaggio context-free.<br />

• Due grammatiche sono equivalenti se producono lo<br />

stesso linguaggio.<br />

• * S ⇒ α Se α contiene non-terminali, è chiamata forma di frase di G.<br />

Se α non contiene no-terminali è chiamata frase di G.<br />

Derivazioni Left-Most e Right-Most<br />

Left-Most Derivation<br />

E ⇒ -E ⇒ -(E) ⇒ -(E+E) ⇒ -(id+E) ⇒ -(id+id)<br />

Right-Most Derivation<br />

E ⇒ -E ⇒ -(E) ⇒ -(E+E) ⇒ -(E+id) ⇒ -(id+id)<br />

top-down parser cerca una left-most derivation del<br />

programma sorgente<br />

bottom-up parser cerca una right-most derivation del<br />

programma sorgente<br />

7


Parse Tree (Abstract Sintax Tree)<br />

I nodi non foglie dell’albero sintattico sono simboli non-terminal, le foglie simboli<br />

terminali.<br />

E ⇒ -E<br />

-<br />

-<br />

E<br />

E<br />

E<br />

E<br />

⇒ -(E) E<br />

⇒ -(E+E)<br />

- E<br />

( E )<br />

⇒ -(id+E) ⇒ -(id+id)<br />

( E )<br />

E +<br />

id<br />

E<br />

-<br />

E<br />

E<br />

( E )<br />

E +<br />

id<br />

E<br />

id<br />

-<br />

E<br />

( E )<br />

Un albero sintattico è una rappresentazione grafica di una derivazione.<br />

Per esempio partendo da Prog possiamo generate,<br />

aplicando ripetutamente le produzione le seguenti<br />

stringhe:<br />

Prog<br />

{ Seq }<br />

{ Seq ;Istr}<br />

Seq<br />

{Istr;Istr}<br />

{ id = Expr ;Istr}<br />

Seq Istr<br />

{ id = id ;Istr}<br />

{ id = id ; id = Expr }<br />

{ id = id ; id = Expr + id}<br />

{ id = id ; id = id + id}<br />

Istr<br />

E<br />

E<br />

+<br />

E<br />

8


Parser<br />

Esempio: Consideriamo l’analisi della frase<br />

1 + 2 * 3<br />

secondo la seguente grammatica<br />

::= + <br />

| * <br />

L’analisi <strong>sintattica</strong> può essere vista come un processo | numberper<br />

costruire gli alberi sintattici (parse tree).<br />

La sintassi di un programma è descritto da un agrammatica<br />

libera dal contesto (CFG). La notazione BNF (Backus-Naur<br />

Form) è una notazione per la descrzione di una CFG.<br />

Il parser verifica se un programma sorgente soddisfa le regole<br />

implicate da una grammatica context-free o no.<br />

Se le soddisfa, il parser crea l’albero sintattico (del programma<br />

Altrimenti genera un messaggio di errore.<br />

Una grammatica non contestuale<br />

Da una specifica rigorosa della sintassi dei linguaggi di<br />

programmazione<br />

Il progetto della grammatica e la fase iniziale del progetto del<br />

compilatore<br />

Esistono strumenti automatici per costruire automaticamente il<br />

compilatore dalla grammatica<br />

Ambiguita<br />

• Una grammatica è ambigua se esistono più alberi sintattici per la<br />

stessa frase.<br />

E ⇒ E+E ⇒ id+E ⇒ id+E*E<br />

⇒ id+id*E ⇒ id+id*id<br />

Esempio<br />

T = { (, ), id , + , * , / , -}<br />

E V ⇒= {E} E*E ⇒ E+E*E ⇒ id+E*E<br />

E<br />

⇒<br />

→<br />

id+id*E<br />

E + E<br />

⇒ id+id*id<br />

E → E – E<br />

E → E * E<br />

E → E / E<br />

E → -E<br />

E → ( E )<br />

E → id<br />

E +<br />

id<br />

E<br />

E<br />

* E<br />

E<br />

id<br />

id<br />

E<br />

E +<br />

id E<br />

E<br />

* E<br />

id id<br />

9


Esempio<br />

T = { Ambiguita<br />

(, ), id , + , * , / , -}<br />

V = {E}<br />

E → E + E<br />

E → E – E<br />

•E Una → E grammatica * E è ambigua se esistono più alberi sintattici per la<br />

stessa E → E frase. / E<br />

E<br />

E → -E<br />

E + E<br />

E E → ⇒( E E+E ) ⇒ id+E ⇒ id+E*E<br />

E → ⇒id id+id*E ⇒ id+id*id<br />

id E * E<br />

E ⇒ E*E ⇒ E+E*E ⇒ id+E*E<br />

⇒ id+id*E ⇒ id+id*id<br />

E +<br />

id<br />

E<br />

E<br />

* E<br />

E<br />

id<br />

id<br />

id id<br />

Per poter costruire un parser la grammatica<br />

non deve essere ambigua<br />

Grammatica non ambigua ∃ un unico<br />

albero sintattico per ogni frase del linguaggio<br />

Le ambiguità nella grammatica devono<br />

essere eliminate durante il progetto del<br />

compilatore<br />

10


Ambiguità<br />

istr → if expr then istr |<br />

if expr then istr else istr | otheristrs<br />

istr<br />

if E 1 then if E 2 then S 1 else S 2<br />

if expr then istr else istr<br />

E 1 if expr then istr S 2<br />

1<br />

E 2<br />

Ambiguità<br />

S 1<br />

istr<br />

if expr then istr<br />

E 1 if expr then istr else istr<br />

E 2 S 1 S<br />

• Noi preferiamo il secondo albero sintattico (else corrisponde al<br />

if più vicino).<br />

• Dobbiamo eliminare l’ambiguità con tale obiettivo<br />

• La grammatica non-ambigua sarà:<br />

istr → matchedistr | unmatchedistr<br />

matchedistr → if expr then matchedistr else matchedistr<br />

| otheristrs<br />

unmatchedistr → if expr then istr |<br />

if expr then matchedistr else unmatchedistr<br />

2<br />

11


Ambiguita – Precedenza degli<br />

operatori<br />

Grammatiche ambigue possono essere rese non-ambigue<br />

in accordo alle precedenze degli operatori e alle regole di<br />

associativià degli operatori.<br />

E → E+E | E*E | E^E | id | (E)<br />

E → E+T | T<br />

T → T*F | F<br />

F → G^F | G<br />

G → id | (E)<br />

precedenze: ^ (right to left)<br />

* (left to right)<br />

+ (left to right)<br />

Ricorsione sinistra (Left Recursion)<br />

Una grammatica è “left recursive” se ha un non<br />

terminale A tale che<br />

+<br />

A ⇒ Aα per qualche stringa α<br />

Le tecniche di parser Top-down non possono gestire<br />

grammatiche left-recursive.<br />

Una grammatica left-recursive deve esssere<br />

convertita in una non left-recursive.<br />

La ricorsione sinistra può comparire in un singolo<br />

passo della derivazione (immediate left-recursion), o<br />

può comparire in più che un passo.<br />

12


Immediate Left-Recursion<br />

A → A α | β dove β ≠ Aγ e γ∈(V ∪ T)*<br />

⇓<br />

A →βA ’<br />

A ’ →αA ’ | ε grammatica equivalente<br />

In generale<br />

A → A α1 | ... | A αm | β1 | ... | βn dove βi ≠ Aγ e γ∈(V ∪ T)*<br />

⇓<br />

A →β 1 A ’ | ... | β n A ’<br />

A ’ →α 1 A ’ | ... | α m A ’ | ε grammatica equivalente<br />

Esempio<br />

E → E+T<br />

E → T<br />

T → T*F<br />

T → F<br />

eliminate immediate left recursion<br />

F → id<br />

F →(E)<br />

E → T E ’<br />

E ’ → +T E ’<br />

E’ → ε<br />

T → F T ’<br />

T ’ → *F T ’<br />

T’ → ε<br />

F → id<br />

F → (E)<br />

13


Left-Recursion – Problema!!<br />

• Una grammatica che non e direttamente left-recursive, ma lo è in<br />

modo indirettp.<br />

• Anche in questo caso va eliminata la ricorsione sinistra<br />

Esempio<br />

S → Aa | b<br />

A → Sc | d<br />

S ⇒ Aa ⇒ Sca<br />

A ⇒ Sc ⇒ Aac<br />

Algoritmo per l’eliminazione della<br />

ricorsione sinistra<br />

- Ordinare I non-terminali A1 ... An -fori from 1 to n do {<br />

- for j from 1 to i-1 do {<br />

sostituire ogni produzione<br />

Ai → Aj γ<br />

con<br />

Ai →α1 γ | ... | αk γ<br />

dove Aj →α1 | ... | αk }<br />

- eliminare la ricorsione sinistra nelle produzioni di Ai }<br />

14


Esempio<br />

S → Aa | b<br />

A → Ac | Sd | f<br />

- Ordinare I non-terminali: S, A<br />

per S:<br />

- non c’e una ricorsione sinistra diretta.<br />

for A:<br />

- sostituiamo A → Sd con A → Aad | bd<br />

Cosi avremoA → Ac | Aad | bd | f<br />

- Eliminiamo la ricorsione sinistra in A<br />

A → bdA ’ | fA ’<br />

A ’ → cA ’ | adA ’ | ε<br />

Avremo la grammatica non ricorsiva equivalente:<br />

S → Aa | b<br />

A → bdA ’ | fA ’<br />

A ’ → cA ’ | adA ’ | ε<br />

Esempio<br />

S → Aa | b<br />

A → Ac | Sd | f<br />

- Ordinare I non-terminali : A, S<br />

per A:<br />

Eliminamo la ricorsione sinistra in A<br />

A → SdA ’ | fA ’<br />

A ’ → cA ’ | ε<br />

per S:<br />

- Sostituiamo S → Aa with S → SdA ’ a | fA ’ a<br />

Così avremo S → SdA ’ a | fA ’ a | b<br />

- Eliminamo la ricorsione sinistra in S<br />

S → fA’aS’ | bS ’<br />

S ’ → dA ’ aS’ | ε<br />

Avremo la grammatica non ricorsiva equivalente:<br />

S → fA’aS’ | bS ’<br />

S ’ → dA ’ aS’ | ε<br />

A → SdA ’ | fA ’<br />

A ’ → cA ’ | ε<br />

15


Left-Factoring (fattorizzazione<br />

sinistra)<br />

Un parser top down deterministico richiede<br />

una grammatica left-factored.<br />

grammatica grammatica equivalente<br />

istr → if (expr ) istr else istr |<br />

if (expr) istr<br />

Left-Factoring (cont.)<br />

In generale,<br />

A → αβ1 | αβ2 dove α∈(V∪T)* - {ε}, β1∈ (V∪T)* - {ε}, β2∈ (V∪T)* -<br />

{ε}<br />

e β1≠. Noi possiamo riscrivere la grammatica come segue<br />

A → αA ’<br />

A’ →β 1 | β 2<br />

16


Algoritmo<br />

Per ogni non-terminale A con due o più<br />

alternative (produzioni) con una parte non<br />

vuota comune<br />

A → αβ1 | ... | αβn | γ1 | ... | γm diventa<br />

A → αA ’ | γ 1 | ... | γ m<br />

A ’ →β 1 | ... | β n<br />

Esempio<br />

A → abB | aB | cdg | cdeB | cdfB<br />

⇓<br />

A → aA ’ | cdg | cdeB | cdfB<br />

A ’ → bB | B<br />

⇓<br />

A → aA ’ | cdA ’’<br />

A ’ → bB | B<br />

A ’’ → g | eB | fB<br />

17


Esempio<br />

A → ad | a | ab | abc | b<br />

⇓<br />

A → aA’ | b<br />

A’ → d | ε | b | bc<br />

⇓<br />

A → aA’ | b<br />

A’ → d | ε | bA’’<br />

A’’ →ε | c<br />

istr → if (expr ) istr else istr |<br />

if (expr) istr<br />

istr → if (expr ) istr X<br />

X → else istr<br />

X →ε 18


Le frasi possono essere analizzate da<br />

sinistra a destra (L parser), possono<br />

essere costruite con derivazioni left-most<br />

(LL(k) parser) o right-most (LR(k) parser)<br />

utilizzando k symboli di look-ahead!<br />

LL è più conosciuta come top-down parser.<br />

LR è più conosciuta come bottom-up<br />

parser.<br />

Per ragioni pratiche k deve essere piccolo.<br />

Per un compilatore è auspicabile l’uso di<br />

grammatiche che possono essere analizzate in<br />

modo deterministico con al più k symboli di lookahead.<br />

L’assenza di ambiguità è condizione<br />

necessaria per un analisi determinstica<br />

Consideriamo un bottom up parse per abbcde<br />

generato dalla seguente grammatica con assioma<br />

S, eseguento un approccio “left-most matches<br />

First”.<br />

S → aAcBe<br />

A → Ab|b<br />

B → d<br />

19


abbcde applicando B → d<br />

aAbcBe applicando A → b<br />

aAcBe applicando A → Ab<br />

S applicando S → aAcBe<br />

Parser<br />

Due differenti metodi di analisi <strong>sintattica</strong><br />

A 1<br />

A 2<br />

S 4<br />

B 3<br />

a b b c d e<br />

top-down: l’albero sintattico è creato dalla radice alle foglie<br />

bottom-up: l’albero sintattico è creato dalle foglie alla radice<br />

Il parser può lavorare in una varietà di modi ma esso<br />

tipicamente processa l’ input da sinistra a destra.<br />

Parser sia top-down che bottom-up possono essere<br />

implmentati in modo efficente solo per alcune sottoclassi di<br />

gramatiche context-free:<br />

LL per top-down<br />

LR per bottom-up<br />

Una “left-most (right-most) derivation” è una derivazione nella<br />

quale durante ogni passo viene sostituito solo il non-terminale<br />

più a sinistra (destra).<br />

20


Definzione di First(α)<br />

Data una grammatica non contestuale G(V,T,P,S) si definisce<br />

First(α) = {a in T | α⇒* aβ}<br />

Quindi First(α) è il seguente sottoinsieme di T:<br />

•se α = aβ con a ∈ T allora a ∈ First(α)<br />

•se α = Aβ con A ∈ T allora<br />

• se ∃ una produzione A → γallora First(γ) ⊆ First(α)<br />

• se ∃ una produzione A →εallora First(β) ⊆ First(α)<br />

First(α) è l’insieme di tutti i terminali con cui può iniziare una<br />

stringa derivata da α.<br />

Se α →*ε allora ε∈First(α).<br />

First(α) = {a ∈ T | α →* a β} ∪ if a → *ε then {ε} else ∅<br />

Esempio<br />

T = {a, b, c, d, e},<br />

V = {S, B, C}<br />

P = { S → aSe | B,<br />

B → bBe | C,<br />

C → cCe | d }<br />

S = S<br />

First(aSe) = {a}<br />

First(B) = First(bBe) ∪ First(C) = {b} ∪ First(cCe) ∪ First(d) =<br />

{b} ∪ {c} ∪ {d} = {b, c, d}<br />

First(bBe) = {b}<br />

First(C) = First(cCe) ∪ First(d) = {c} ∪ {d} = {c, d}<br />

First(cCe) = {c}<br />

21


Esempio<br />

T = {a, b, c, d, e}<br />

V = {A, B, C, D}<br />

P = { A → B | Ce | a<br />

B → bC,<br />

C → Dc<br />

D → d | ε}<br />

S = A<br />

First(B) = First(b) ={b}<br />

First(C e) = First (Dc) = First(d) ∪ First(ε) = {d} ∪{ε} = {d,<br />

ε }<br />

First(a) = {a}<br />

First(bC )= {b}<br />

Esempio<br />

T = {(, +, ), v, f}<br />

V = {E, Prefix, Tail}<br />

P = { E → Prefix(E) | v Tail<br />

Prefix → f | ε<br />

Tail → + E | ε }<br />

S = E<br />

First(Prefix(E) ) = {f} ∪{ε}<br />

First(v Tail) = {v}<br />

First(f) = {f}<br />

First(ε )= {ε}<br />

22


Esempio<br />

T = {a, (, ), +, ","}<br />

V = {E, T, L, P}<br />

P = { E → E + T | T<br />

T → a | (E) |<br />

a(L)<br />

E}<br />

S = E<br />

L → P | ε ,<br />

P → E | P ","<br />

First(E) = {a, (}<br />

First(T) = {a, (}<br />

First(L) = {ε , a, (}<br />

T = {a, b, c, d, e}<br />

V = {A, B, C, D}<br />

P = { A → B | C | Ce | a<br />

B → bC<br />

Definizione di Follow(A)<br />

C → Dc | D<br />

D → d | ε }<br />

S = A<br />

First(A) = {a, b, d, c, e, ε }<br />

First(B) = {b}<br />

First(C) = {d, c, ε }<br />

Data una grammatica non contestuale G(V,T,P,S) si definisce<br />

Follow(A) l’insieme dei terminali che seguono in una qualsiasi frase<br />

Esso è definito come segue:<br />

Follow(A) = {a ∈ T | S → +… Aa ….} ∪<br />

(if S ⇒* … A then {ε} else ∅}<br />

23


T = {a, b, c, d, e},<br />

V = {S, B, C}<br />

P = { S → aSe | B,<br />

B → bBe | C,<br />

C → cCe | d }<br />

S = S<br />

Follow(S) = {e, $}<br />

Follow(B) = {e, $}<br />

Follow(C) = {e, $}<br />

T = {(, +, ), v, f}<br />

V = {E, Prefix, Tail}<br />

P = { E → Prefix(E) | v<br />

Tail<br />

Prefix → f | ε<br />

Tail → + E | ε }<br />

S = E<br />

Follow(Prefix ) = { ( }<br />

Follow(E) = {$}<br />

Follow(Tail) = { $ }<br />

T = {a, (, ), +, ","}<br />

V = {E, T, L, P}<br />

P = { E → E + T | T<br />

T → a | (E) | a(L)<br />

L → P | ε ,<br />

P → E | P "," E}<br />

S = E<br />

Follow(E) = {+, ) , “,“ , $}<br />

Follow(T) = {+, ) , “,“ , $}<br />

Follow(L) = { ) }<br />

Follow(P) = {) , “,”}<br />

T = {a, b, c, d, e}<br />

V = {A, B, C, D}<br />

P = { A → B | Ce | a<br />

B → bC,<br />

C → Dc<br />

D → d | ε}<br />

S = A<br />

Follow(A) = {$}<br />

Follow(B) = {$}<br />

Follow(C) = {$, e}<br />

Follow(D) = {c}<br />

24


T = {a, b, c, d, e}<br />

V = {A, B, C, D}<br />

P = { A → B | C | Ce | a<br />

B → bC<br />

C → Dc | D<br />

D → d | ε }<br />

S = A<br />

Follow(A) = {$}<br />

Follow(B) = Follow(A) = {$}<br />

Follow(C) = Follow(A) ∪ Follow(B) ∪ {e} = {e , $}<br />

Follow(D) = {c } ∪ Follow(C) = {c, e , $}<br />

25

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!