Documente Academic
Documente Profesional
Documente Cultură
Preambule
Ce polycopie est destine aux e tudiants en informatique des Licences et Masters de lUniversite Mohamed Premier. Il est e crit dans le but detre utilise comme
support de cours du module Compilation de la Fili`ere SMI (Semestre 5). Il a
comme objectif principal dintroduire les trois premi`eres e tapes de la phase danalyse dun compilateur, a` savoir lanalyse lexicale, lanalyse syntaxique et lanalyse
semantique.
Une version e lectronique de ce polycopie est e galement disponible dans la page
du site web du cours a` ladresse : http ://www.comp.sci.sitew.com
Introduction a` la compilation
1.1 Notion de compilation . . . . . . . . . . . . .
1.1.1 Definition . . . . . . . . . . . . . . . .
1.1.2 Avantages dun compilateur . . . . . .
1.1.3 Les principales taches dun compilateur
1.2 Les phases dun compilateur . . . . . . . . . .
1.2.1 Le front-end . . . . . . . . . . . . . .
1.2.2 Le back-end . . . . . . . . . . . . . . .
1.2.3 Les differents modules . . . . . . . . .
1.3 Les interpreteurs . . . . . . . . . . . . . . . .
1.4 Caracteristiques dun bon compilateur . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
1
2
3
3
4
4
5
6
7
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
9
9
10
10
11
12
14
15
15
16
16
17
17
18
20
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
`
TABLE DES MATIERES
iv
2.10.2 Equivalence
de Nerode . . . . . . . . . . . . . . . .
2.10.3 Algorithme de Hopcroft & Ullman . . . . . . . . . .
2.10.4 Un exemple dapplication . . . . . . . . . . . . . .
2.11 Langages reguliers et automates finis . . . . . . . . . . . . .
2.11.1 Algorithme BMC . . . . . . . . . . . . . . . . . . .
2.11.2 Methode algebrique . . . . . . . . . . . . . . . . .
2.12 Lemme de pompage . . . . . . . . . . . . . . . . . . . . . .
2.5
2.6
Alanyse lexicale
3.1 Role de lanalsye lexicale . . . . . . . . . . . . . .
3.2 Unites lexicales, lex`emes et mod`eles . . . . . . . .
3.2.1 Definitions . . . . . . . . . . . . . . . . .
3.2.2 Les unites lexicales les plus courantes . . .
3.3 Interface avec lanalyseur syntaxique . . . . . . . .
3.3.1 Position de lanalyseur lexical . . . . . . .
3.3.2 Attribut dun lex`eme et table des symnoles
3.4 Implementation dun analyseur lexical . . . . . . .
3.5 La methode manuelle . . . . . . . . . . . . . . . .
3.6 La methode semi-automatique . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
20
21
22
22
22
25
27
27
28
29
30
30
31
33
33
34
34
36
36
38
38
40
42
42
45
46
.
.
.
.
.
.
.
.
.
.
49
49
49
49
50
50
50
51
51
52
54
`
TABLE DES MATIERES
4
Loutil Flex
4.1 Presentation de Flex . . . . . . . . . . . . .
4.1.1 Le compilateur Flex . . . . . . . .
4.1.2 La fonction yylex . . . . . . . . . .
4.2 Format dun programme Flex . . . . . . . .
4.3 Les r`egles . . . . . . . . . . . . . . . . . .
4.4 Variables et fonctions predefinies . . . . . .
4.5 Routines de Flex . . . . . . . . . . . . . .
4.5.1 R`egles pour les actions . . . . . . .
4.5.2 Directives de Flex . . . . . . . . .
4.6 Comment Flex gen`ere un analyseur lexical .
4.7 Situation a` larret de lautomate . . . . . .
4.8 Exemple dun analyseur lexical avec Flex .
4.9 Quelques options de compilation avec Flex
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Grammaires hors-contexte
5.1 Notion de GHC . . . . . . . . . . . . . . . . . . . . . .
5.2 Derivations . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Langage engendre par une GHC . . . . . . . . . . . . .
5.4 Langages hors contexte . . . . . . . . . . . . . . . . . .
5.4.1 Definition . . . . . . . . . . . . . . . . . . . . .
5.4.2 Proprietes de cloture pour langages algebriques .
5.5 Proprietes de non cloture . . . . . . . . . . . . . . . . .
5.6 Arbres de derivation . . . . . . . . . . . . . . . . . . . .
5.7 Ambigute . . . . . . . . . . . . . . . . . . . . . . . . .
5.8 Ree criture de grammaires . . . . . . . . . . . . . . . . .
5.8.1 Elimination
de la recursivite immediate a` gauche
5.9.2 Elimination
de lambigute des operateurs . . . .
Analyse syntaxique
6.1 Role de lanalyse syntaxique .
6.2 Analyse syntaxique et GHC . .
6.3 Analyse descendante . . . . .
6.3.1 Principe . . . . . . . .
6.3.2 Un premier exemple .
6.3.3 Un deuxi`eme exemple
6.4 Analyse predictive . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
59
59
59
60
60
61
61
62
62
62
63
64
65
66
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
69
69
71
72
72
72
73
73
74
75
76
76
76
77
78
78
79
.
.
.
.
.
.
.
83
83
84
85
85
85
86
87
`
TABLE DES MATIERES
vi
6.5
6.6
6.7
6.8
7
Loutil Bison
7.1 Presentation de Bison . . . . . . . . . .
7.2 Etapes
pour utiliser Bison . . . . . . . .
7.3 Format de specification Bison . . . . .
7.4 La section du prologue . . . . . . . . .
7.5 La section des declarations Bison . . . .
7.5.1 Symboles terminaux . . . . . .
7.5.2 Precedence des operateurs . . .
7.5.3 Les symboles non-terminaux . .
7.5.4 Typage des symboles . . . . . .
7.6 La section des r`egles de la grammaire .
7.6.1 Ecriture
dune grammaire Bison
7.6.2 Actions . . . . . . . . . . . . .
7.6.3 La variable globale yylval . . .
7.7 La section de lepilogue . . . . . . . . .
7.8 La fonction yyparse . . . . . . . . . . .
7.9 Traitement des erreurs de syntaxe . . . .
7.9.1 Un exemple . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
87
88
89
91
91
92
92
92
93
95
95
97
99
101
101
102
102
103
104
104
105
107
107
108
108
110
111
111
112
112
113
2.1
2.2
2.3
2.4
2.5
2.6
2.7
2.8
2.9
2.10
2.11
2.12
2.13
2.14
2.15
2.16
2.17
2.18
2.19
2.20
2.21
2.22
2.23
2.24
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
4
5
6
viii
44
)
44
45
47
3.1
3.2
51
3.3
3.4
3.5
4.1
4.2
4.3
4.4
4.5
Etapes
de compilation dun fichier Flex. . . . . . . .
Fichier source a` analyser. . . . . . . . . . . . . . . .
Resultat de lanalyse lexicale. . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
59
64
65
66
66
5.1
5.2
5.3
5.4
5.5
5.6
5.7
5.8
Un arbre de derivation. . . . . . . . . . . . . . . . . . .
Deux arbres de derivation distincts pour le mot w = xyz.
Algorithme de lelimination de la RIG. . . . . . . . . .
Algorithme de lelimination de la RG. . . . . . . . . . .
Algorithme de factorisation a` gauche. . . . . . . . . . .
Larbre de derivation de lexpression 2 3 4. . . . . .
Larbre de derivation de lexpression 2 3 4. . . . . .
Larbre de derivation de lexpression 2 + 3 4. . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
74
75
76
77
78
80
81
82
6.1
6.2
6.3
6.4
6.5
6.6
6.7
6.8
6.9
6.10
6.11
6.12
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
84
85
85
86
86
93
94
94
95
95
97
97
7.1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
52
54
55
57
Chapitre 1
Introduction a` la compilation
Sommaire
1.1
1.2
1.3
1.4
1.1
Notion de compilation . . . . . . . . . . . . .
1.1.1 Definition . . . . . . . . . . . . . . . .
1.1.2 Avantages dun compilateur . . . . . .
1.1.3 Les principales taches dun compilateur
Les phases dun compilateur . . . . . . . . .
1.2.1 Le front-end . . . . . . . . . . . . . .
1.2.2 Le back-end . . . . . . . . . . . . . . .
1.2.3 Les differents modules . . . . . . . . .
Les interpreteurs . . . . . . . . . . . . . . . .
Caracteristiques dun bon compilateur . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
2
3
3
4
4
5
6
7
Notion de compilation
1.1.1 Definition
Definition 1.1.1 Un compilateur est un programme qui traduit un programme
e crit dans un langage L vers un autre programme e crit dans un autre langage L0 .
Durant ce processus de traduction, le compilateur detectera e galement les erreurs
e videntes du programmeur.
Introduction a` la compilation
1.1.2
1.1.3
. Ecrire
dans un langage intermediaire la sequence dinstructions qui seront
executees par la machine.
. Optimiser cette sequence dinstructions, notament la taille du code intermediaire
et sa vitesse dexecution.
. Traduire les instructions du langage intermediaire dans le langage cible.
Introduction a` la compilation
1.2.1
Le front-end
1.2.2
Le back-end
ameliore de facon a` ce que le code machine resultant sexecute le plus rapidement possible.
. La production du code cible : le code intermediaire optimise est convertit en code cible qui est soit du code machine translatable ou du code en
lanagage dassemblage. Une tache cruciale de cette e tape est lallocation
des registers : les noms des variables symboliques utilises dans le code intermediaire seront traduits en nombres dont chacun correspond a` un registre
dans le code machine cible.
La figure 1.3 resume le schema globale dun compilateur avec ses e tapes :
Introduction a` la compilation
1.3
Les interpreteurs
suite, linterpretation est typiquement plus lente que lexecution dun programme
compile.
Exemple 1.3.1 Basic, Visual Basic, JavaScript et Matlab sont des langages interpretes.
La compilation et linterpretation peuvent e tre combinees pour implementer un
langage de programmation : le compilateur produira le code intermediaire, lequel
est ensuite interprete par un interpreteur (au lieu detre compile en code machine).
1.4
Chapitre 2
Expressions reguli`eres et automates
finis
2.1
Langages formels
2.1.1 Alphabets
Definition 2.1.1 Un alphabet est un ensemble A fini non vide (A 6= ) de symboles.
Par exemple, pour decire des nombres binaires, on utilise lalphabet A = {0, 1}.
LASCII et lEBCDIC sont des exemples dalphabet informatiques. Les symboles les plus couramment utilises sont :
Les lettres : a, b, ..., z, A, B, ..., Z, , , ...,
Les chiffres : 0, 1, ..., 9
Les symboles mathematiques : +, -, *, /,
2.1.2
, ...
Mots
Definition 2.1.2 Un mot (ou chane) w sur un alphabet A, est une seqeunce finie
et ordonnee de symboles de A. Si la suite des symboles de w est formee par les
symboles respectifs : a1 , a2 , ..., an (o`u ai A denote le i-`eme symbole de w), on
e crit w = a1 a2 ...an .
Definition 2.1.3 Si w = a1 a2 ...an est un mot, le nombre n represnte le nombre de
symboles contenus dans le mot w et sappelle la longueur du mot w. On le note
aussi par |w|.
10
2.1.3
Langages
2.1.4
. Egalit
e : deux mots u et v sur un meme alphabet A sont e gaux, si et seulement si |u| = |v| = l et ui = vi , pour i = 1, 2, ..., l.
. Concatenation : soient u et v deux mots sur un meme alphabet A. La
concatenation de u et v qui secrit uv, est le mot forme en joignant les
symboles de u et v. Par exemple, la concatenation de u = 101 et v = 00 est
le mot uv = 10100.
. Puissance : soient w un mot sur un alphabet A et n 0 un entier. La puissance n-`eme du mot w qui se note wn , est le mot obtenu par la concatenation
11
si n = 0
wn =
(2.1)
n1
w w si n 1
. Miroir : le miroir dun mot w = a1 a2 ...an est le mot w
e = an ...a2 a1 obtenu
g
en inversant les symboles de w. Par exemple, aabaabc = cbaabaa. Un mot
w est un palindrome sil est identique a` son miroir, cest-`a-dire w
e = w. Par
exemple, abba est un palindrome.
Les operations sur les mots verifient les proprietes suivantes (u, v, w e tant des
mots sur un alphabet A et n, m des entiers) :
1. |uv| = |u| + |v|.
2. u(vw) = (uv)w = uvw : la concatenation est associative.
3. w = w = w : le mot vide est neutre pour la concatenation.
4. |wn | = n|w|.
5. wn+m = wn wm .
6. (wn )m = wnm .
7. |w|
e = |w|.
8. u
fv = veu
e.
ee = w : le miroir est une operation involutive.
9. w
Remarque 2.1.3 La concatenation nest pas commutative.
2.1.5
12
Comme exemple, donnons les prefixes, les suffixes, les facteurs et les sous-mots
du mot w = 1011 sur lalphabet A = {0 1} :
Prefixes : , 1, 10, 101 et 1011 = w.
Suffixes : , 1, 11, 011 et 1011.
Facteurs : , 0, 1, 01, 10, 11, 011, 101 et 1011.
Sous-mot : , 0, 1, 01, 10, 11, 011, 101, 111 et 1011.
2.1.6
Les langages sont avant tout des ensembles, nous pouvons alors leur appliquer
les operations ensemblistes : reunion, intersection, complementaire, difference et
difference symetrique. Pour lanalyse lexicale, on sinteresse principalement aux
trois operations suivantes : reunion, concatenation, fermeture et a` leurs derivees.
Voici leurs definitions :
. Reunion. La reunion de deux langages L et M sur un meme alphabet A
notee L M , est definie par :
L M = {w A | w L ou w M }
. Concatenation. La concatenation de deux langages L et M sur un meme
alphabet A notee LM , est definie par :
LM = {uv A | u L et v M }
. Puissance. La puissance n-`eme dun langage L sur A, notee Ln , est le langage defini par recurrence par :
(
{}
si n = 0
Ln =
(2.2)
n1
L L si n 1
13
[
L =
Li = L0 L1 L2 ...
i=0
L =
Li = L1 L2 L3 ...
i=1
14
(L ) = L .
L+ = LL = L L.
g =K
e L.
e
(LK)
e K.
e
12. (Lg
K) = L
e
e = L.
13. L
g
) = (L)
e .
14. (L
9.
10.
11.
2.2
La forme des lex`emes dune unite lexicale peut e tre decrite de mani`ere informelle dans le manuel du langage. Par exemple, pour le langage C, un identificateur est une suite de lettres, de chiffres et de caract`eres de soulignement qui
commence par une lettre ou un caract`ere de soulignement et qui ne soit pas un
mot-cle du langage. Une telle description est satisfaisante pour le programmeur
humain, mais certainement pas pour le compilateur.
Pour lanalyse lexicale, une specification est traditionnellement e crite en utilisant
une expression reguli`ere : cest une notation algebrique compacte pour decrire
les unites lexicales. Cette notation est a` la fois simple a` comprender et a` utiliser par
les programmeurs humains, mais e galement manipulable dune mani`ere simple
par un programme informatique.
15
2.2.1 Definition
La definition suivante explique sans ambiguite et dune facon parall`ele, ce qui
est une expression reguli`ere et le langage quelle denote (represente). Pour noter
une expression reguli`ere, nous utilisons les symbles r, s, t, ...etc, et leurs langages
L(r), L(s), L(t), ...etc.
Definition 2.2.1 Soit A un alphabet quelconque.
est une expression reguli`ere qui denote le langage vide .
est une expression reguli`ere qui denote le langage {} reduit au mot vide.
Attention, les langages et {} sont distincts ({} =
6 ).
Tout symbole a A est une expression reguli`ere qui denote le langage {a}
(langage reduit au seul mot a, symbole de lalphabet A).
r|s est une expression reguli`ere qui denote la reunion des deux langages
denotes respectivement par r et s, cest-`a-dire L(r|s) = L(r) L(s).
rs est une expression reguli`ere qui denote la concatenation des deux languages L(r) et L(s), cest-`a-dire L(rs) = L(r)L(s).
r est une expression reguli`ere qui denote la fermeture de Kleene du language L(r), cest-`a-dire L(r ) = (L(r)) .
2.2.2
R`egles de precedence
16
Les trois operateurs sassocient de gauche a` droite. Selon ces conventions, lexpression reguli`ere a|ab est e quivalente a` (a|(a(b ))).
La definition sugg`ere de confondre une expression reguli`ere s avec le langage
L(s) quelle denote (s L(s)). Comme exemple, le langage {a, b}({cc} ){b}+
sera confondu avec lexpression reguli`ere quelle le denote, a` savoir (a|b)(cc) b+ .
2.2.3
2.2.4
Quelques exemples
Mots-cles. Un mot-cle (comme if) est decrite par une expression reguli`ere
qui coincide avec ce mot-cle.
Identificateurs. En C par exemple, un identificateur consiste en une sequence
de lettres, chiffres et le caract`ere de soulignement et doit commencer par une
lettre ou le symbole souligne. Cela peut e tre decrit par lexpression reguli`ere
[a zA Z ][a zA Z 0 9] .
Nombres entiers. Une constante enti`ere commence par un signe 0 (facultatif),
et suivie dune sequence non vide de chiffres, do`u lexpression reguli`ere
pour designer des entiers en C : ?[0 9]+ .
17
2.2.6
Definitions reguli`eres
Pour des commodites de notation, on peut souhaiter donner des noms aux
espressions reguli`eres et definir des expressions reguli`eres en utilisant ces noms
comme sils e taient des symboles.
18
Definition 2.2.3 Si A est un alphabet de base, une definition reguli`ere est une
suite de definition de la forme :
d1 r1
d2 r2
...
dn rn
o`u chaque di est un nom distinct et chaque ri est une expression reguli`ere sur
A {d1 , d2 , ..., dn }.
2.3
.
[ ]
r{n, m}
r{n, }
r{n}
r1 |r2
r1 /r2
r
$r
\t ou \t
\n ou \n
{}
()
19
Description
un seul caract`ere x
concatenation des expressions reguli`eres r1 et r2
e toile de lexpression reguli`ere r
e toile positive de lexpression reguli`ere r
e quivalent a` (r|)
un caract`ere parmis ceux entre les crochets, cesta` -dire x, y ou z
un caract`ere parmis ceux entre les crochets, mais
entre c et f , cest-`a-dire c, d, e ou f (, a` linterieur
de [ ], est loperateur dintevalle)
nimporte quel caract`ere, sauf le caract`ere retour a`
la ligne \n
nimporte quel caract`ere, sauf ceux entre les crochets (operateur de complementation)
de n a` m fois lexpression reguli`ere r, cest-`a-dire
rn , rn+1 , ..., rm
un nombre de fois n lexpression reguli`ere r,
cest-`a-dire rn , rn+1 , ..., etc
exactement n fois lexpression reguli`ere r, ou rn
reunion des expressions reguli`eres r1 et r2
r1 , mais seulement si elle est suivie de r2
r, mais sauf si elle est au debut dune ligne
r, mais sauf si elle est a` la fin dune ligne
un caract`ere tabulation
un caract`ere retour a` la ligne
operateur de referencement dune definition
reguli`ere
operateur de regroupement
20
2.4
Langages reguliers
On ne peut pas decrire tous les langages par des expressions reguli`eres. Par
exemple, le langage {an bn | n 0} ne peut pas e tre decrit par une expression
reguli`ere.
Definition 2.4.1 Un langage L sur un alphabet A est dit regulier si et seulement
sil existe une expression reguli`ere r qui le denote, cest-`a-dire telle que L =
L(r)).
Exemple 2.4.1 Les langages suivants sont reguliers :
Le langage forme par les mots sur {0, 1} qui se terminent pas 0 : il est
denote par (0|1) 0.
Le langage forme par les mots sur {a, b} ayant exactement une occurrence
de b : il est denote par a ba .
Le langage forme par les mots sur {a, b} de longueur 2 : il est denote
par (a|b)(a|b)(a|b) .
2.4.1
Proprietes de cloture
Theor`eme 2.4.2 Soit A un alphabet. Lensemble des langages reguliers sur A est
clos par operations reguli`eres :
21
22
2.6
2.6.1
2.6.2
23
24
La fonction de transition dun AFD peut e tre representee a` laide dun tableau
bidimensionel appele matrice de transition. Les lignes de cette matrice sont indexees par les e tats et les colonnes par les symboles de lentree. Une case (q, a),
o`u q E et a A represente letat darrivee de la transition avec le symbole a
qui part de letat q. Si cette transition nest pas definie, cette case est laissee vide.
La matrice de transition de lAFD de lexemple est la suivante :
0
1
a
0
1
b
1
25
26
27
2.7
28
2.7.1
29
1
2
3
b
{3}
{2}
{1, 3}
F = {3}.
30
2.8
Lalgorithme de Thompson donne un moyen pour convertir automatiquement une expression reguli`ere r denotant un langage L en un AFND ayant certaines proprietes et qui acc`epte le meme langage L.
2.8.1
AFND normalise
Lalgorithme de Thompson construit en fait un AFND normalise. Ce dernier est un cas special dun AFND :
1. Il poss`ede un seul e tat initial q0 .
2. Il poss`ede un seul e tat final qf 6= q0 .
3. Aucune transition ne pointe sur letat initial.
31
32
33
2.9
2.9.1
-cloture
34
2.9.2
2.9.3
35
36
2.10
Un AFD obtenu a` partir dun AFND peut avoir un nombre detats plus grand
que celui de lAFND. Dun point de vue pratique, il est tr`es interessant davoir un
AFD ayant un nombre minimal detats. En theorie des langages, on montre que
tout AFD poss`ede un AFD e quivalent minimal.
Nous presentons un algorithme de minimisation dun AFD : lalgorithme de
Moore. Lalgorithme demarre avec un AFD simplifie (dont tous les e tats sont
utiles). Lidee de lalgorithme de Moore est de reduire lautomate en identifiant
les e tats appeles inseparables.
37
38
2.10.2
Equivalence
de Nerode
2.10.3
39
40
2.10.4
Un exemple dapplication
Groupes Etats
a
b
#1
1
#1 #1
#1
2
#1 #1
#1
3
#2
#1
5
#1 #1
#1
6
#2
#2
4
#2 #2
#2
7
#2 #2
Les e tats 1 et 3 sont separables, ainsi que 1 et 6. Remarquons que le groupe
#2 = {4, 7} est non cassable.
On obtient alors une nouvelle partition avec trois goupes : #1 = {1, 2, 5},
#2 = {3, 6} et #3 = {4, 7}. On calcule la nouvelle fonction de transition
relative a` cette deuxi`eme partition :
41
Groupes
#1
#1
#1
#2
#2
#3
#3
Etats
1
2
5
3
6
4
7
a
#1
#2
#2
#3
#3
#3
#3
b
#1
#2
#2
#3
#3
Etats
1
2
5
3
6
4
7
a
#2
#3
#3
#4
#4
#4
#4
b
#2
#3
#3
#4
#4
Le processus de partionnement est terminee car tous les groupes sont maintenant incassables.
On obtient lautomate minimal en transformant chaque groupe en e tat. Il y
a donc 4 e tats : #1, #2, #3 et #4. Letat initial est q0 = #1 et lensemble
des e tats finaux est F = {#4}. La fonction de transition est :
#1
#2
#3
#4
a
#2
#3
#4
#4
b
#2
#3
#4
42
2.11
2.11.1
Algorithme BMC
43
Cet algorithme sarrete toujours, parce que lon diminue, a` chaque iteration, le
nombre de transitions et detats, jusqu`a obtenir une seule transition (, e, ). Il
est clair que e est une expression reguli`ere pour le langage L(M ) reconnu par M .
Comme exemple, considerons lAFD de la figure 2.5. On lui ajoute les e tats et
, et on obtient lautomate suivant :
44
45
X1 = aX2 | bX3
(2.3)
X2 = aX2 | bX3 |
X3 =
Un tel syst`eme peut e tre resolu facilement en appliquant la methode des substitutions et le resultat du lemme suivant :
Lemme 2.11.2 (Lemme dArden). Soient E et F deux langages sur un alphabet
A. Si 6 E, alors lunique solution de lequation X = EX | F est X = E F .
Preuve du lemme dArden. Posons Y = E F . Alors, EY | F = EE F | F =
E + F | F = (E + {})F = E F = Y , ce qui montre que E F est bien une
solution de lequation X = EX | F .
46
Supposons maintenant que lequation X = EX | F poss`ede au moins deux solutions L et K. On a : L \ K = (EL | F ) \ K = EL \ K, car F K. Or,
(EL \ K) (EL \ EK), puisque EK K. Par suite (L \ K) E(L \ K), car
(EL \ EK) E(L \ K). Par recurrence, on obtient que (L \ K) E n (L \ K),
pour tout entier n 1. Comme le mot vide nappartient pas a` E, alors tout mot
de L \ K a pour longueur au moins n pour tout n 1. Par consequent, L \ K =
et alors L K. On montre de meme que K L (car L et K jouent des roles
symeetriques). Finalement, on a montre que L = K.
Resolvons le syst`eme (2.3). En substituant la troisi`eme e quation dans les deux
premi`eres, on obtient :
X1 = aX2 | b
(2.4)
X2 = aX2 | (b | )
X3 =
En appliquant le lemme dArden a` la deuxi`eme e quation, on trouve :
X2 = a (b|)
Finalement, on obtient :
X1 = a(a (b|))|b = a+ b|a+ |b = (a+ |)b|a+ = a b|a a = a (a|b)
2.12
Lemme de pompage
Etant
donne un alphabet A et un langage L defini sur A. Comment savoir si
L est regulier ? Pour une reponse affirmative, il suffit de trouver une expression
reguli`ere qui le denote ou un automate fini qui le reconnat. Pour une reponse
negative, on utilise un outil theorique : le lemme de pompage.
Theor`eme 2.12.1 Si un lanage L est regulier sur A, alors il existe un entier naturel N tel que pour tout mot w L, de longueur |w| N , w secrit sous la forme
w = xyz avec :
1. |xy| N .
2. y 6= , (ou bien |y| 1).
3. xy k z L, k 0.
Intuitivement, N cest le nombre des e tats de lAFD minimal reconnaissant L.
Pour accepeter un mot w L de longueur |w| N , il faut au moins visiter un
47
certain e tat e plus dune fois (ce qui est illustre par la figure 2.29). Soit le mot x lu
depuis letat initial q0 jusqu`a letat e, le mot y lu sur une boucle autour de letat e
et le mot z lu depuis letat e vers un e tat final qf . Il est clair alors que w = xyz et
que lon a : |xy| N , y 6= et xy k z L, k 0.
Chapitre 3
Alanyse lexicale
3.1
50
Alanyse lexicale
Exemple 3.2.2 En langage C, if, for, int et return sont des lex`emes de lunite
lexicale mot-cle.
Definition 3.2.3 Un mod`ele dunite lexicale est une r`egle qui decrit lensemble
des lex`emes qui seront des instances de cette unite lexicale.
Exemple 3.2.3 En langage P HP , le mod`ele dun identificateur est une chane
de caract`eres qui commence par une lettre (minuscule ou majuscule) ou le caract`ere ou le caract`ere $ et qui ne contient que des lettres, des chiffres et des
caract`eres et $.
3.2.2
3.3
3.3.1
51
des caract`eres non encore lus et delivre la premi`ere unite lexicale rencontree. La
figure ci-dessous montre la relation (de type producteur/consommateur) qui relie
lanalyseur lexical et lanalyseur syntaxique :
3.4
52
Alanyse lexicale
3.5
La methode manuelle
Lecriture dun analyseur lexical a` la main nest pas souvent tr`es difficile : on
ignore tous les caract`eres inutiles, jusqu`a ce quon trouve le premier caract`ere
significatif. En suite, on teste si le prochain lex`eme est un mot-cle, un nombre, un
identificateur, ...etc.
On va maintenant implementer dans un langage de haut-niveau (langage C, par
exemple) un analyseur lexical qui lit et convertit le flot dentree en un flot dunites
lexicales. Pour simplifier, nous nous limitons a` un fragment dun langage source
fictif. Pour ce fragment du langage considere, lanalyseur lexical reconnatra les
unites lexicales suivantes :
. KEYWORD qui denote les quatre mots-cles : si, alors, sinon et finsi.
. IDENT qui denote les identificateurs.
. NBENT qui denote les nombres entiers. Lattribut est dans ce cas, la valeur
decimale de lentier reconnu.
On fait e galement les hypoth`eses suivantes :
les mots-cles sont reserves, cest-`a-dire quils ne peuvent pas e tre utilises
comme identificateurs.
les lex`emes sont separes par un caract`ere blanc (espace, tabulation et retour
a` la ligne). Notre analyseur e liminera ces caract`eres.
La figure suivante sugg`ere la mani`ere dont lanalyseur lexical, represente par la
fonction AnalLex(), realise sa tache. La fonction getchar() permet de lire le
prochain caract`ere sur lentree standard et la fonction ungetc() permet de rendre
ce caract`ere au flot dentree.
F IG . 3.2 Implementation dun anlyseur lexical e crit a` la main (en langage C).
Ainsi, le code suivant :
53
54
Alanyse lexicale
3.6
La methode semi-automatique
55
56
Alanyse lexicale
Lorsquon arrive a` letat 1, on lit un caract`ere, puis on effectue les tests suivants :
1. Si le caract`ere lu est une lettre ou un chiffre, on reste dans letat 1, tout en
enregistrant le caract`ere lu dans la position adequate de la chane.
2. Pour tout autre caract`ere lu, on rend le caract`ere lu et lon passe a` letat 2.
Lorsquon arrive a` letat 3, on lit un caract`ere, puis on effectue le test suivant :
1. Si le caract`ere lu est un chiffre, on reste dans letat 3, tout en continuant
levaluation du nombre.
2. Pour tout autre caract`ere lu, on rend le caract`ere et lon passe a` letat 4.
Le traitement des e tats finaux seffectue comme suit :
1. Si letat courant est 2, reculer la lecture du caract`ere. Si la chane trouvee
est un identificateur, retourner IDENT, sinon retourner KEYWORD.
2. Si letat courant est 4, reculer la lecture du caract`ere et retourner NBENT.
Voici le pseudo-code de la simulation de lAFD de notre analyseur :
57
Chapitre 4
Loutil Flex
4.1
Presentation de Flex
60
Loutil Flex
4.1.2
La fonction yylex
Le fichier lex.yy.c fournit une fonction externe nommee yylex() qui va realiser
lanalyse lexicale des lex`emes. La compilation de ce fichier par gcc, et sa liaison
avec la librairie lfl de Flex produira un analyseur lexical. Par defaut, ce derniers va lire les donnees a` partir de lentree standard (stdin) et e crira les resultats
sur la sortie standrad (stdout). La fonction yylex na pas dargument et retourne
un entier qui designe le code de lunite lexicale reconnue.
4.2
Flex est cree pour e tre utilise avec C (ou C++). Un programme Flex se compose de trois sections (dans cet ordre) : la secdion des definitions, la section des
r`egles, et la section du code auxilliaire. Voici le format dun fichier Flex :
%{
/* Inclusions et D
eclarations */
%}
/* D
efinitions des expressions r
eguli`
eres */
%%
/* R`
egles */
%%
/* Code C auxilliaire */
La section des definitions comprend : les inclusions de fichiers, les declarations
globales C (variables, types et fonctions) et les definitions des expressions
reguli`eres.
La section des r`egles contient les expressions reguli`eres qui denoteront les
lex`emes a` reconnatre et les actions a` executer.
La section du code auxilliaire contient les routines C necessaires pour le
fonctionnement desire de lanalyseur.
Les declarations sont copiees en haut dans le fichier lex.yy.c, alors que les routines sont copiees en bas. Les definitions permettent de donner des noms a` des
expressions reguli`eres. Ceci a pour objectif de simplifier les e critures des expressions reguli`eres complexes dans la section des r`egles et daugmenter leur lisibilite
et leur identification.
4.3
61
Les r`egles
Une r`egle est formee dune expression reguli`ere (appelee motif) suivie dune
sequence dinstructions C (nommee action). Lidee est que chaque fois que
lanalyseur lit une entree (une chane de caract`ees) qui verifie le motif dune r`egle,
il execute laction associee avec ce motif. Par exemple, si lon veut afficher un
identificateur C, on e crira la r`egle suivante :
[a-zA-Z_][a-zA-Z_0-9]*
Les r`egles doivent e tre e crites apr`es le premier symbole %%. Les motifs doivent
commencer en colonne 0 et sont e crits sur une seule ligne. Les actions doivent
commencer sur la meme ligne, mais peuvent tenir sur plusieurs lignes. Voici la
forme de la section des r`egles :
exp_reg_1 action_1
exp_reg_2 action_2
...
exp_reg_n action_n
4.4
62
Loutil Flex
4.5
4.5.1
Routines de Flex
R`egles pour les actions
. Si pour un motif, laction nest pas presente, alors le lex`eme correspondant sera tout simplement ignore. La meme chose se produira si lon a
specifier laction notee ;. Par exemple, voici un programme qui supprimera toutes les occurrences des mots mauvais et bon :
%%
"bon"
"mauvais"
. Flex recopie dans le fichier de sortie toutes les chanes de lentree non reconnues par aucune expression r`eglui`ere : cest laction par defaut.
. Une action | signifie la meme chose que laction de la r`egle suivante.
Elle est utilisee quand plusieurs r`egles partagent la meme action. Voici un
exmple :
%%
a
ab
abc
abcd
4.5.2
|
|
|
printf("a, ab, abc ou abcd\n");
Directives de Flex
Flex offre un nombre de routines speciales qui peuvent e tre integrees avec les
actions :
. ECHO : copie le contenu de la variable yytext dans le fichier de sortie. Si
ce dernier est stdout, ECHO est e quivalente a` laction
printf("\%s", yytext);
63
%{
int nb_mots = 0;
%}
%%
bon
REJECT;
[a-z]* nb_mots++;
. yyless(n) : rend tout les caract`eres du lex`eme courant dans le flot dentree,
sauf les n premiers caract`eres de ce lex`eme. Les variables yytext et yyleng seront ajustees automatiquement. Par example, sur lentree foobar,
le programme suivant affichera foobarbar :
%%
foobar
[a-z]+
ECHO; yyless(3);
ECHO;
4.6
ECHO; yyterminate();
La table des transitions de lAFD genere par le compilateur Flex est parcourue
dans la routine yylex() par le code qui anime lautomate. Ce code est invariable
64
Loutil Flex
4.7
4.8
65
Pour terminer, nous donnons le code Flex dun analyseur lexical du fragment
du langage source e tudie dans le chapitre precedent (chapitre 3). Cet analyseur
affichera les lex`emes et leur unites lexicales. Tout dabord, on e crit le code Flex
dans un fichier texte enregistre avec lextension lex, disons lexer.lex :
%{
#include <stdlib.h>
%}
%%
alors |
finsi |
si
|
sinon printf("%s : KEYWORD\n", yytext);
[a-zA-Z_][a-zA-Z_0-9]* printf("%s : IDENT\n", yytext);
[0-9]+ printf("%d : NBENT\n", atoi(yytext))d;
[ \t\n] ;
Ensuite, nous compilons ce fichier pour obtenir lexecutable de notre analyseur,
disons lexer.exe :
F IG . 4.3 Etapes
de compilation dun fichier Flex.
Supposons que lon veut analyser le fichier texte suivant, disons data.txt :
66
Loutil Flex
4.9
67
Chapitre 5
Grammaires hors-contexte
Dans le chapitre 2, on a vu deux formalismes e quivalents pour specifier et reconnatre des unites lexicales : les expressions reguli`eres et les automates
finis. Malheurement, ces deux formalismes ne peuvent pas decrire certains langages. La theorie des langages montre par exemple quil est impossible de decrire
le langage {an bn | n 0} sur lalphabet binaire {a, b} par une expression
reguli`ere ou par un AFD (le langage nest pas regulier).
Lanalyse syntaxique est un peu plus compliquee que lanalyse lexicale et necessite
des methodes plus avancees. Cependant, la meme strategie de base est utilisee :
une notation tr`es adaptee pour les programmeurs humains est convertie en une
machine automatique (reconnaisseur) dont lexecution est plus efficace.
Un programmeur humain manipule une notation plus facile a` utiliser et a` comprendre : les grammaires hors-contexte (GHC). En theorie des langages, une
GHC est une notation recursive pour decrire certains langages. Les GHC trouvent
une importante application dans les specifications des langages de programmation. Ce sont des notations concises qui permettent de decrire la syntaxe des
langages de programmation classiques. De plus, il est possible de transformer
mecaniquement une GHC en un analyseur syntaxique. Lanalyseur fait apparatre la structure du programme source, souvent sous la forme dun arbre dexpression pour chaque instruction du programme.
5.1
Notion de GHC
Les GHC constituent un outil formel pour decrire quels sont les flux de lex`emes
corrects et comment ils doivent e tre structures. Lanalyseur lexical decrit comment
former les lex`emes du langage. Lanalyseur syntaxique decrit ensuite comment
70
Grammaires hors-contexte
assembler les lex`emes pour former des phrases correctes. Une GHC fournit une
description des phrases (programmes) syntaxiquement corrects.
Definition 5.1.1 Une GHC est un 4-uplet G = (V, T, P, S) o`u :
. V : un ensemble fini. Cest lalphabet des symboles variables ou nonterminaux.
. T : un ensemble fini disjoint de V (V T = ). Cest lalphabet des symboles terminaux).
. P : ensemble fini delements appeles r`egles de production.
. S V appele axiome (ou symbole de depart).
Les terminaux sont en fait les lex`emes qui seront delivres par lanalyseur lexical. Les non-terminaux sont des meta-symboles qui servent pour decrire les
diverses constructions dun langage (instructions, blocs, boucles, expressions, ...).
Les r`egles de production sont des r`egles de ree criture qui montrent comment
certains terminaux et non-terminaux se combinent pour former une contruction
donnee. Laxiome est un meta-symbole particulier, puisque ce dernier refl`ete
la construction generale dun langage (il identifie tout le programme). Par convention, les symboles non-terminaux seront notes en majuscule, alors que les symboles terminaux seront notes en minuscule.
Definition 5.1.2 Dans une GHC G = (V, T, P, S), une forme sur G est un mot
sur lalphabet (V T ), cest-`a-dire (V T ) .
Definition 5.1.3 Dans une GHC G = (V, T, P, S), une r`egle de production est
un couple (X, ) o`u X V et est une forme de G. On note une r`egle de
production (X, ) par : X qui se lit X peut se ree crire comme . Si
(X, ) et (X, ) sont deux r`egles de productions telles que 6= , on e crit :
X | .
Exemple 5.1.1 Soit G = (V, T, P, S) avec :
V = {S, A, B}.
T = {a, b}.
5.2. Derivations
71
P = {S AB | aS | A, A Ab | , B AS}.
S : laxiome.
Pour cette grammaire, les mots AB, aaS et sont des formes sur G.
5.2 Derivations
Definition 5.2.1 Soient G = (V, T, P, S) une GHC, et deux formes sur G. On
dit que la forme derive en une seule e tape de la forme , sil existe une r`egle
de production X et deux formes u et v telles que = uXv et = uv. On
e crit dans ce cas : 1 ou, pour simplifier .
Exemple 5.2.1 Dans la GHC de lexemple 4.3.1, on a : AB AAS. En effet,
la production B AS permet de substituer B par AS dans AB. Ce qui donne
AAS.
Definition 5.2.2 Soient G = (V, T, P, S) une GHC, et deux formes sur G et
k 1 un entier. On dit que derive en k e tapes de , sil existe (k + 1) formes
0 , 1 , ..., k telles que :
1. 0 = .
2. i i+1 , i {0, 1, ..., k 1}.
3. k =
Dans ce cas, on e crit : k . Lentier k sappelle la longueur de la derivation.
Definition 5.2.3 Soient G = (V, T, P, S) une GHC, et deux formes sur G.
On dit que derive en plusieurs e tapes de , sil existe un entier k 1, tel que
k . On e crit pour simplifier : . Par convention, 0 .
Exemple 5.2.2 Soit la GHC de lexemple 4.3.1. On a AB 8 bba. En effet, on a :
AB AbB (r`egle A Ab).
AbB AbbB (r`egle A Ab).
AbbB bbB (r`egle A ).
bbB bbAS (r`egle B AS).
72
Grammaires hors-contexte
bbAS bbS (r`egle A ).
bbS bbaS (r`egle S aS).
bbaS bbaA (r`egle S A).
bbaA bba (r`egle A ).
5.3
5.4
5.4.1
Definition 5.4.1 Soit L un langage sur un alphabet A. On dit que L est hors
contexte (ou algebrique) sur A, si et seulement si L est engendre par une GHC,
cest-`a-dire il existe une GHC G = (V, T, P, S) tel que A = T et L(G) = L.
5.5. Proprietes de non cloture
73
5.4.2
Theor`eme 5.4.2 La classe des langages algebriques est close par operations rationnelles et par substitution :
. Reunion : si L et K sont algebriques, alors L K est algebrique.
. Concatenation : si L et K sont algebriques, alors LK est algebrique.
. Etoile
: si L est algebrique, alors L est algebrique.
. Substitution : si L et K sont algebriques, alors le langage obtenu en substituant toutes les lettres a dans tous les mots L par K est algebrique.
74
Grammaires hors-contexte
5.6
Arbres de derivation
Proposition 5.6.1 Soient G = (V, T, P, S) une GHC. Un mot T appartient au langage L(G) engendre par G, si et seulement si, il existe un arbre de
derivation total de G ayant comme fronti`ere le mot .
5.7. Ambigute
75
5.7
Ambigute
Definition 5.7.1 Une GHC est dite ambigue, sil existe un mot L(G) ayant
au moins deux arbres de derivation distinctes.
Definition 5.7.2 Un langage L est dit non ambigu, sil existe au moins une GHC
non ambigue qui lengendre. Sinon, L est dit inheremment ambigu.
Exemple 5.7.1 Soit G la GHC definie par : S AB|AC, A x|xy, B z et
C yz. La grammaire G est ambigue. En effet, le mot = xyz poss`ede deux
arbres de derivation distincts :
76
5.8
5.8.1
Grammaires hors-contexte
Elimination
de la recursivite immediate a` gauche
5.8.2
Elimination
de la recursivite a` gauche
Definition 5.8.2 Une GHC G est recursive a` gauche, si elle contient au moins
un non-terminal X tel que X + X, o`u est une forme sur G.
Exemple 5.8.3 La grammaire : S Ba | b et B Bc | Sd | est recursive a`
gauche, puisque S Ba Sda.
Algorithme de lelimination de la RG :
77
5.8.3
Factorisation a` gauche
Definition 5.8.3 Une GHC G est non factorisee a` gauche, si elle contient au
moins une alternative de la forme X | o`u , et sont des formes sur
G.
Exemple 5.8.5 La grammaire : S aEbS | aEbSeB | a, E bcB | bca et
B ba nest pas factorisee a` gauche.
Algorithme de factorisation a` gauche :
78
Grammaires hors-contexte
5.9
5.9.1
79
5.9.2 Elimination
de lambigute des operateurs
Soit la grammaire ambigue suivante qui manipule un operateur :
E E E
E num
On peut ree crire cette grammaire de mani`ere a` obtenir une grammaire e quivalente
mais non ambigue. Lassociativite de loperateur influencera le choix des productions convenables :
Si nest pas associatif, nous transformons la grammaire comme suit :
E E0 E0 | E0
80
Grammaires hors-contexte
E 0 num
Notons que cette transformation modifie le langage genere par la grammaire initiale, parce quelle rend illegales les expressions de la forme num num num.
Si est associatif a` gauche, nous transformons la grammaire comme suit :
E E E0 | E0
E 0 num
Maintenant, lexpression 2 3 4 poss`ede un seul arbre de derivation :
81
82
Grammaires hors-contexte
Chapitre 6
Analyse syntaxique
6.1
84
Analyse syntaxique
dun compilateur :
6.2
6.3
85
Analyse descendante
6.3.1 Principe
Un analyseur descendant construit larbre de derivation de la chane dentree,
en partant de la racine (laxiome de la grammaire) et en creant les nuds de
larbre en preordre jusqu`a les feuilles (la chane de terminaux).
86
Analyse syntaxique
Nous repetons ce processus jusqu`a obtenir larbre de derivation totale (voir figure
6.4). Sur cet exemple, lanalyse est tr`es simple car chaque production commence
par un terminal different, et par consequent le choix de la prochane production a`
appliquer ne pose aucun probl`eme.
6.3.3
Un deuxi`eme exemple
87
6.4
Analyse predictive
88
Analyse syntaxique
6.4.2
89
90
Analyse syntaxique
Pour calculer la fonction Suivant(X) pour tous les non-terminaux X, il suffit alors
de resoudre le syst`eme de contraintes obtenues en appliquant le lemme 4.14.1,
sans oublier dajouter la production S 0 S$ de la grammaire augmentee.
Exemple 6.4.4 Soit la grammaire definie par : T R | aT c et R RbR |
On ajoute la production T 0 T $. On a alors :
1. La r`egle T 0 T $ ajoute la contrainte : {$} Suivant(T ).
2. La r`egle T R ajoute la contrainte : Suivant(T ) Suivant(R).
3. La r`egle T aT c ajoute la contrainte : {c} Suivant(T ).
4. La r`egle R RbR ajoute les deux contraintes : Suivant(R) Suivant(R)
qui est inutile, et {b} Suivant(R).
5. La r`egle R najoute rien.
Ce qui donne finalement, Suivant(T ) = {$, c} et Suivant(R) = {$, c, b}.
6.5
91
Analyse LL(1)
Soient X V le symbole non-terminal courant a` developper et a le symbole terminal de prevision (le lex`eme suivant de lentree). Il est clair que si lune
de deux r`egles suivantes est verifiee, alors on peut choisir la production X :
. (R1) : a Premier().
. (R2) : Si Nullable() = V rai et a Suivant(X).
Si on peut toujours choisir une production uniquement en utilisant ces deux r`egles,
lanalyse est dite LL(1) : cest un cas particulier danalyse predictive. La premi`ere
lettre L signifie Left-to-right scanning, cest-`a-dire que lanalyseur lit la chane
de lentree de gauche a` droite. La seconde lettre L signifie Left-most derivation, cest-`a-dire que lanalyseur effectue la derivation la plus a` gauche. Le
nombre 1 indique quil y a un seul symbole de prevision. Si lon utilise k symboles de prevision, lanalyse dans ce cas est appellee LL(k).
92
Analyse syntaxique
2. Nullable() = V rai Nullable() = F aux : les deux formes et ne
peuvent e tre nullables toutes les deux a` la fois.
3. Si Nullable() = V rai, alors ne se derive pas en une chane commencant
par un terminal a Suivant(X).
6.5.2
Un analyseur syntaxique LL(1) simplemente en utilisant lune des deux approches suivantes :
. La descente recursive : la structure de la grammaire est directement traduite en une structure dun programme danalyse.
. Lutilisation dune table danalyse : cette derni`ere va guider le processus
de choix des productions.
6.6
6.6.1
Comme son nom lindique, la descente recursive est une technique danalyse qui utilise des procedures recursives pour implementer lanalsye syntaxique
predictive. Lidee centrale est que chaque symbole non-terminal dans la grammaire est implemente par une procedure dans le programme danalyse. Chaque
telle procedure examine le symbole suivant de lentree afin de choisir une production. La partie droite de la production est alors analysee de la mani`ere suivante :
. Un symbole terminal est mis en correspondance avec le symbole suivant
de lentree. Sils concordent, on se deplace sur le symbole suivant de
lentree. Sinon, une erreur syntaxique est rapportee.
. Un symbole non-terminal est analyse en invoquant sa procedure associee.
93
6.6.2 Un exemple
Considerons la grammaire LL(1) definie par :
T R | aT c
R bR |
On ajoute la r`egle de la grammaire augmentee : T 0 T $. Lanalyse syntaxique
par descente recursive de cette grammaire sera effectuee a` travers 3 procedures
recursives : T 0 (), T () et R(). Chaque procedure danalyse X() permet de simuler
le choix dune X-production. Pour lanalyse, on a besoin des e lements suivants :
. Une variable globale nommee suivant qui joue le role du symbole de
prevision.
. Une fonction globale nommee exiger(Lex`eme), qui prend comme argument un symbole de lentree (un lex`eme) et le compare avec le prochain
symbole de lentree (le symbole de prevision). Sil y a concordance, le prochain symbole est lu dans la variable suivant.
. La procedure erreur() qui reporte une erreur de syntaxe.
Voici le pseudo-code de cette fonction :
94
Analyse syntaxique
95
a` R, avant lequel on doit verifier le prefixe b. Dans les autres cas, une erreur de
syntaxe doit e tre rapportee.
6.7
6.7.1 Principe
Dans un analyseur LL(1) dirige par table, on encode la selection des productions dans une table, appelee table danalyse LL(1), au lieu dutiliser un programme. Ensuite, un programme (non recursif) utilisera cette table et une pile
96
Analyse syntaxique
T
T
R
a
T T$
T aT c
0
b
T T$
T R
R bR
$
T T$
T R
R
0
T R
R
97
98
Analyse syntaxique
. Une pile : contient une sequence de symboles (terminaux ou non-terminaux),
avec un symbole $ qui marque le fond de la pile. Au depart, la pile contient
laxiome de la grammaire au sommet et le symbole $ au fond.
. Une table danalyse LL(1).
Un flot de sortie.
Exemple 6.7.2 Le tableau suivant illustre lanalyse predictive LL(1) dirigee par
table de la grammaire definie par :
T R | aT c
R bR |
pour la chane dentree = aabbbcc :
6.8
Entree
aabbbcc$
aabbbcc$
abbbcc$
abbbcc$
bbbcc$
bbbcc$
bbbcc$
bbcc$
bbcc$
bcc$
bcc$
cc$
cc$
c$
$
99
Sortie
T aT c
T aT c
T R
R bR
R bR
R bR
R
ACCEP T
Pour developper un analyseur LL(1) pour une grammaire, nous devons suivre
les e tapes suivantes :
1. Supprimer lambigute.
2. Eliminer
la recursivite a` gauche.
3. Factoriser a` gauche.
4. Ajouter la r`egle de grammaire augmentee.
5. Calculer le predicat N ullable et la fonction P remier pour chaque production, et la fonction Suivant pour chaque symbole non-terminal.
6. Pour un non-terminal X a` developper et un symble dentree a, choisir la
production X lorsque a P remier() et/ou si N ullable() = V rai
et a Suivant(X).
7. Implementer lanalyseur soit par un programme effectuant une descente
recursive, soit par un programme iteratif utilisant une table danalyse LL(1).
Linconvenient majeur de lanalyse LL(1) est que la majorite des grammaires
necessitent des ree critures supplementaires. Meme si ces ree critures peuvent e tre
100
Analyse syntaxique
effectuees dune mani`ere automatique, il reste un nombre important de grammaires qui ne peuvent pas e tre transformees automatiquement en grammaires
LL(1). Pour analyser de telles grammaires, nous devons recourir a` de nouvelles
techniques plus avancees.
Chapitre 7
Loutil Bison
7.1
Presentation de Bison
102
Loutil Bison
7.2
Etapes
pour utiliser Bison
Pour e crire un analyseur avec Bison, le processus comprend les e tapes suivantes :
1. Ecrire
la specification formelle de la grammaire dans un format reconnu
par Bison. Pour chaque r`egle de production du langage, decrire laction a`
executer lorsquune instance de cette r`egle est reconnue. Laction doit e tre
une sequence dinstructions C.
2. Ecrire
un analyseur lexical qui va reconnatre les lex`emes et les delivrer a`
lanalyseur syntaxique.
3. Ecrire
un controleur (un programme) qui appelle lanalsyeur produit par
Bison.
4. Ecrire
les routines derreurs.
7.3
Prologue : D
eclarations C */
D
eclarations Bison */
R`
egles de production de la grammaire */
Epilogue : Code C auxilliaire */
103
7.4
La section du prologue
Les e lements declares dans cette section seront recopies dans le fichier de
lanalyseur syntaxique en haut et avant la definition de la focntion yyparse. Si
aucune declaration C nest necessaire, on peut omettre les symboles %{ et %}.
On peut avoir plusieurs sections de prologue intermixees avec les declarations
Bison. Cela permet davoir des declarations C et des declarations Bison qui se
ref`erent les unes aux autres. Par exemple, dans le code suivant :
104
Loutil Bison
%{
#include <stdio.h>
#include "ptypes.h"
%}
%union {
long int n;
tree t; /* tree est d
efini dans "ptypes.h". */
}
%{
static void print_token_value(FILE*, int, YYSTYPE);
#define YYPRINT(F, N, L) print_token_value(F, N, L)
%}
la declaration %union peut utiliser les types definis dans les fichiers den-tete indiques dans la premi`ere section du prologue, et favorise de prototyper les fonctions
qui prennent des arguments de type YYSTYPE.
7.5
7.5.1
Les symboles terminaux (lex`emes, tokens) sont representes dans Bison par
des codes numeriques. Ces codes sont retournes par la fonction yylex de lanalyseur lexical. On na pas besoin de connatre la valeur dun code, mais seulement
le nom du symbole utilise pour lindiquer. Pour donner un nom a` un symbole
terminal, on utilise la clause %token :
%token NomSymbolique
Il y a trois moyens pour e crire des symboles terminaux :
1. Les tokens nommes : ils sont e crits en utilisant des identificateurs C. Par
convention, on les e crit en utilisant des lettres majuscules (il sagit dune
convention et non pas dune r`egle stricte). Ils doivent e tre declares dans la
section des declarations Bison en utilisant la clause %token.
2. Les tokens caract`eres : ils sont e crits en utilisant la meme syntaxe de C
pour e crire des constantes caract`eres. Par exemple, + et \n. Par convention, un token caract`ere est employe uniquement pour representer un token
105
return IDENT;
return NOMBRE;
300
0x12d
Cependant, il est generalement bon de laisser Bison choisir les codes numeriques
pour tous les tokens.
7.5.2
Bison offre des directives pour fixer la precedence et lassociativite pour les
operateurs :
106
Loutil Bison
< > =
+ -
* /
/* op
erateur de puissance */
Dans cette exemple, tous les operateurs ont une associativite gauche, sauf le dernier qui a une associativite droite. Loperateur a la plus forte priorite et les
trois premiers operateurs <, > et = ont la plus faible priorite.
Certains operateurs ont plusieurs priorites. Par exemple, le signe comme
operateur unaire poss`ede une prioprite tr`es forte, et une priorite plus faible en tant
quoperateur binaire. Les directives %left, %right et %nonassoc ne peuvent pas
e tre utilisees plusieurs fois avec les memes operateurs. Pour traiter ce probl`eme,
Bison offre une quatri`eme directive : le modificateur de priorite %prec. Comme
exemple, on va resoudre ce probl`eme pour loperateur . Tout dabord, on
declarera une precedence pour un symbole terminal artificil, disons UNMOINS. Ce
token est artificile, car il ne sera jamais delivre par lanalyseur lexical. Il servira
tout simplement a` regler la priorite. Ensuite, on declarera loperateur binaire
en e crivant :
%left -
Apr`es, on declarera loperateur unaire comme e tant de meme priorite que
loperateur UNMOINS en e crivant :
%left UNMOINS
Maintenant la precedence de UNMOINS peut e tre utilisee dans la r`egle de production appropriee :
107
...
%left + -
%left * /
%left UNMOINS
...
expr : expr - expr
| - expr %prec UNMOINS
...
La derni`ere ligne signifie que loperateur dans le contexte de la r`egle :
expr expr
a une priorite identique a` celle de loperateur artificiel UNMOINS, donc plus grande
que celle des operateurs +, binaire, et /.
7.5.3
7.5.4
108
Loutil Bison
%{
#define YYSTYPE double
%}
Si les symboles ont des types differents, ce qui est normalement le cas en pratique,
il faut declarer tous ces differents types dans une union en utilisant la directive
%union (dans la section des declarations Bison). Dans ce cas, il est necessaire de
preciser le type de chaque symbole lors de sa declaration en utilisant la directive
%type. La syntaxe de cette declaration est :
%type <Type_Name> Name
Il est e galement possible de declarer le type dun symbole terminal au moment
de sa declaration avec la directive %token selon la syntaxe suivante :
%token <Type_Name> Name
Supposons par exemple que lon a trois symboles terminaux : IDENT dont la
valeur (de son attribut) est de type chane de caract`eres, NUMBER dont la valeur
est de type entier et REAL dont la valeur est de type double. En plus, on a un
symbole terminal exp de type double. Tout dabord, on doit declarer une union
des trois types indiques comme suit :
%union {
int
intval;
double dblval;
char* strval;
} /* sans ; */
En suite, on passe aux declarations des types, en e crivant par exemple :
%token
%type
%type
%type
7.6
7.6.1
<intval>
<dblval>
<strval>
<dblval>
NUMBER
REAL
IDENT
exp
Ecriture
dune grammaire Bison
109
tete : corps ;
o`u :
. tete est le symbole non-terminal qui definit le membre gauche la r`egle
de production.
. corps est la sequence des symboles terminaux et non-terminaux qui
definissent le membre droit de cette r`egle de production.
Le point virgule ; a` la fin indique que la definition de la r`egle de production est
terminee.
Exemple 7.6.1 La r`egle de production :
exp : exp + exp ;
signifie que deux expressions separees par le symbole +, peuvent e tre combinees
pour former une autre expression.
Remarque 7.6.1 Les caract`eres espaces dans les r`egles sont utilises uniquement
pour separer les symboles.
Les r`egles de production issues dun meme symbole non-terminal peuvent e tre
definies separamment (une par une) ou joingees en utilisant le caract`ere barreverticale | comme suit :
tete : corps1
| corps2
| ...
;
Pour e crire une r`egle de production avec un membre droit qui est le symbole , il
suffit de laisser vide le corps de cette r`egle.
Exemple 7.6.2 Pour definir une sequence dexpressions formee par 0, 1, ou plusieurs expressions separees par des virgules, on e crira :
seqexp :
| seqexp1
;
seqexp1 : exp
| seqexp1 , exp
;
110
Loutil Bison
7.6.2
Actions
111
7.7
La section de lepilogue
112
7.8
Loutil Bison
La fonction yyparse
7.9
Lorsque lanalyseur produit par Bison rencontre une erreur, il appelle par
defaut la fonction yyerror(char) qui se contente dafficher le message syntax
error puis il sarreete. Cette fonction peut e tre redefinie par le programmeur.
Dans Bison, il est possible de definir la facon de recouvrir une erreur de syntaxe (recuperation sur erreur) en e crivant des r`egles qui reconnaissent un token
special : error. Cest un symbole terminal predefini et reserve pour le traitement des erreurs.
En effet, lanalyseur syntaxique produit par Bison gen`ere automatiquement un
token error chaque fois quune erreur de syntaxe survienne. Si lon a fournit une
113
7.9.1
Un exemple
114
Loutil Bison
%%
axiome : entree { puts("Cest fini"); }
;
entree : ligne
| entree ligne
;
ligne
%%
int main()
{
fp = fopen("resultats.txt", "w");
yyparse();
fclose(fp);
}
void yyerror(char* mess)
{
puts(mess);
}
Voici le code Flex de lanalyseur lexical (Fichier lexer.lex) :
%{
#include "parser.tab.h"
#include <stdlib.h>
%}
%%
[a-z]+ { yylval.strval = strdup(yytext); return NOM; }
[0-9]+ { yylval.intval = atoi(yytext); return ENTIER; }
\n
return *yytext;
[ \t] ;
.
{ printf("%c: illegal\n", *yytext); }