IFT313
Introduction aux langages formels
Froduald Kabanza
Département d’informatique Université de Sherbrooke
planiart.usherbrooke.ca/kabanza/cours/ift313
Automates à pile LL
IFT313 © Froduald Kabanza
2
Sujet
• Automate à pile LL.
IFT313 © Froduald Kabanza
3
Objectif
• Pouvoir décrire et simuler un automate à
pile LL pour une grammaire donnée.
IFT313 © Froduald Kabanza
4
Références
[2] Appel, A. and Palsberg. J. Modern Compiler Implementation in Java.
Second Edition. Cambridge, 2004.
– Section 3.2
[4] Aho, A., Lam, M., Sethi R., Ullman J. Compilers: Principles, Techniques, and Tools, 2nd Edition. Addison Wesley, 2007.
– Sections 4.4.4
IFT313 © Froduald Kabanza
5
Rappel : Automate à pile
- Un automate à pile a :
- Une entrée et une tête de lecture.
- Un nombre fini d’états dont un état initial et des états accepteurs.
- Une relation de transition.
- Une pile pouvant croître arbitrairement.
Automate à pile entrée
pile
$
$
IFT313 © Froduald Kabanza
6
Rappel : Transitions
- Une transition de la forme (p, u, b) (q, g) signifie que l’automate peut passer de l’état p à l’état q, pourvu que la chaîne d’entrée commence par le préfixe u et la chaîne b est au sommet de la pile.
Après la transition, u est lu et consommé du mot d’entrée, b est enlevé de la pile et remplacé par g, et le nouvel état devient q.
La chaîne b est lue de gauche à droite : son premier caractère doit donc être au sommet de la pile.
La chaîne g est écrite de telle façon que son premier caractère soit au sommet de la pile.
entrée
pile
p q
$
$
entrée
pile
p q
$
$
u u
b g
IFT313 © Froduald Kabanza
7
Configurations, trace, acceptation
- La configuration d’un automate est un triplet (s, V
*, A
*), indi- quant, respectivement, son état courant, le contenu de la pile, et la partie de l’entrée qui reste à lire.
- Une trace (ou exécution) d’un automate sur une entrée est la séquence de configurations qu’il produit en lisant son entrée et en suivant ses transitions.
- Une exécution accepte une chaîne d’entrée si la dernière con- figuration est de la forme (p, $, $), avec p un état accepteur.
En d’autre mot, dans la dernière configuration, la pile est
vide, on est à la fin de l’entrée et l’état est accepteur.
IFT313 © Froduald Kabanza
8
Rappel : Exemple
- M = (S,V,T,R,s,S,$), tel que:
• S = {s,p}
• V = {A}
• T = {a,b}
• R = { (s, a, ε) (s, A), (s, b, A) (p, ε), (p, b, A) (p, ε)}
- L(M) = {an bn | n >= 0},
c-à-d., chaîne commençant par des a suivi d’autant de b.
Cette automate non-déterministe correspond à la grammaire
G = (V,A,R,S), tel que : V = {S}
A = {a,b}
R = {S ε, S aSb}
s p
a, ε/A b, A/ε
b, A/ε
IFT313 © Froduald Kabanza
9
Parseurs LL (Top-down)
- Les parseurs LL (appelés aussi top-down parsers)
valident la syntaxe d’une chaîne de tokens (le code d’un programme) en cherchant la dérivation la plus à gauche de la chaîne.
- La recherche de la dérivation se fait en partant du symbole de départ (d’où le « top »), en allant vers la chaîne à dériver, c-à-d., vers le bas (d’où le « - down »).
- Nous allons voir une procédure permettant de construire systématiquement un au- tomate à pile LL à partir de n’importe quelle grammaire hors-contexte.
- Cette procédure va ensuite servir de source d’inspiration pour construire des parseurs LL efficaces.
IFT313 © Froduald Kabanza
10
Procédure pour construire un automate à pile LL à partir d’une grammaire hors-contexte
- Pour chaque grammaire G = (V, T, P, S), on génère un automate à pile non- déterministe M = ({p, q}, V U T, T, R, p, {q},$), tel que R contient les tran- sitions suivantes :
(p, ε, ε) (q, S)
(q, ε, A) (q, a ) pour chaque A a dans P (q, a, a) (q, ε) pour chaque terminal a dans T
- Une preuve de la validité de cette construction est donnée dans [Wolper, Pierre. Introduction à la calculabilité, 3è édition. Dunod, 2006]
p ε, ε/S q
q ε, A/ a
q
a, a/ε
IFT313 © Froduald Kabanza
11
Intuition derrière la construction
- Intuitivement, les transitions implémentent trois types de mouvements de symboles sur la pile, de manière à simuler la dérivation la plus à gauche d’une chaîne d’en- trée par la grammaire.
• Le placement initiale qui place le symbole de départ de la grammaire sur la pile.
(p, ε, ε) (q, S) pour le symbole de départ S
• La prédiction de la règle de production à appliquer pour une étape de dériva- tion, valide lorsque le sommet de la pile est un non-terminal.
(q, ε, A) (q, a) pour chaque A a dans P
• Le matching du prochain caractère de l’entrée, valide lorsque le sommet de la pile est un terminal.
(q, a, a) (q, ε) pour chaque terminal a
p ε, ε/S q
q ε , A/ a
q
a, a/ε
IFT313 © Froduald Kabanza
12
Exemple 1
G = (V,A,P,S) : V = {S}
A = {a,b}
P = {S ε, S aSb}
M = ({p,q}, {S, a, b}, {a, b}, R, p,{q},$) : R = {
1. (p, ε, ε)
(q, S) 2.(q, ε, S) (q, ε)
3.
(q, ε, S) (q, aSb)
4. (q, a, a )
(q, ε) 5. (q, b, b )
(q, ε) }Entrée : aabb
(p, $, aabb$) (q, S$, aabb$) (q, aSb$, aabb$) (q, Sb$, abb$) (q, aSbb$, abb$) (q, Sbb$, bb$) (q, bb$, bb$) (q, b$, b$) (q, $, $)
Accepte Configuration
Simulation
1.
3.
4.
3.
4.
2.
5.
5.
Transition
IFT313 © Froduald Kabanza
13
Exemple 1 (suite)
G = (V,A,P,S) : V = {S}
A = {a,b}
P = {S ε, S aSb}
M = ({p,q}, {S, a, b}, {a, b}, R, p,{q},$) : R = {
1. (p, ε, ε)
(q, S) 2.(q, ε, S) (q, ε)
3.
(q, ε, S) (q, aSb)
4. (q, a, a )
(q, ε) 5. (q, b, b )
(q, ε) }(p, $, aabb$) (q, S$, aabb$) (q, aSb$, aabb$) (q, Sb$, abb$) (q, aSbb$, abb$) (q, Sbb$, bb$) (q, bb$, bb$) (q, b$, b$) (q, $, $)
Accepte Configuration
1.
3.
4.
3.
4.
2.
5.
5.
Transition
En affichant les règles de production corre- spondants aux transitions de l’automate, on obtient la dérivation la plus à gauche du mot d’entrée.
Règle Dérivation
S aSb
S Þ aSb Þ
S aSb
aaSbb Þ
S
ε aabb
IFT313 © Froduald Kabanza
14
Exemple 2
G = (V, A, R, Exp) : V = {Exp}
A = {(, ), +, *, num}
R = { Exp num Exp ( Exp)
Exp Exp + Exp Exp Exp * Exp }
M = ({p,q}, V U A, A, R, p, {q},$) : R = { (p, ε, ε) (q, Exp) (q, ε, Exp) (q, num) (q, ε, Exp) (q, (Exp) )
(q, ε, Exp) (q, Exp + Exp) (q, ε, Exp) (q, Exp * Exp) (q, (, () (q, ε)
(q, ), )) (q, ε) (q, +, +) (q, ε) (q, *, *) (q, ε)
(q, num, num) (q, ε) }
IFT313 © Froduald Kabanza
16
Exemple 2 (suite)
Entrée : num+num*num
(p, $, num+num*num$) (q, Exp$, num+num*num$) (q, Exp+ Exp$, num+ num*num$)
(q, num+Exp$, num+num*num$) (q, +Exp$, +num*num$) (q, Exp$, num*num$) (q, Exp*Exp$, num*num$) (q, num*Exp$, num*num$) (q, *Exp$, *num$) (q, Exp$, num$)
(q, num$, num$) (q, $, $)
Accepte
M = ({p,q}, V U A, A, R, p, {q},$) : R = {
1. (p, ε, ε) (q, Exp) 2. (q, ε, Exp) (q, num) 3. (q, ε, Exp) (q, (Exp) ) 4. (q, ε, Exp) (q, Exp + Exp)
5. (q, ε, Exp) (q, Exp * Exp)
6. (q, (, () (q, ε) 7. (q, ), )) (q, ε) 8. (q, +, +) (q, ε) 9. (q, *, *) (q, ε)
10. (q, num, num) (q, ε) }
Configuration Transition 1 4 2 10 8 5 2 10 9 2 10
Règle
Exp Exp+Exp Exp num
Exp Exp*Exp Exp num Exp num
IFT313 © Froduald Kabanza
17
Retour à l’intuition derrière la construction
- Une transition prédictive (predictive transition) essaie de prédire la règle de pro- duction qui pourrait être appliquée à l’étape courante pour dériver le reste de la chaîne d’entrée.
Pour ce faire la partie gauche de la production (un non-terminal) est enlevée de la pile et remplacée par la partie droite de la production.
(q, ε, A) (q, a) pour chaque A a dans P
- Une transition match (match transition) reconnaît un symbole (token) qui com- mence le reste de la chaîne d’entrée, en utilisant le contenu de la pile.
Si le symbole au sommet de la pile correspond au prochain symbole sur l’entrée, le sym- bole est enlevé de la chaîne d’entrée (il est lu) et de la pile (il est reconnu).
(q, a, a) (q, ε) pour chaque terminal a
IFT313 © Froduald Kabanza
18
Vers un driver pours parseurs LL
- En réalité, les transitions de l’automate ainsi formulées peuvent être implémentées à la volée, en partant directement des règles de production de la grammaire, sans avoir à décrire l’automate à pile explicitement.
Les déplacements des symboles sur la pile, et de la pile, se font directement, en partant des règles de production de la grammaire.
A chaque étape on doit décider si on prédit une règle de production pour
dériver le reste de la chaîne ou si on matche le prochain symbole du reste de la chaîne.
- Le driver LL suivant implémente un tel automate à pile piloté directement par la grammaire.
- Il est important de garder à l’esprit que ce driver n’est rien d’autre qu’une simula- tion à la volée d’un automate à pile dérivé directement d’une grammaire hors-con- texte par la procédure qu’on vient de voir.
IFT313 © Froduald Kabanza
19
Pilote LL
Algorithm LLDriver
variables : stack (pile), x (symbole au sommet de la pile), a (symbole d’entrée courant), in (entrée) initialement la pile contient $S (le symbole départ S) et l’entrée contient w$ (chaîne de tokens w).
while (true)
{ if (x = = $) && (a= = $) return true ; // on accepte la chaîne d’entrée comme étant correcte if (x = = a) // match transition
{ pop a from the stack; // dépiler le symbole de la pile
a = in.read(); // lire le token (symbole) courant et avancer la tête de lecture continue;}
if x is a nonterminal // predictive transition
{ find a production x y1 … yk; // trouver une production dont la partie gauche est x // les essayer tous jusqu’à en trouver menant à l’acceptation (backtracking) exit with error if no such production exists;
pop x from the stack;
push yk on the stack; …; push y1 on the stack;
continue; } exit with error;}
IFT313 © Froduald Kabanza
22
Exemple
G = (
{S}
,{a,b}
,P,S) : P = {S ε, S aSb}S$
aSb$
Sb$
aSbb$
Sbb$
bb$
b$
$ return true
Pile
0.
3.
2.
3.
2.
3.
2.
2.
1.
Étape
Nous avons déjà vu qu’en affichant les rè- gles de production correspondant aux tran- sitions de l’automate, on obtient la dériva- tion la plus à gauche de la chaîne.
Règle Dérivation
S Þ aSb Þ
S aSb
aaSbb Þ
S
ε aabb
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
aabb$
aabb$
abb$
abb$
bb$
bb$
b$
$ S aSb
IFT313 © Froduald Kabanza
23
Exemple
G = (
{S}
,{a,b}
,P,S) : P = {S ε, S aSb}S$
aSb$
Sb$
aSbb$
Sbb$
bb$
b$
$ return true
Pile
0.
3.
2.
3.
2.
3.
2.
2.
1.
Étape
Observation 1 : La chaîne obtenue en con- caténant le préfixe de la chaîne déjà scan- née (soulignée) et le contenu de la pile est une forme sententielle gauche.
Règle
S Þ aSb aSb aaSbb aaSbb aabb aabb aabb S aSb
S
ε
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
aabb$
aabb$
abb$
abb$
bb$
bb$
b$
$ S aSb
S Þ déjà lu + pile
*
*
IFT313 © Froduald Kabanza
24
Exemple
G = (
{S}
,{a,b}
,P,S) : P = {S ε, S aSb}S$
aSb$
Sb$
aSbb$
Sbb$
bb$
b$
$ return true
Pile
0.
3.
2.
3.
2.
3.
2.
2.
1.
Étape
Observation 2 : la chaîne restant à lire est dérivable du contenu de la pile pour toute exécution qui accepte.
Règle
S Þ aabb aSb Þ aabb Sb Þ abb aSbb Þ abb Sbb Þ bb bb Þ bb b Þ b ε Þ ε S aSb
S
ε
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
aabb$
aabb$
abb$
abb$
bb$
bb$
b$
$ S aSb
chaîne restante Þ pile*
*
*
*
**
*
*
*
IFT313 © Froduald Kabanza
25
Résumé
- Les algorithmes d’analyse syntaxique descendante sont aussi dit « prédictives » parce qu’ils prédisent la production à appliquer à chaque étape de la dérivation.
- L’algorithme LLDriver implémente un automate à pile non-déterministe pour une analyse syntaxique LL, descendante.
- Le non-déterminisme est implémenté en mémorisant les différents choix possibles de règles de production applicables à un moment donné, de manière à revenir en ar- rière (backtracking) si un choix s’avère un échec.
- La leçon suivante explique comment éliminer le non déterminisme en prédisant la bonne règle de production à utiliser, en fonction, non seulement du symbole au sommet de la pile comme dans la version précédente, mais aussi du prochain token.
- Cela mène à un algorithme efficace d’analyse syntaxique LL, descendant, non- récursif.