IFT313
Introduction aux langages formels
Froduald Kabanza
Département d’informatique Université de Sherbrooke
planiart.usherbrooke.ca/kabanza/cours/ift313
Ensembles first et follow
IFT313 © Froduald Kabanza
2
Sujets
• C’est quoi un symbole nullable pour une grammaire?
• C’est quoi un ensemble first pour une grammaire ?
• C’est quoi un ensemble follow pour une grammaire ?
• Quel l’algorithme pour calculer ces symboles et ces ensembles ?
• Quel est le rôle de ces concepts dans l’analyse syntaxique descendante (prédictive) ?
– C’est quoi une table d’analyse LL ?
– Comment la calculer.
IFT313 © Froduald Kabanza
8
Exemple
return true Pile
0.
3.
3.
3.
2.
3.
3.
2.
3.
3.
2.
3.
2.
3.
2.
3.
3.
1.
Étape Règle
Algorithm LLDriver
0. stack = ($S); a = in.read(); x=stack.top();
while (true) {
1. if (x = = $) && (a= = $) return true ;
2. if (x = = a) {
pop a from stack; a = in.read();
continue;}
3. if x is a nonterminal { find x y;
pop x from stack; push y on stack;
continue; } 4. exit with error;}
Entrée 1. E TE’
2. E’ + TE’ | 3. ε 4. T FT’
5. T’ *FT’ | 6. ε 7. F ( E ) | n
Entrée :
n+n*n
E Þ TE’
FT’E’
nT’E’
nT’E’
nE’
n +TE’
n+TE’
n+FT’E’
n+nT’E’
n+nT’E’
n+n*FT’E’
n+n*FT’E’
n+n*nT’E’
n+n*nT’E’
n+n*nE’
n+n*n
S Þ déjà lu + pile
* n+n*n$ *
n+n*n$
n+n*n$
n+n*n$
+n*n$
+n*n$
+n*n$
n*n$
n*n$
n*n$
*n$
*n$
n$
n$
$ $ $ E TE’
T FT’
F n T’ ε E’ +TE’
T FT’
F n T’ *FT’
F n T’ ε E’ ε
E$
TE’$
FT’E’$
nT’E’$
T’E’$
E’$
+TE’$
TE’$
FT’É’$
nT’E’$
T’E’$
*FT’E’$
FT’E’$
nT’E’$
T’E’$
E’$
$
IFT313 © Froduald Kabanza
13
Définition de l’ensemble First
- Étant donné a une chaîne de terminaux et de non-terminaux, alors
First( a) est l’ensemble de terminaux commençant une chaîne dérivée de a.
- Exemple :
G = (V, A, R, E) : V = {E, E’, T, T’}
A = {(, ), +, *, n}
R = { E TE’
E’ + TE’ | ε T FT’ | ε T’ *FT’ | ε
F ( E ) | n}
First(TE’) = {(, n, +}
First(+TE’) = { + } First(FT’) = { (, n}
First(*FT’) = { * } First((E)) = { ( } First(n) = { n}
IFT313 © Froduald Kabanza
14
Utiliser l’ensemble First dans le driver LL
- Si le sommet de la pile contient un terminal X, le driver LL doit prédire la règle X a tel que le prochain token est dans First( a) .
- Exemple :
G = (V, A, R, E) : V = {E, E’, T, T’}
A = {(, ), +, *, n}
R = { E TE’
E’ + TE’ | ε T FT’ | ε T’ *FT’ | ε
F ( E ) | n}
First((E)) = {( } First(n) = {n}
Si le sommet de la pile est F : - si le prochain token est ( , le driver LL prédit F ( E )
- sinon, si le prochain token est n le driver LL prédit F n
- sinon le driver sort avec une erreur.
IFT313 © Froduald Kabanza
15
Utiliser l’ensemble First dans le driver LL
- Si la grammaire ne contient pas deux productions X a et X b telles que First( a) et First(b) ont une intersection, alors le driver LL exécute de manière déterministe :
Il y a zéro ou une production à prédire à chaque étape.
- Par contre si la grammaire contient deux productions X a and X b telles que First( a) et First(b) ont une intersection, le driver LL ne peut pas prédire la production à appliquer en lisant juste un token : le driver LL exécuterait de manière non-déterministe.
- On va justement éviter des grammaires qui donneraient lieu à un tel non-
déterminisme.
IFT313 © Froduald Kabanza
16
Exemple
First(n) = {n}
First((E)) = {(}
First(E+E) = {n,(}
First(E*E) = {n,(}
Si le sommet de la pile est E :
- Si le prochain token est n, le driver ne peut pas prédire laquelle des productions 1, 3 ou 4 est appropriée.
- Si le prochain token est (, le driver ne peut pas prédire non plus laquelle des productions 2, 3 ou 4 est appropriée.
G = (V, A, R, E) : V = {E}
A = {(, ), +, *, n}
R = { 1. E n 2. E (E) 3. E E+E 4. E E*E}
IFT313 © Froduald Kabanza
17
Motivation pour l’ensemble Follow
- Nous venons de voir que la bonne production à prédire est une règle X a telle le prochain token est dans First( a ) .
- Toute fois, cette ligne directrice doit être généralisée pour tenir compte des productions commençant par la chaîne vide.
- Pour ce faire, nous allons introduire les notions de symbolles nullables et
d’ensemble Follow.
IFT313 © Froduald Kabanza
18
Motivation pour les symboles nullables
- À première vue le calcul de l’ensemble First parait très simple.
Si a= XY, on a l’impression que Y peut être ignoré et que First(a)= First(XY)
= First(X).
- En fait, cela est vrai uniquement si X ne peut pas dériver la chaîne vide.
- Autrement, First(a) doit aussi inclure First(Y).
- Exemple :
G = (V, A, R, S) : V = {X, Y, S}
A = {a, b, c}
R = { 1. S a |
2. S X Y S
3. X b | 4. X Y 5. Y ε | 6. Y c}
First(S) = { a, b, c } First(X) = { b, c } First(Y) = { c } First(a) = { a } First(b) = { b } First(c) = { c}
Puisque X peut dériver Y (production 4), une chaîne dérivée de XYS peut tout aussi bien être dérivée de YYS. Vu que Y peut dériver la chaîne vide (production 5), la chaîne peut tout aussi bien être dérivée de YS, ou de S.
IFT313 © Froduald Kabanza
19
Définition de symboles nullables
- On vient de voir qu’en calculant First(S) ou First(XYS) on doit tenir compte des non-terminaux qui pourraient dériver la chaîne vide et de ceux qui pourraient les suivre dans une dérivation.
- Les non-terminaux qui peuvent dériver la chaîne vides sont appelés des symboles nullables.
Ainsi, X et Y sont nullables dans la grammaire ci après.
- Exemple :
G = (V, A, R, S) : V = {X, Y, S}
A = {a, b, c}
R = { 1. S a |
2. S X Y S
3. X b | 4. X Y 5. Y ε | 6. Y c}
First(S) = { a, b, c } First(X) = { b, c } First(Y) = { c } First(a) = { a } First(b) = { b } First(c) = { b }
Puisque X peut dériver Y (production 4), une chaîne dérivée de XYS peut tout aussi bien être dérivée de YYS. Vu que Y peut dériver la chaîne vide (production 5), la chaîne peut tout aussi bien être dérivée de YS, ou de S.
IFT313 © Froduald Kabanza
20
Définitions formelles
- nullable(X) est vrai si et seulement si X peut dériver la chaîne vide en zéro ou plusieurs étapes.
- First(a) est l’ensemble de terminaux qui pourraient commencer une chaîne dérivée de a.
- Follow(X) est l’ensemble de terminaux qui pourraient suivre X immédiatement, dans une forme sententielle.
- En d’autres mots, l’ensemble de terminaux a tels que S => g Xab pour g et b quelconques.
- En plus, si X peut être le dernier symbole dans une forme sententielle, on ajoute $ à Follow(X).
- Ceci arriverait aussi si une dérivation contient XY tel que Y peut dériver la chaîne vide.
- Récursivement, ceci arriverait aussi si une dérivation contient XYZ tel que Y et Z peuvent chacun dériver la chaîne vide. Ainsi de suite …
*
IFT313 © Froduald Kabanza
21
Définitions formelles
- Une définition plus formelle de nullable, First and Follow est que ce sont les plus petits ensembles pour lesquels les propriétés suivantes sont valides :
If S is the start symbol, then Follow[S] contains $;
For each terminal symbol a, First(a) = { a };
For each production X Y1 … Yk
If Y1 … Yk are all nullable or (if k = 0) nullable[X] = true;
for each i from 1 to k,
if Y1… Yi-1 are all nullable (or if i=1) First[X] = Union(First[X], First[Yi]);
if Yi+1… Yk are all nullable (or if i=k) Follow[Yi] = Union(Follow[Yi], Follow[X]);
for each j from i + 1 to k
if Yi+1… Yj-1 are all nullable (or if i+1=j) Follow[Yi] = Union(Follow[Yi], First[Yj]);
- Pour obtenir les ensembles nullable, First et Follow, on calcule le point fixe (ou la fermeture) de ces équations (c-à-d., on refait des itérations tant qu’on n’obtient pas une solution stable).
IFT313 © Froduald Kabanza
22
Algorithme pour calculer nullable, First et Follow
Algorithme nullableFirstFollow Entrée : une grammaire.
Sortie : Trois vecteur (nullable, First et Follow) tel que pour chaque symbole de grammaire X (terminal ou non):
nullable(X) est true si X est nullable
First(X) est l’ensemble de terminaux qui peuvent commencer une chaîne dérivable de X
Follow(X) est l’ensemble de terminaux qui peuvent suivre X dans une dérivation.
IFT313 © Froduald Kabanza
23
Algorithme pour calculer nullable, First et Follow
Algorithme nullableFirstFollow
initialize all entries of First and Follow to the empty set and those of nullable to false;
set Follow[S] = {$}, where S is the start symbol and $ the end marker;
for each terminal symbol a, First(a) = {a};
do {
for each production X Y1…Yk {
if Y1…Yk are all nullable or (if k = 0) nullable[X] = true;
for (i = 1; i <= k; i++) {
if Y1…Yi-1 are all nullable (or if i=1) First[X] = Union(First[X], First[Yi]);
if Yi+1…Yk are all nullable (or if i=k) Follow[Yi] = Union(Follow[Yi], Follow[X]);
for (j = i+1; j <= k; j++)
if Yi+1…Yj-1 are all nullable (or if i+1=j) Follow[Yi] = Union(Follow[Yi], First[Yj]);
} }
} while First, Follow or nullable is modified in the current iteration
IFT313 © Froduald Kabanza
24
Exemple 1
- G = (V, A, R, S) : V = {X, Y, S}
A = {a, b, c}
R = { 1. S a 2. S X Y S 3. X b
4. X Y 5. Y ε 6. Y c}
Iter 0.
S X Y
nullable First
Follow
false false false
{} {} {}
{$} {} {}
Iter 1.
nullable First Follow
false true false
{a} {b} {c}
{$} {a, c} {a}
Iter 2.
nullable First Follow
true true false
{a, b, c} {b, c} {c}
{$} {a, b, c} {a,b, c}
IFT313 © Froduald Kabanza
25
Exemple 2
E E’ T
nullable First Follow
true false false
{(,n} {+} {*}
{), $} {+, ), $}
G = (V, A, R, E) :
V = {E, E’, T, T’, F, (, )}
A = {(, ), +, *, n}
R = {
E TE’
E’ + TE’ | ε
T FT’
T’ *FT’ | ε
F ( E ) | n }
T’ F
true false
{(,n} {(,n}
{), $} {+, ), $}{+,*,), $}
IFT313 © Froduald Kabanza
26
Généralisation aux chaînes de symboles
- Il est utile de généraliser nullable et First à de chaînes de symboles d’une grammaire :
• Étant donne une chaîne a, nullable( a ) si et seulement si chaque symbole de a est nullable.
• Étant donne un symbole X et une chaîne g : First(X g )=First[X] if not nullable[X]
First(X g )=Union(First[X], First( g)) if nullable[X]
IFT313 © Froduald Kabanza
27
Comment prédire une production ?
- Nous utilisons les ensembles First et Follow pour prédire une production en fonc- tion du non-terminal au dessus de la pile et du prochain token (terminal) à l’entrée.
- L’idée de base est que si le sommet de la pile est A, et le prochain token est a, alors on prédit la production A a telle que a est dans First(a).
Ainsi le driver LL va appliquer A a , en remplaçant A par a au sommet de la pile.
- La seule complication est lorsque a peut dériver la chaîne vide.
Dans ce cas, nous allons prédire A a si a est dans Follow(A) (ceci inclus le cas où le prochain token est $ (EOF) et $ est dans Follow(A)).
IFT313 © Froduald Kabanza
28
Exemple
G = (V, A, R, E) :
V = {E, E’, T, T’, F, (, )}
A = {(, ), +, *, n}
R = {
E TE’
E’ + TE’ | ε
T FT’
T’ *FT’ | ε
F ( E ) | n }
E E’ T
nullable First Follow
true false false
{(,n} {+} {*}
{), $} {+, ), $}
T’ F
true false
{(,n} {(,n}
{), $} {+, ), $}{+,*,), $}
IFT313 © Froduald Kabanza
29
Exemple
return true Pile
0.
3.
3.
3.
2.
3.
3.
2.
3.
3.
2.
3.
2.
3.
2.
3.
3.
1.
Étape Règle
Algorithm LLDriver
0. stack = ($S); a = in.read(); x=stack.top();
while (true) {
1. if (x = = $) && (a= = $) return true ;
2. if (x = = a) {
pop a from stack; a = in.read();
continue;}
3. if x is a nonterminal { find x y;
pop x from stack; push y on stack;
continue; } 4. exit with error;}
Entrée 1. E TE’
2. E’ + TE’ | 3. ε 4. T FT’
5. T’ *FT’ | 6. ε 7. F ( E ) | n
Entrée :
n+n*n
n+n*n$
n+n*n$
n+n*n$
n+n*n$
+n*n$
+n*n$
+n*n$
n*n$
n*n$
n*n$
*n$
*n$
n$
n$
$ $ $ E TE’
T FT’
F n T’ ε E’ +TE’
T FT’
F n T’ *FT’
F n T’ ε E’ ε
E E’ T
nullable First
Follow
true false false
{(,n} {+} {*}
{), $} {+, ), $}
T’ F
true false
{(,n} {(,n}
{), $} {+, ), $}{+,*), $}
E$
TE’$
FT’E’$
nT’E’$
T’E’$
E’$
+TE’$
TE’$
FT’É’$
nT’E’$
T’E’$
*FT’E’$
FT’E’$
nT’E’$
T’E’$
E’$
$
IFT313 © Froduald Kabanza
30
Résumé
- Une fois de plus, voici les règles pour prédire une production:
- L’idée de base est que si le sommet de la pile est A, et le prochain token est a, alors on prédit la production A a telle que a est dans First(a).
Ainsi le driver LL va appliquer A a , en remplaçant A par a au sommet de la pile.
- La seule complication est lorsque a peut dériver la chaîne vide.
Dans ce cas, nous allons prédire A a si a est dans Follow(A) (ceci inclut le cas où le prochain token est $ (EOF) et $ est dans Follow(A)).
- Avec ces règles, on peut générer une table d’analyse M, telle que M[A,a] contient la règle de production à appliquer lorsque A est au sommet de la pile et a est le prochain token.
- Nous verrons comment à la prochaine leçon.