Vous êtes sur la page 1sur 124

Polycopie de Compilation

Prof. Abdelmajid DARGHAM


Octobre 2016

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

Table des mati`eres


Preambule
1

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

Expressions reguli`eres et automates finis


2.1 Langages formels . . . . . . . . . . . .
2.1.1 Alphabets . . . . . . . . . . . .
2.1.2 Mots . . . . . . . . . . . . . .
2.1.3 Langages . . . . . . . . . . . .
2.1.4 Operations sur les mots . . . . .
2.1.5 Parties dun mot . . . . . . . .
2.1.6 Operations sur les langages . . .
2.2 Les expressions reguli`eres . . . . . . .
2.2.1 Definition . . . . . . . . . . . .
2.2.2 R`egles de precedence . . . . . .
2.2.3 Les notations abregees . . . . .
2.2.4 Quelques exemples . . . . . . .
2.2.5 Quelques proprietes algebriques
2.2.6 Definitions reguli`eres . . . . . .
2.3 Expressions reguli`eres en Flex . . . . .
2.4 Langages reguliers . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

9
9
9
9
10
10
11
12
14
15
15
16
16
17
17
18
20

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

`
TABLE DES MATIERES

iv

2.4.1 Proprietes de cloture . . . . . . . . . . . . . . . . .


Les automates finis . . . . . . . . . . . . . . . . . . . . . .
Les automates finis deterministes . . . . . . . . . . . . . . .
2.6.1 Definition . . . . . . . . . . . . . . . . . . . . . . .
2.6.2 Representation dun AFD . . . . . . . . . . . . . .
2.6.3 Reconnaissance dun mot par un AFD . . . . . . . .
2.6.4 Algorithme de reconnaissance dun mot par un AFD
2.7 Les automates finis non-deterministes : AFND . . . . . . . .
2.7.1 Definition dun AFND . . . . . . . . . . . . . . . .
2.7.2 Reconnaissance dun mot par un AFND . . . . . . .
2.8 Conversion dune ER en un AFND . . . . . . . . . . . . . .
2.8.1 AFND normalise . . . . . . . . . . . . . . . . . . .
2.8.2 Lalgorithme de Thompson . . . . . . . . . . . . . .
2.9 Conversion dun AFND en un AFD . . . . . . . . . . . . .
2.9.1 -cloture . . . . . . . . . . . . . . . . . . . . . . .
2.9.2 Algorithme de calcul de la -cloture . . . . . . . . .
2.9.3 Algorithme de determinisation dun AFND . . . . .
2.10 Minimisation dun AFD . . . . . . . . . . . . . . . . . . .
2.10.1 Simplification dun AFD . . . . . . . . . . . . . . .

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.8.2 Elimination de la recursivite a` gauche . . . . . .


5.8.3 Factorisation a` gauche . . . . . . . . . . . . . .
5.9 Precedence des operateurs . . . . . . . . . . . . . . . .
5.9.1 Associativite des operateurs . . . . . . . . . . .

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

6.4.1 Le predicat Nullable . . . . . . . . . . . . . . . . . . . .


6.4.2 La fonction Premier (First) . . . . . . . . . . . . . . . . .
6.4.3 La fonction Suivant (Follow) . . . . . . . . . . . . . . . .
Analyse LL(1) . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.5.1 Grammaires LL(1) . . . . . . . . . . . . . . . . . . . . .
6.5.2 Implementation dun analyseur LL(1) . . . . . . . . . . .
Analyseur syntaxique LL(1) par descente recursive . . . . . . . .
6.6.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6.2 Un exemple . . . . . . . . . . . . . . . . . . . . . . . . .
Analyseur syntaxique LL(1) dirige par une table . . . . . . . . . .
6.7.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.7.2 ALgorithme danalyse syntaxique LL(1) dirigee par table .
Preparation dune grammaire pour une analyse LL(1) . . . . . . .

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

Table des figures


1.1
1.2
1.3
1.4

Schema general dun compilateur. . . . . . . . . . . .


Division classique dun compilateur. . . . . . . . . . .
Schema general dun compilateur avec ses e tapes. . . .
Schema general de lenvironnement dun compilateur. .

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

Representation graphique dun AFD. . . . . . . . . . . . . . . . . 23


Un AFD reconnaissant le langage a ba . . . . . . . . . . . . . . . 23
LAFD complet e quivalent a` lAFD de la figure 2.2. . . . . . . . 25
Algorithme de reconnaissance dun mot par un AFD. . . . . . . . 27
Exemple dun AFND. . . . . . . . . . . . . . . . . . . . . . . . . 29
Automate reconnassant . . . . . . . . . . . . . . . . . . . . . . 31
Automate reconnassant . . . . . . . . . . . . . . . . . . . . . . 31
Automate reconnassant un symbole a A. . . . . . . . . . . . . 31
Automate reconnassant la reunion (r|s). . . . . . . . . . . . . . . 32
Automate reconnassant la concatenation (rs). . . . . . . . . . . . 32
Automate reconnassant letoile (r) . . . . . . . . . . . . . . . . . 32
Automate reconnaissant lexpresion a ba . . . . . . . . . . . . . . 33
Algorithme de calcul de la b-cloture dun ensemble detats T . . . . 34
Algorithme de determinisation dun AFND. . . . . . . . . . . . . 35
LAFD obtenu par determinisation de lAFND de la figure 2.5. . . 36
Un AFD a` simplifier. . . . . . . . . . . . . . . . . . . . . . . . . 37
Suppression de letat non accessibles 7. . . . . . . . . . . . . . . 37
Un AFD a` simplifie. . . . . . . . . . . . . . . . . . . . . . . . . 37
Pseudo-code le lalgorithme de minimisation de Hopcroft & Ullman. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Un AFD simplifie a` minimiser. . . . . . . . . . . . . . . . . . . . 40
LAFD minimal. . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Lautomate de la figure 2.5, augmente de deux e tats et . . . . . 43
Lautomate precedent, apr`es suppression de letat 2. . . . . . . . . 44
Lautomate precedent, apr`es suppression des deux transitions (1, a+ b, 3)
et (1, b, 3). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

2.20
2.21
2.22
2.23
2.24

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

2
4
5
6

viii

TABLE DES FIGURES


2.25 Lautomate precedent, apr`es suppression de letat 3. . . . . . . . .
2.26 Lautomate precedent, apr`es suppression des deux transitions (1, a+ ,
et (1, a b, ). . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.27 Lautomate compl`etement reduit. . . . . . . . . . . . . . . . . . .
2.28 Automate minimal et lemme de pompage. . . . . . . . . . . . . .

44
)
44
45
47

3.1
3.2

51

3.3
3.4
3.5

Relation entre lanalyseur lexical et lanalyseur syntaxique. . . . .


Implementation dun anlyseur lexical e crit a` la main (en langage
C). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Pseudo-code de lanlyseur lexical. . . . . . . . . . . . . . . . . .
Un AFD qui reconnat le fragment du langage e tudie. . . . . . . .
Pseudo-code du simulateur de lAFD de lanlyseur lexical. . . . .

4.1
4.2
4.3
4.4
4.5

Creation dun analyseur lexical avec Flex. . . . . . .


Programme de simulation de lAFD genere par Flex.

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

Position de lanalyseur syntaxique. . . . . . . . . . . .


Premi`ere derivation du mot w = accbbadbc. . . . . . .
Deuxi`eme derivation du mot w = accbbadbc. . . . . .
Arbre de derivation totale du mot w = accbbadbc. . . .
Premi`ere derivation du mot w = acb. . . . . . . . . . .
Fonction exiger. . . . . . . . . . . . . . . . . . . . . .
Procedure associee au symbole T 0 . . . . . . . . . . . .
Procedure associee au symbole T . . . . . . . . . . . .
Procedure associee au symbole R. . . . . . . . . . . .
Procedure danalyse syntaxique. . . . . . . . . . . . .
Algorithme de remplissage de la table danalyse LL(1).
Les e lements pour un analyseur LL(1) dirige par table.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

84
85
85
86
86
93
94
94
95
95
97
97

7.1

Creation dun analyseur syntaxique avec Bison. . . . . . . . . . . 101

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

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

Les langages de haut-niveau sont appropries aux programmeurs humains et se


caracterisent par une tr`es grande difference du langage machine que lordinateur
sait executer. Ainsi, des moyens pour combler le fosse entre les langages de hautniveau et le langage machine sont necessaires. Cest l`a o`u le compilateur entre en
jeu.

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

La figure 1.1 illustre le schema general dun compilateur :

F IG . 1.1 Schema general dun compilateur.


Le programme de depart sappele programme source et le programme darrive
sappele programme cible.
Dordinaire, L est un langage de programmation de haut-niveau (C ou Java par
exemple) et L0 est un langage de bas-niveau (par exemple un code machine adapte
a` un ordinateur particulier).
Exemple 1.1.1
gcc : compilateur de C vers de lassembleur.
javac : compilateur de Java vers du byte code.
latex2html : compilateur de Latex vers du P ostcript ou du HT M L.

1.1.2

Avantages dun compilateur

Lutilisation dun compilateur permet de gagner plusieurs avantages :


. Il permet de programmer avec un niveau superieur dabstraction plutot quen
assembleur ou en langage machine.
. Il permet de faciliter la tache de la programmation en permettant decrire
des programmes lisibles, modulaires, maintenables et reutilisables.
. Il permet de seloigner de larchitecture de la machine et donc decrire des
programmes portables.

1.2. Les phases dun compilateur

. Il permet de detecter des erreurs et donc decrire des programmes plus


fiables.

1.1.3

Les principales taches dun compilateur

. Lire et analyser le programme source.


. Detecter des erreurs (lexicales, syntaxiques et semantiques).

. 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.

1.2 Les phases dun compilateur


Traditionellement, un compilateur se decoupe en trois grandes parties :
. Le front-end (ou partie frontale) qui se compose de trois phases danalyse qui sont : lanalyse lexicale, lanalyse syntaxique et lanalyse semantique.
. Le coeur (ou partie mediane) qui travaille sur une representation intermediaire du programme, independante a` la fois du langage source et
du langage cible.
. Le back-end (ou partie arri`ere) qui contient trois phases de synth`ese
qui sont : la production du code intermediaire, loptimisation du code intermediaire et la production du code cible.
La figure 1.2 montre le schema global dun compilateur travaillant avec cette division en trois parties. Lavantage de ce decoupage est la reutilisabilite du coeur a` la
fois pour differents langages sources et differents langages cibles. Le role fondamental du coeur est la production dune representation intermediaire entre le code
source et le code cible. Cette representation doit e tre plus facile a` construire et a`
manipuler, mais aussi plus facile a` traduire.

Introduction a` la compilation

F IG . 1.2 Division classique dun compilateur.

1.2.1

Le front-end

Il comprend trois e tapes danalyse qui sont :


. Lanalyse lexicale : elle prend en entree le texte du programme source et
fournit la liste de lex`emes (jetons ou tokens), avec pour chaque lex`eme
son unite lexicale a` laquelle il appartient (en plus dautres informations).
Le texte est lu caract`ere par caract`ere et divise en lex`emes, qui correspond
chacun a` un mot valide dans le langage de programmation source. Un motcle, le nom dune variable, une constante numerique sont des lex`emes.
. Lanalyse syntaxique : elle prend la liste des lex`emes produite par lanalyse
lexicale et regroupe ces derniers en une structure darbre (appelee larbre
de syntaxe) qui refl`ete la structure grammaticale du programme source.
. Lanalyse semantique : elle prend en entree larbre de syntaxe pour determiner
si le programme source viole certaines r`egles de coherence. Par exemple, si
une variable est utilisee mais non declaree ou si elle est utilisee dans un
contexte nayant pas un sens pour le type de la variable, comme laddition
dun nombre avec une chane de caract`eres (valable en Javascipt, mais non
valable en C).

1.2.2

Le back-end

Il comprend trois e tapes de synth`ese qui sont :


. La production du code intermediaire : le programme source est traduit en
une representation intermediaire simple qui est independante de la machine.
. Loptimisation du code intermediaire : le code intermediaire produit est

1.2. Les phases dun compilateur

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 :

F IG . 1.3 Schema general dun compilateur avec ses e tapes.

1.2.3 Les differents modules


Le processus de compilation necessite dautres modules supplementaires :
. Le gestionnaire de la table des symboles : une table de symbole est une
table qui stocke les informations necessaires sur les lex`emes. Le gestionnaire de la table des symboles est un programme qui g`ere cette table (construction
de la table, insertions et recherches).
. Le gestionnaire des erreurs : il sagit du module responsable de la detection
et de la signalisation des erreurs rencontrees dans le programme source.

Introduction a` la compilation

. Le pre-processeur : certains langages (comme C) ont besoin dun module


qui effectue un pre-traitement du texte source avant de declencher le processus de compilation. Le plus souvent la tache de ce module consiste a` faire
des recherche/remplacement de texte ou dinclusion de certains fichiers.
. Lassemblage et ledition de lien : le code en langage dassemblage est traduit en representation binaire et les addresses des variables et des fonctions
sont fixees. Ces deux e tapes sont typiquement effectues par des programmes
fournis par la machine ou par le constructeur du syst`eme dexploitation, et
ne font pas partie du compilateur lui-meme.
. Le chargeur : cest un programme qui charge le code machine absolue dans
la memoire principale (la RAM) de lordinateur.
La figure 1.4 resume lenvironnement dun compilateur :

F IG . 1.4 Schema general de lenvironnement dun compilateur.

1.3

Les interpreteurs

Linterpretation constitue une autre solution pour implementer un langage


de programmation. Un interpreteur, au lieu de produire du code cible a` partir de
larbre de syntaxe, effectue lui-meme les operations specifiees dans le code source.
En fait, larbre de syntaxe sera traite directement pour e valuer les expressions et
executer les instructions. Un interpreteur aura besoin de traiter la meme pi`ece
de larbre de syntaxe plusieurs fois (par exemple, le corps dune boucle). Par

1.4. Caracteristiques dun bon compilateur

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

Caracteristiques dun bon compilateur

Voici les caracteristiques dun bon compilateur :


. Il doit generer un code objet optimise en temps et en espace.
. Le temps de compilation est tr`es rapide.
. Il doit dectecter toutes les erreurs lexicales et syntaxiques.
. Il doit detecter le maximum des erreurs semantiques.
. Le nombre de passes de compilation est tr`es reduit.
. Les messages derreurs sont clairs et precis.

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

Expressions reguli`eres et automates finis

Par exemple, 11010 est un mot sur lalphabet A = {0, 1} de longueur 5.


Definition 2.1.4 Sur tout alphabet A, on peut definir un mot special : le mot vide
note , comme e tant le mot de longueur 0 (|| = 0).

2.1.3

Langages

Definition 2.1.5 Un langage L sur un alphabet A est un ensemble (fini ou infini)


de mots sur A.
Par exemple, le langage des nombres binaires pairs et de longueur 3 est forme par
les mots suivants : 000, 010, 100, 110.
Remarque 2.1.1 Selon ces definitions, une unite lexicale est un langage sur un
alphabet particulier. Dautre part, un alphabet A est considere comme un langage
sur A (les symboles sont des mots de longueur 1).
Definition 2.1.6 Soit A un alphabet. Le langage de tous les mots sur A est e galement
un langage sur A. Cest le langage universel sur A, note A .
Remarque 2.1.2 Il decoule de cette definition :
. Une chane w est un mot sur un alphabet A, si et seulement si w A .
. Un ensemble L est un langage sur un alphabet A, si et seulement si L A .

2.1.4

Operations sur les mots

Sur les mots, on peut definir plusieurs operations :

. 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

2.1. Langages formels

11

de n copies de w. Par exemple, (101)3 = 101101101. Une definition recursive


de la puissance n-`eme dun mot w est la suivante :
(

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

Parties dun mot

Soit w un mot sur un alphabet A.


. Prefixe : un mot u est un prefixe (ou facteur gauche) de w, si et seulement
si, w = uv, pour un certain mot v. Autrement dit, le mot w commence par u.
. Suffixe : un mot v est un suffixe (ou facteur droit) de w, si et seulement
sil existe un mot u tel que w = uv. Autrement dit, le mot w se termine par
v.

12

Expressions reguli`eres et automates finis


. Facteur : un mot u est un facteur de w, si et seulement sil existe deux mots
et tels que w = u. Autrement dit, le mot u apparat dans w.
. Sous-mot : un mot w0 est un sous-mot de w, si et seulement si, w0 peut e tre
obtenu en supprimant un certain nombre (eventuellement 0) de symboles
de w.
. Prefixe, (Suffixe, Facteur et Sous-mot) propre : un prefixe w0 (resp. suffixe, facteur et sous-mot) dun mot w est dit propre, si w0 6= et w0 6= w.

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

Operations sur les langages

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

2.1. Langages formels

13

Dune mani`ere informelle, un mot w Ln est obtenu par la concatenation


de n mots de L.
. Fermeture de Kleene. La fermeture de Kleene (ou letoile) dun langage
L sur A, notee L , est le langage obtenu par la reunion de toutes les puissances de L :

[
L =
Li = L0 L1 L2 ...
i=0

Dune mani`ere informelle, un mot w L est obtenu par la concatenation


dun nombre fini quelconque, e ventuellemnt nul, de mots de L.
. Fermeture positive. La fermeture de Kleene positive dun langage L sur
A, notee L+ , est le langage obtenu par la reunion de toutes les puissances
non nulles de L :
+

L =

Li = L1 L2 L3 ...

i=1

Dune mani`ere informelle, un mot de w L+ est obtenu par la concatenation


dun nombre fini quelconque non nul de mots de L.
e est le langage forme par
. Miroir. Le miroir dun langage L sur A, note L,
tous les miroirs de ses mots :
e = {w
L
e : w L}
Voici quelques proprietes de ces operations (L, M, K e tant des langages sur un
alphabet A, et n, m des entiers) :
1. L(M K) = (LM )K = LM K : la concatenation des langages est associative.
2. L{} = {}L = L : le langage reduit au mot vide {} est neutre pour la
concatenation des langages.
3. L = L = .
4. L(M K) = (LM )(LK) et (LM )K = (LK)(M K) : la concatenation
est distributive par rapport a` la reunion.
5. L(M K) (LM ) (LK) et (L M )K (LK) (M K).
6. Si K M , alors LK LM et KL M L.
7. Ln+m = Ln Lm .
8. (Ln )m = Lnm .

14

Expressions reguli`eres et automates finis

(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.

Remarque 2.1.4 La concatenation des langages nest pas commutative.


Les operations de reunion, de concatenation et de fermeture sont dites operations
reguli`eres, car elles servent a` e crire ce quon appelle des expressions reguli`eres :
La reunion exprime le choix.
La concatenation exprime la sequence.
La fermeture de Kleene exprime la repetition.
La fermeture de Kleene positive exprime la repetition non nulle.
Par exemple, soit le langage L des mots sur lalphabet A = {a, b, c} qui commencent par a ou b, suivis de 0 ou plusieurs occurrences de cc et se termine par
au moins un b. Il est clair que L = L1 L2 L3 , o`u L1 = {a, b}, L2 = {cc} et L3 =
{b}+ . Finalement, L = {a, b}({cc} ){b}+ , qui sera simplifie a` (a|b)(cc) b+ .

2.2

Les expressions reguli`eres

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.

2.2. Les expressions reguli`eres

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

Lorsquon combine plusieurs symboles de construction dans une expression


reguli`ere, comme par exemple dans a|ab , il nest pas clair comment les sousexpressions reguli`eres sont regroupees. On peut alors utiliser les parenth`eses (...)
pour fixer un regroupement de symboles, par exemple (a|(ab)) . Pour simplifier
lecriture des expressions reguli`eres, on adopte les r`egles de priorite suivantes :
1. loperateur de fermeture a la plus haute priorite.
2. loperateur de concatenation a la deuxi`eme plus haute priorite.
3. loperateur de reunion | a la plus faible priorite.

16

Expressions reguli`eres et automates finis

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

Les notations abregees

Il est utile dutiliser certains raccourcis par convention :


intervalle de symboles : nous e crivons par exemple [09] au lieu de 0 | 1 | 2
| 3 | 4 | 5 | 6 | 7 | 8 | 9. De meme, lensemble des lettres minuscules est denote
par [a z]. Les entiers positifs sont decrits par lexpression reguli`ere tr`es
courte, a` savoir [0 9][0 9] .
la repetition non nulle : nous e crivnos s+ au lieu de ss = s s. Avec cette
notation, on peut simplifier la description de lensemble des entiers positifs
a` [0 9]+ .
lalternative (0 ou une occurrence de) : nous e crivnos s? au lieu de (s | ).
Les operateurs + et ? ont la meme priorite que loperateur .

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]+ .

2.2. Les expressions reguli`eres

17

2.2.5 Quelques proprietes algebriques


Definition 2.2.2 Deux expressions reguli`eres r et s, sur un meme alphabet A,
sont dites e quivalentes, et lon e crit r = s, si elles denotent le meme langage,
cest-`a-dire si L(r) = L(s).
Par exemple, il est clair que (a|b) = (b|a) . Voici quelques proprietes algebriques
des expressions reguli`eres :
1. (r|s)|t = r|(s|t) = r|s|t : la reunion est associative.
2. r|s = s|r : la reunion est commutative.
3. r|r = r : la reunion est idempotent.
4. r? = r| : par definition de loperateur ?.
5. (rs)t = r(st) = rst : la concatenation est associative.
6. r = r = r : lelement est neutre pour la concatenation.
7. r(s|t) = (rs)|(rt) : la concatenation est distribituve a` droite par rapport a` la
reunion.
8. (r|s)t = (rt)|(st) : la concatenation est distribituve a` gauche par rapport a`
la reunion.
9. (r ) = r : letoile est idempotent.
10. r r = r .
11. rr = r r = r+ : par definition de loperateur +.

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

Expressions reguli`eres et automates finis

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 }.

Pour distinguer un nom di dune expression reguli`ere ri , on e crire le nom en


gras et lexpression reguli`ere en italique. Comme exemple, donnons une definition
recursive dun identificateur P ascal :
lettre [A Za z]
chiffre [0 9]
id lettre(lettre|chiffre)

2.3

Expressions reguli`eres en Flex

Flex est un compilateur qui gen`ere automatiquement des analyseurs lexicaux


en C (voir chapitre 4). Il utilise des expressions reguli`eres pour specifier des
unites lexicales. Flex offre un ensemble plus e tendu doperateurs (autre que la
concatenation, la reunion et letoile) pour decrire des expressions reguli`eres. Voici
la description de la majorite de ces operateurs :

2.3. Expressions reguli`eres en Flex


Expression Reguli`ere
x ou x
r1 r 2
r
r+
r?
[xyz]
[c f ]

.
[ ]
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

Les caract`eres dechapements \ et ... sont utilises pour designer un operateur


de Flex (si ce dernier est un caract`ere dans le lex`eme).
Les caract`eres \, et $ ne peuvent pas apparatre dans des ( ), ni dans
des definitions reguli`eres.
` linterieur de [ ], loperateur \ garde sa signification ainsi que loperateur
A
sil nest pas au debut ou a` la fin.
Lorsque loperateur est e crit au milieu dun [ ], il signifie un intervalle.

20

Expressions reguli`eres et automates finis


Le caract`ere de fin de fichier (EOF en C) est designe en Flex par lexpression reguli`ere << EOF >>.

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) .

Le resultat suivant decoule immediatement des proprietes des expressions reguli`ers :


Theor`eme 2.4.1 Soit A un alphabet quelconque.
1. et {} sont reguliers.
2. a A, le langage {a} est regulier.
3. Tout le langage reduit a` un seul mot est regulier.
4. Tout langage fini est regulier.
5. Lalphabet A est un langage regulier.

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 :

2.5. Les automates finis

21

1. Reunion : si L et K sont reguliers, alors L K est regulier.


2. Concatenation : si L et K sont reguliers, alors LK est regulier.
3. Etoile : si L est regulier, alors L est regulier.
Ce resultat decoule aussi des proprietes sur les expressions reguli`eres. Nous pouvons en deduire la propriete suivante :
Theor`eme 2.4.3 Sur un alphabet A, le langage universel A est regulier.
Remarque 2.4.1 On montre e galement que lensemble des langages reguliers sur
A est clos par les operations suivantes :
1. Intersection : si L et K sont reguliers, alors L K est regulier.
2. Difference : si L et K sont reguliers, alors L \ K est regulier.
e est regulier.
3. Miroir : si L est regulier, alors L
4. Prefixe : si L est regulier, alors P ref (L) est regulier.
5. Suffixe : si L est regulier, alors Suf f (L) est regulier.
6. Facteur : si L est regulier, alors F act(L) est regulier.

2.5 Les automates finis


Les expressions reguli`eres constituent un formalisme algebrique pour specifier
les langages reguliers. Dans cette section, nous allons introduire un formalisme
graphique qui nous permet de resoudre la question de la reconnaissance de ces
langages.
Definition 2.5.1 En general, un reconnaisseur pour un langage L sur un alphabet A est un programme qui prend en entree une chane w A et repond oui
si w L et non si w 6 L.
Les automates finis constituent un outil formel tr`es interessant de la theorie des
langages. Nous allons voir comment transformer une expression reguli`ere en un
reconnaisseur en construisant un automate fini. Il y a deux grandes categories
dautomates finis : les AFD (automates finis deterministes) et les AFND (automates
finis non-deterministes).
Les AFD et les AFND sont capables de reconnatre precisement les langages
reguliers. Alors que les AFD produisent des reconnaisseurs plus rapides que les
AFND, un AFD est generalement beaucoup volumineux quun AFND e quivalent.

22

Expressions reguli`eres et automates finis

2.6
2.6.1

Les automates finis deterministes


Definition

Definition 2.6.1 Un automate fini deterministe (AFD), est un objet mathematique


forme dun 5-uplet M = (E, A, , q0 , F ) o`u :
E est un ensemble fini detats.
A est lalphabet dentree.
est la fonction de transition definie de E A vers E.
q0 E est letat initial.
F E est lensemble des e tats finaux.
Un AFD dans le sens abstrait, est une machine qui poss`ede un nombre fini
detats et un nombre fini de transitions entre ces e tats. Une transition entre
deux e tats est e tiquette par un symbole de lalphabet dentree. En theorie des
langages, un AFD peut e tre utilise pour decider si un mot w appartient a` un lan`
gage donne L. Pour faire ceci, nous partons de letat initial q0 de lautoamte. A
chaque e tape, on lit un symbole de w a` partir de lentree, puis on suit une transition e tiquettee par ce symbole et on change letat courant. Il y a deux cas qui
peuvent se presenter :
. Tous les symboles de lentree ont e te lus. Nous testons alors si le dernier
e tat est un e tat final. Si oui, le mot w L, sinon w 6 L.
. On se bloque sur un symbole particulier car il ny a pas de transition avec
ce symbole. Dans ce cas, w 6 L.

2.6.2

Representation dun AFD

Un AFD est represente par un graphe oriente e tiquette :


. Les e tats sont les sommets du graphe et sont representes par des petits
circles portant des numeros (ou des noms) qui les identifient. Cependant,
ces numeros ou noms nont aucune signification operationnelle.

2.6. Les automates finis deterministes

23

. Un e tat final est represente par deux cercles concentriques.


. Letat initial q0 est marque par une fl`eche qui pointe sur son cercle.
. Les transitions representent les arcs du graphe. Une transition est denotee
par une fl`eche reliant deux e tats. Cette fl`eche porte une e tiquette qui est un
symbole de lalphabet.

F IG . 2.1 Representation graphique dun AFD.


Prenons lexemple de lexpression reguli`ere a ba . Elle denote le langage sur lalphabet {a, b} des mots ayant exactement une seule occurrence de b. Ce langage
est reconnu par lAFD suivant :

F IG . 2.2 Un AFD reconnaissant le langage a ba .


LAFD de lexemple est defini formellement par les e lements suivants :
E = {0, 1}.
A = {a, b}.

24

Expressions reguli`eres et automates finis


Trois transitions : (0, a) = 0 (cest une boucle), (0, b) = 1 et (q1 , a) =
1 (une autre boucle).
Letat initial q0 = 0.
F = {1}.

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

Le mot baa appartient au langage a ba , en effet :


On part de letat initial q0 = 0. On lit le premier symbole de lentree, a`
savoir b. Il y a une transition de letat 0 avec le symbole b, on la suit et
on passe a` letat 1.
On lit le second symbole de lentree, a` savoir a. Il y a une transition de
letat 1 avec le symbole a, on la suit et on reset a` letat 1.
On lit le troisi`eme symbole de lentree, a` savoir a. Il y a une transition de
letat 1 avec le symbole a, on la suit et on reset a` letat 1. On a termine
la lecture de tous les symboles de lentree, on teste le dernier e tat. Comme
1 F , alors le mot baa a ba .
Le mot aa nappartient pas au langage a ba , en effet :
On part de letat initial q0 = 0. On lit le premier symbole de lentree, a`
savoir a. Il y a une transition de letat 0 avec le symbole a, on la suit et
on reste a` letat 0.
On lit le second symbole de lentree, a` savoir a. Il y a une transition de
letat 0 avec le symbole a, on la suit et on reset a` letat 0. On a termine

2.6. Les automates finis deterministes

25

la lecture de tous les symboles de lentree, on teste le dernier e tat. Comme


0 6 F , alors le mot aa 6 a ba .
Le mot bba nappartient pas au langage a ba , en effet :
On part de letat initial q0 = 0. On lit le premier symbole de lentree, a`
savoir b. Il y a une transition de letat 0 avec le symbole b, on la suit et
on passe a` letat 1.
On lit le deuxi`eme symbole de lentree, a` savoir b. Il ny a pas de transition
de letat 1 avec le symbole b, on est bloque sur le troisi`eme symbole et le
mot bba 6 a ba .
Remarque 2.6.1
La fonction de transition dun AFD est partielle, cest-`a-dire que (q, a) nest
pas toujours defini pour tout e tat q et tout symbole a. Cependant, il est possible
de la rendre totale par le procede suivant :
1. On ajoute un nouveau e tat a` E (un e tat artificiel).
2. On met (q, a) = pour tout e tat q et tout symbole a pour lesquels (q, a)
est non defini.
3. On ajoute les transitions (, a) = pour tout symbole a.
LAFD obtenu par cette transformation est e quivalent a` lAFD initial, en ce sens
quils reconnaissent le meme langage. Un tel AFD est dit complet.
` titre dexemple, voici lAFD complet e quivalent a` lAFD de la figure 2.2 :
A

F IG . 2.3 LAFD complet e quivalent a` lAFD de la figure 2.2.

2.6.3 Reconnaissance dun mot par un AFD


Definition 2.6.2 Par definition, un mot w est reconnu par un AFD M = (E, A, ,
q0 , F ), sil existe un chemin C e tiquette par w et allant de letat initial q0 vers un
e tat final q F de M . Formellement, un mot w = a1 ...an est reconnu par lAFD

26

Expressions reguli`eres et automates finis

M , sil existe n transitions de la forme suivante : (q0 , a1 ) = q1 , (q1 , a2 ) = q2 ,


..., (qn1 , an ) = qn et qn F .
Exemple 2.6.1 Les mots b, ab et aabaaa sont reconus par lAFD de la figure 2.3.
Une autre definition e quivalente de la reconnaissance dun mot par un AFD est
basee sur la fonction de la transition iteree notee .
Definition 2.6.3 La fonction de transition iteree dun AFD complet est la fonction totale definie de E A vers E par :
(q, ) = q
(q, aw) = ((q, a), w)
o`u q E est un e tat, a A est un symbole et w A un mot.
Exemple 2.6.2 Dans lAFD de la figure 2.3, on a : (0, baa) = 1 et (0, aa) =
0, mais (0, abba) = .
Definition 2.6.4 Un mot w est reconnu par un AFD M = (E, A, , q0 , F ), si et
seulement si, (q0 , w) F .
Definition 2.6.5 Le langage reconnu par un AFD M , note L(M ) = (E, A, ,
q0 , F ), est le langage forme par tous les mots reconnus par M . Formellement :
L(M ) = {w A | (q0 , w) F }
Exemple 2.6.3 Le langage reconnu par lAFD de la figure 2.3 est a ba .
La preuve est e vidente si lon utilise la formule donnee par la proposition suivante :
Proposition 2.6.1 Dans un AFD complet M = (E, A, , q0 , F ), on a :
(q, uv) = ( (q, u), v)
pour tout mots u et v de A , et tout e tat q E.
Remarque 2.6.2 Dans un AFD, le chemin de reconnaissance dun mot est unique.

2.7. Les automates finis non-deterministes : AFND

27

F IG . 2.4 Algorithme de reconnaissance dun mot par un AFD.

2.6.4 Algorithme de reconnaissance dun mot par un AFD


Lalgorithme de reconnaissance dun mot par un AFD est plus simple et plus efficace (son temps dexecution est lineaire en la longueur du mot). Nous presentons
ici le pseudo-code de cet algorithme. Ce dernier prends un mot w A et un AFD
M = (E, A, , q0 , F ) et retourne 1 si w L(M ) et 0 sinon.
Notre objectif est de transformer une expression reguli`ere en un reconnaisseur.
Il suffit alors de trouver un automate fini a` partir dune telle expresssion reguli`ere.
Malheureusement, lobtention dun AFD a` partir dune expession reguli`ere nest
pas toujours garantie et nest pas automatique. Lutilisation des AFND a e te choisie car il est plus simple (et meme automatique) de construire un AFND a` partir
dune expression reguli`ere (ER) que de construire un AFD. Les AFND constituent alors un bon intermediaire entre les ER et les AFD. Ce qui est rassurant,
cest que le passage dun AFND a` un AFD est e galement automatique et plus
simple en pratique (meme sil est theoriquement exponnentiel).

2.7

Les automates finis non-deterministes : AFND

Les automates finis non-deterministes (AFND) constituent un outil formel


efficace pour transformer des expressions reguli`eres en programmes efficaces de
reconnaissance de langages. Par leur nature non-deterministe, ils sont plus compliques que les AFD.

28

Expressions reguli`eres et automates finis

2.7.1

Definition dun AFND

La nature non-deterministre dun AFND provient des points suivants :


. Il peut posseder plusieurs e tats initiaux.
. Il peut y avoir plusieurs tarnsitions a` partir dun meme e tat avec le meme
symbole de lentree.
. Il peut y avoir des tarnsitions spontanees (cest-`a-dire sans lire aucun symbole dentree) dun e tat vers un autre.

Definition 2.7.1 Une -transition (transition spontanee, ou transition immediate)


est le passage dun e tat q vers un autre e tat q 0 sans lire aucun symbole. Dans ce
cas, on dit que lautomate a lit le mot vide .
Par sa nature, le chemin de reconnaissance dun mot par un AFND nest pas
unique. Voici la definition formelle dun AFND :
Definition 2.7.2 Un AFND est un objet mathematique forme dun 5-uplet M =
(E, A, , Q0 , F ) o`u :
. E est un ensemble fini detats.
. A est lalphabet dentree.
. Q0 E est lensemble des e tats initiaux.
. est la fonction de transition definie de E (A {}) vers (E) ((E)
designe lensemble des parties de E).
. F E est lensemble des e tats finaux.
La fonction de transition associe a` un couple forme dun e tat de depart q E et
un symbole dentree a A (ou ), un sous-ensemble detats darrive (q, a) E.
Un AFND se represente de la meme facon quun AFD, sauf que les -transitions
sont e tiquettees par le mot vide . La figure suivante montre un exemple dun
AFND :
Formellement, cet AFND est defini par les e lements suivants :

2.7. Les automates finis non-deterministes : AFND

29

F IG . 2.5 Exemple dun AFND.


E = {1, 2, 3}.
A = {a, b}.
Q0 = {1}.
La matrice de transition :

1
2
3

b
{3}

{2}

{1, 3}

F = {3}.

2.7.2 Reconnaissance dun mot par un AFND


Definition 2.7.3 Soit M = (E, A, , Q0 , F ) un AFND et w A un mot sur
A. On dit que M reconnat (ou acc`epte) le mot w, sil existe un chemin dans M
allant dun e tat initial q0 Q0 vers lun des e tats finaux de M qui correspond
a` la sequence des symboles du mot w. Plus formellement, il existe une sequence
detats e1 , e2 , ..., en+1 et une sequence de symboles a1 , a2 , ..., an telles que :
1. e1 = q0 Q0 .
2. ej+1 (ei , ai ), pour i = 1, 2, ..., n.
3. a1 a2 ...an = w.
4. en+1 F .

Par exemple, lAFND de la figure 2.5 reconnat le mot aab :

30

Expressions reguli`eres et automates finis


On part de letat initial 1. On lit le symbole et on passe a` letat 2.
On lit le symbole a et on passe a` letat 1.
On lit le symbole et on passe a` letat 2.
On lit le symbole a et on passe a` letat 1.
On lit le symbole b et on passo a` letat 3.
On a lit tous les symboles du mot et le dernier e tat est 3 F .

Par contre, le meme automate nacc`epte pas le mot bb.


Definition 2.7.4 Soit M = (E, A, , Q0 , F ) un AFND. Le langage reconnu
(ou accepte) par M , note L(M ), est le langage forme par tous les mots reconnus
par M .
Exemple 2.7.1 Le langage reconnu par lautomate de la figure 2.5 est a (a|b).
Un programme qui decide si un mot est accepte par un AFND donne, doit verifier
tous les chemins possibles pour tester lacceptation du mot. Cela necessite alors
deffectuer des retours en arri`ere (backtracking) jusqu`a trouver un chemin favorable. Comme un algorithme qui fait des retours en arri`ere est le plus souvent non
efficace, les AFND sont des mauvais reconnaisseurs.

2.8

Conversion dune ER en un AFND

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.

2.8. Conversion dune ER en un AFND

31

4. Aucune transition ne sorte de letat final.


5. Tout e tat, est soit lorigine dexactement une transition e tiquettee par un
symbole de lalphabet, soit lorigine dau plus deux transitions, e tiquettees
par le mot vide .

2.8.2 Lalgorithme de Thompson


Lalgorithme est recursif : on decompose lexpression reguli`ere en sous-expressions
e lementaires avec les operations reguli`eres, puis on applique recursivement le
procede suivant :
1. Pour lexpression reguli`ere r = , construire lAFND :

F IG . 2.6 Automate reconnassant .


2. Pour lexpression reguli`ere r = , construire lAFND :

F IG . 2.7 Automate reconnassant .


3. Pour lexpression reguli`ere r = a A, construire lAFND :

F IG . 2.8 Automate reconnassant un symbole a A.


4. Supposons que M (r) et M (s) soient les AFND obtenus par lalgorithme
de Thompson pour les expressions reguli`eres r et s, respectivement.
(a) Pour lexpression reguli`ere (r|s), construire lAFND :
(b) Pour lexpression reguli`ere (rs), construire lAFND :
(c) Pour lexpression reguli`ere (r) , construire lAFND :

32

Expressions reguli`eres et automates finis

F IG . 2.9 Automate reconnassant la reunion (r|s).

F IG . 2.10 Automate reconnassant la concatenation (rs).

F IG . 2.11 Automate reconnassant letoile (r) .


Theoriquement, on montre que le nombre des e tats de lAFND obtenu ne depasse
pas le double de la taille de lexpression reguli`ere. Cette taille, notee k ... k, est
calculee recursivement de la mani`ere suivante :
1. k k =k k = 0.
2. k a k = 1, a A.
3. k (r|s) k =k r k + k s k +1.
4. k (rs) k =k r k + k s k +1.
5. k (r) k =k r k +1.
Exemple 2.8.1 La figure suivante montre lAFND obtenu par lapplication de
lalgorithme de Thompson a` lexpression reguli`ere a ba .

2.9. Conversion dun AFND en un AFD

33

F IG . 2.12 Automate reconnaissant lexpresion a ba .

2.9

Conversion dun AFND en un AFD

La transformation est faite en utilisant une technique de regroupement. Lidee


generale est que chaque e tat dans lAFD correspond a` un ensemble detats dans
lAFND. LAFD utilise un e tat pour garder trace de tous les e tats possibles que
lAFND peut atteindre apr`es avoir lu chaque symbole de lentree. Ceci revient a`
dire que, apr`es avoir lu le mot a1 a2 ...an , lAFD est dans un e tat qui represente
le sous-ensemble T des e tats de lAFND accessibles a` partir de letat de depart
de lAFND en suivant le chemin a1 a2 ...an . Le nombre detats de lAFD peut e tre
exponentiel par rapport au nombre detats de lAFND, mais en pratique, ce cas le
plus defavorable apparat rerement.

2.9.1

-cloture

Les -transitions compliquent un peu cette construction : chaque fois quon


a un e tat de lAFND, on peut toujours choisir de suivre une -transition. Ainsi,
e tant donne un symbole, un e tat darrive peut e tre trouve, soit en suivant une transition e tiquettee avec ce symbole, soit en faisant un certain nombre d-transitions
puis une transition avec le symbole. On peut resoudre ce cas de figure dans la
construction, en e tendant en premier lieu lensemble des e tats de lAFND avec
ceux accessibles a` partir de ces e tats en utilisant uniquement des -transitions.
Ainsi, pour chaque symbole de lentree possible, on suit les transitions avec ce
symbole pour former un nouveau sous-ensemble detats de lAFND.
On definit l-cloture (ou l-fermeture) dun sous-ensemble detats T , note
b(T ), comme e tant la reunion de T et de lensemble de tous les e tats accessibles depuis lun des e tats de T en utilisant uniquement un certain nombre dtransitions. Formellement :
Definition 2.9.1 Soit T E, o`u E est lensemble des e tats dun AFND donne.
b(T ) = T {q 0 E | il existe un chemin e tiquete par des allant dun e tat q T
vers letat q 0 }.

34

Expressions reguli`eres et automates finis

Exemple 2.9.1 Pour lun AFND de la figure 2.5, on a :


b({1, 3}) = {1, 2, 3}

2.9.2

Algorithme de calcul de la -cloture

Le calcul de la -cloture dun sous-ensemble detats T est un processus typique


de recherche dans un graphe dun ensmeble donne de nud. Dans ce cas, les e tats
de T forment lensemble donne de nuds et le graphe est constitue uniquement
des -transitions de lAFND. Un algorithme simple pour calculer la -cloture de T
utilise une pile pour conserver les e tats dont les transitions sur nont pas encore
e te examines. Voici le pseudo-code de cet algorithme :

F IG . 2.13 Algorithme de calcul de la b-cloture dun ensemble detats T .

2.9.3

Algorithme de determinisation dun AFND

Lalgorithme de determinisation prend en entree un AFND M = (E, A, ,


Q0 , F ) et fournit en sortie un AFD M 0 = (E 0 , A0 , 0 , q00 , F 0 ) e quivalent a`
M (cest-`a-dire tel que L(M ) = L(M 0 )). Le pseudo-code de cet algorithme est
presente dans la figure suivante :
Exemple 2.9.2 Appliquons cet algorithme pour convertir lAFND de la figure
2.5 en un AFD e quivalent :
1. A0 = A = {a, b}.

2.9. Conversion dun AFND en un AFD

35

F IG . 2.14 Algorithme de determinisation dun AFND.


2. q00 = b{1} = {1, 2}.
3. Iteration 1 :
0 ({1, 2}, a) = b({1, 3}) = {1, 2, 3} ;
0 ({1, 2}, b) = b({3}) = {3} ;
Iteration 2 :
0 ({1, 2, 3}, a) = b({1, 3}) = {1, 2, 3} ;
0 ({1, 2, 3}, b) = b({3}) = {3} ;
0 ({3}, a) = b({}) = ;
0 ({3}, b) = b({}) = ;
On renomme les e tats : {1, 2} = 1, {1, 2, 3} = 2 et {3} = 3.
4. F 0 = {2, 3}.
La representation graphique de lAFD obtenu est illustre par la figure suivante :

36

Expressions reguli`eres et automates finis

F IG . 2.15 LAFD obtenu par determinisation de lAFND de la figure 2.5.

2.10

Minimisation dun AFD

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.

2.10.1 Simplification dun AFD


Definition 2.10.1 Soit M un AFD. Un e tat q de M est accessible, sil existe au
moins un chemin de letat initial q0 vers q.
Definition 2.10.2 Soit M un AFD. Un e tat q de M est co-accessible, sil est
accessible et sil existe au moins un chemin menant de letat q vers un e tat final q 0
de M .
Definition 2.10.3 Soit M un AFD. Un e tat q de M est utile, sil est co-accessible.
On dit que M est simplifie, sil ne contient que des e tats utiles.
On obtient un AFD simplifie en supprimant tous les e tats non accessibles et non
co-accessibles ainsi que toutes les transitions qui sy rapportent (sortantes et entrantes de ces e tats). Par exemple, simplifions lAFD de la figure suivante :
LAFD considere nest pas simplifie : letat 7 est non accessible. On supprime cet
e tat, ainsi que toutes les transitions qui sy rapportent. On obtient lAFD suivant :

2.10. Minimisation dun AFD

37

F IG . 2.16 Un AFD a` simplifier.

F IG . 2.17 Suppression de letat non accessibles 7.


On supprime letat 8 car il nest pas co-accessible, ainsi que toutes les transitions
qui sy rapportent. On obtient finalement lAFD simplifie :

F IG . 2.18 Un AFD a` simplifie.

38

2.10.2

Expressions reguli`eres et automates finis

Equivalence
de Nerode

Soit M = (E, A, , q0 , F ) un AFD simplifie. Pour tout e tat q E, on note


Lq (M ) le langage forme par tous les mots reconnus par lautomate M en prennant
letat q comme e tat initial. Formellement :
Lq (M ) = {w A | (q, w) F }
Lorque lAFD M est fixe, on e crira Lq au lieu de Lq (M ).
Definition 2.10.4 Deux e tats p et q sont dits inseparables, si et seulement si Lp =
Lq . Dans le cas contraire, on dit que p et q sont separables. Ainsi, p et q sont
separables si et seulement sil existe un mot w A tel que (p, w) F et
(q, w) 6 F ou vice-versa. Si w est un mot verifiant cette propriete, on dit quil
separe les e tats p et q.
Exemple 2.10.1 Dans lautomate de la figure 2.18, les e tats 3 et 6 sont inseparables
puisque L3 = L6 = (a|b) , alors que les e tats 4 et 6 sont separables puisque
L4 = b a(a|b) 6= (a|b) . Le mot separe les e tats 4 et 6.
Definition 2.10.5 Lequivalence de Nerode sur M est la relation binaire definie
sur lensemble des e tats E par p q si et seulement si p et q sont inseparables.
Les algorithmes de minimisation des AFD se diff`erent de la mani`ere de calculer
les classes dequivalence de cette relation . Nous presentons un algorithme de
minimisation tr`es simple du a` Hopcroft et Ullman [1979].

2.10.3

Algorithme de Hopcroft & Ullman

Soit M un AFD simplifie. Voici tout dabord le pseudo-code de lalgorithme :


Lidee de lalgorithme de Hopcroft & Ullman est de determiner tous les groupes
detats qui peuvent e tre separes par un mot dentree. Chaque groupe detats qui ne
peuvent pas e tre separes est alors fusionne eu un e tat unique. Lalgorithme fonctionne en memorisant et en raffinant une partition de lensemble des e tats. Chaque
groupe detats a` linterieur de la partition consiste en les e tats qui nont pas encore
e te separes les uns des autres, et toute paire detats extraits de differents groupes
a e te prouve separes par un mot.
Initialement, la partition consiste en geux groupes : les e tats finaux et les e tats
non-finaux : 0 = (F, E \ F ). Letape fondamentale consiste a` prendre un groupe
detats, par exemple G = {e1 , e2 , ..., ek }, et un symbole a A, et a` examiner
les transitions des e tats {e1 , e2 , ..., ek } sur a. Si ces transitions conduisent a` des

2.10. Minimisation dun AFD

39

F IG . 2.19 Pseudo-code le lalgorithme de minimisation de Hopcroft & Ullman.


e tats qui tombent dans au moins deux groupes differents de la partition courante,
alors on doit diviser le groupe G de mani`ere que les transitions depuis chaque
sous-ensemble de G soient toutes confiees a` un seul groupe de la partition courante. Supposons par exemple que e1 et e2 m`enent aux e tats t1 et t2 et que t1 et t2
soient dans des groupes differents de la partition. Alors, on doit diviser G en au
moins deux sous-ensembles de telle mani`ere quun sous-ensemble contienne e1 et
lautre e2 . Notons que t1 et t2 sont separes par un mot w, ce qui prouve que e1 et
e2 sont separes par le mot aw.
Nous repetons ce processus de division de groupes de la partition courante jusqu`a
ce que plus aucun groupe nait besoin detre divise. Pour obtenir lAFD minimal,
on applique les r`egles suivantes :
. Transformer chaque groupe en un e tat.
. Letat initial est le groupe qui contient letat initial de lAFD de depart.
. Les e tats finaux sont les groupes qui ne contiennet que des e tats finaux

40

Expressions reguli`eres et automates finis


de lAFD initial.
. La matrice de transition est obtenue en ne gardant quune seule ligne
representatrice dun groupe dans la partition finale.

2.10.4

Un exemple dapplication

Considerons lAFD represente par la figure suivante :

F IG . 2.20 Un AFD simplifie a` minimiser.


La premi`ere partition est formee de deux groupes : #1 = {1, 2, 3, 5, 6} et
#2 = {4, 7}. On calcule la fonction de transition relative a` cette partition :

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 :

2.10. Minimisation dun AFD

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

Les e tats 1 et 2 sont separables. Remarquons que le groupe #2 = {3, 6}


est non cassable.
On obtient alors une nouvelle partition avec quatre goupes : #1 = {1},
#2 = {2, 5}, #3 = {3, 6} et #4 = {4, 7}. On calcule la nouvelle fonction de transition relative a` cette partition :
Groupes
#1
#2
#2
#3
#3
#4
#4

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

Lautomate minimal trouve est represente par le graphe suivant :


Le langage reconnu par cet automate est alors : (a|b)(a|b)a(a|b) .

42

Expressions reguli`eres et automates finis

F IG . 2.21 LAFD minimal.

2.11

Langages reguliers et automates finis

Dapr`es lalgorithme de Thompson, nous pouvons affirmer le resultat suivant


(Theor`eme de Thompson) :
Theor`eme 2.11.1 Soit A un alphabet. Si un langage L est regulier sur A, alors
L est reconnu par un AFD.
Le theor`eme suivant est du a` Kleene :
Theor`eme 2.11.2 Soit A un alphabet. Un langage L est regulier sur A, si et
seulement sil est reconnu par un AFD.
Pour demontrer ce theor`eme, il suffit de prouver que si un langage L est reconnu
par un AFD, alors il est regulier, ce qui revient a` exhiber pour tout AFD une
expression reguli`ere e quivalente. Il existe plusieurs algorithmes pour obtenir cette
expression. Nous presentons premi`erement une methode appelee lalgorithme BMC
(Brzozowski et McCluskey).

2.11.1

Algorithme BMC

Soit M = (E, A, , q0 , F ) un AFD. On cherche une expression reguli`ere


denotant le langage reconnu par M . On va proceder par suppression de transitions
et detats, en remplacant dautres e tiquettes par des expressions reguli`eres :
1. Ajouter a` M deux nouveaux e tats, notes (etat initial) et (etat final), et
les transitions (, , q0 ) et (qf , , ) pour tout e tat qf F .
2. Repeter les actions suivantes tant que possible :
Sil existe deux transitions (p, r, q) et (p, s, q), les remplacer par la
transition (p, r|s, q).
Supprimer un e tat q (autre que et ) et remplacer, pour tout e tats
p, r 6= q, les transitions (p, s, q), (q, t, q) et (q, u, r), par la transition (p, st u, r).

2.11. Langages reguliers et automates finis

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 :

F IG . 2.22 Lautomate de la figure 2.5, augmente de deux e tats et .


. Supprimons letat 2. Les couples de transitions concernees sont (1, a, 2) et
(2, , ) dune part, et (1, a, 2) et (2, b, 3) dautre part. Le premier couple
produit une transition (1, aa = a+ , ), le deuxi`eme produit une transition
(1, aa b = a+ b, 3), ce qui donne lautomate :

44

Expressions reguli`eres et automates finis

F IG . 2.23 Lautomate precedent, apr`es suppression de letat 2.


. Supprimons les deux transitions (1, a+ b, 3) et (1, b, 3), et remplacons les par
la transition (1, a+ b | b = a b, 3). Cel`a donne lautomate :

F IG . 2.24 Lautomate precedent, apr`es suppression des deux transitions


(1, a+ b, 3) et (1, b, 3).
. Supprimons letat 3, le couple de transitions concernees est (1, a b, 3) et
(3, , ), qui produit une transition (1, a b, ). Ce qui donne lautomate :

F IG . 2.25 Lautomate precedent, apr`es suppression de letat 3.


. Supprimons les deux transitions (1, a+ , ) et (1, a b, ), et remplacons les
par la transition (1, a+ | a b = a (a | b), ). Cel`a donne lautomate :

F IG . 2.26 Lautomate precedent, apr`es suppression des deux transitions


(1, a+ , ) et (1, a b, ).
. Il ne reste plus qu`a supprimer letat 1. On obtient lautomate suivant :

2.11. Langages reguliers et automates finis

45

F IG . 2.27 Lautomate compl`etement reduit.


. Finalment, on obtient lexpression reguli`ere a (a|b).

2.11.2 Methode algebrique


` partir dun AFD M = (E, A, , q0 , F ), on va construire un syst`eme de
A
n e quations lineaires (n e tant le nombre des e tats de M ). Les inconnus de ce
syst`eme sont les langages
Xq = {w A | (q, w) F }
o`u q E est un e tat de lautomate. Xq est le langage forme de tous les mots reconnus par M , en prenant q comme e tat initial. Nous avons les resultats suivants :
Lemme 2.11.1 Soient p et q des e tats dun AFD M = (E, A, , q0 , F ).
1. Si (p, a) = q, alors aXq Xp .
2. Si (p, a) = p, alors aXp Xp .
3. Si q F , alors Xq .
Le syst`eme dequations lineaires est obtenu en appliquant ces r`egles. Voici un
exemple de ce syst`eme pour lautomate de la figure 2.5 :

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

Expressions reguli`eres et automates finis

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

2.12. Lemme de pompage

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.

F IG . 2.28 Automate minimal et lemme de pompage.


Exemple 2.12.1 Le langage L = {an bn | n 0} sur A = {a, b} nest pas
regulier.
Preuve. On utilise le lemme de pompage. Supposons que L est regulier. Il verifie
le lemme de pompage. Notons N lentier assure par ce lemme. Prenons le mot
w = aN bN et soit une decomposition w de la forme w = xyz. Comme |xy| N
et |y| 1, alors x = ai , y = aj et z = aN ij bN , avec i 0 et j 1. Le mot
xy 0 z = aN j bN L, ce qui oblige que j = 0. Ceci est impossible, puisque j 1,
ce qui entrane une contraduction. Le langagee L nest pas alors regulier.

Chapitre 3
Alanyse lexicale
3.1

Role de lanalsye lexicale

Le programme danalyse lexicale (scanning) est assure generalement par un


module appele analyseur lexical (scanner). Le role principal de lanalyseur lexical est de combiner les caract`eres du fichier source pour former des mots ayant
un sens pour le langage source. Ces mots sont appeles lex`emes (ou token). Au
passage, cette phase doit :
. reconnatre les mots reserves, les constantes, les identificateurs, ...etc.
. signaler les erreures lexicales (mots mal orthographies) et relier les messages derreurs issues du compilateur au programme source.
. ignorer les commentaires et les caract`eres blancs.
. effectuer, si necessaire, un pre-traitement du texte source.

3.2 Unites lexicales, lex`emes et mod`eles


3.2.1 Definitions
Definition 3.2.1 Une unite lexicale represente une classe particuli`ere du lexique
(vocabulaire) du langage source.
Exemple 3.2.1 Les mots-cles et les identificateurs sont deux unites lexicales.
Definition 3.2.2 Un lex`eme (ou token) est une chane de caract`eres qui appartient a` une unite lexicale.

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

Les unites lexicales les plus courantes

La plupart des langages de programmation disposent des unites lexicales suivantes :


. Les mots-cles (keywords) : leurs definitions et leurs utilisations sont fixees
par le langage. Exemples de mots-cles dans C : if, else, for, while,
do, switch, ...etc.
. Les identificateurs : ce sont les noms quun programmeur utilise pour
designer les entites dun programme (variables, constantes, types, fonctions).
. Les operateurs : symboles doperations arithmetiques, logiques et de comparaison.
. Les nombres : entiers ou reels, positifs ou negatifs.
. Les chanes de caract`eres : seqeunces de caract`eres alphanumeriques.
. Les delimiteurs : espace, tabulation et retour a` la ligne.
. Les symboles speciaux : ( ) [ ] { } ...etc.

3.3
3.3.1

Interface avec lanalyseur syntaxique


Position de lanalyseur lexical

Le plus souvent, lanalyseur lexical est implemente comme un sous-programme


de lanalyse syntaxique. Ce dernier ordonne a` lanalyseur lexical de delivrer la
prochaine unite lexicale du programme source. Lanalyseur lexical lit alors la suite

3.4. Implementation dun analyseur lexical

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 :

F IG . 3.1 Relation entre lanalyseur lexical et lanalyseur syntaxique.

3.3.2 Attribut dun lex`eme et table des symnoles


Savoir a` quelle unite lexicale est associe un lex`eme est essentiel pour lanalyse syntaxique. Cependant, les e tapes suivantes de la compilation auront besoin
dautres informations sur les lex`emes. Lanalyseur lexical doit alors reunir les informations utiles sur les lex`emes. Ces informations sont stockees dans une table,
appelee table des symboles, sous forme dattributs. Un attribut pour un lex`eme
peut e tre sa valeur, sa position dans le fichier source, ...etc.

3.4

Implementation dun analyseur lexical

Il y a trois methodes classiques pour coder un analyseur lexicale :


La methode manuelle : lanalyseur lexical est e crit en chair et en os a` la
main en utilisant un langage e volue (comme C, P ascal, ou Java).
La methode semi-automatique : on simule lAFD (ou les AFD) qui reconnat les unites lexicales en utilisant un langage de haut-niveau. LAFD
e tant construit a` la main.
La methode automatique : lanalyseur lexical est genere a` partir dune
description par un outil informatique qui gen`ere automatiquement le code
executable de lanalyseur. Cette methode sera presentee dans le chapitre
suivant (Loutil Flex).

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 :

3.5. La methode manuelle

53

carlu = gectchar(); /* carlu : variable de type char */


ungetc(carlu, stdin);
laisse le flot dentree inchange. La premi`ere instruction affecte le prochain caract`ere de lentree a` la variable carlu, alors que la deuxi`eme rend a` lentree
standard stdin la valeur de la variable carlu.
La fonction AnalLex() rend un entier qui code une unite lexicale. Lunite lexicale, comme KEYWORD, peut alors e tre code par un entier superieur aux entiers
encodant les caract`eres, par exemple 256. Pour permettre un changement aise de
lencodage, on utilise une constante symbolique KEYWORD pour designer lentier enocdant KEYWORD. Pour realiser ceci en C, on utilise une directive de
definition :
#define KEYWORD 256
#define IDENT
257
#define NBENT
258
Pour envoyer lattribut dun lex`eme, on utilise une variable globale ValLex.
Cette variable est declaree comme union :
union ValLex
{
int valint; /* pour un entier */
char valstr[33];
/* pour un identificateur ou un mot cl
e */
};
La fonction EstMotCle() teste si un identificateur est un mot-cle. La fonction
Erreur affiche un message derreur. Lanalyseur ignore les caract`eres blancs
(espaces, tabulations et retour a` la ligne). Sil reconntre une chane non specifiee
et qui nest pas ignore, il signale une erreur et quite lanalyse lexicale. La figure
3.3 presente le pseudo-code de notre analyseur e crit a` la main.

54

Alanyse lexicale

F IG . 3.3 Pseudo-code de lanlyseur lexical.

3.6

La methode semi-automatique

Les AFD constituent un outil important pour lecriture danalyseurs lexicaux,


car leur implementation avec un langage e volue (de haut-niveau) est tr`es facile.
Si nous disposons dun AFD qui reconnat les unites lexicales du langage source,

3.6. La methode semi-automatique

55

nous pouvons effectuer lanalyse lexicale de ce langage en e crivant un programme


qui simule le travail de lAFD.
Pour notre cas, la figure 3.4 illustre un AFD qui acc`epte les unites lexicales du
fragment du langage e tudie.

F IG . 3.4 Un AFD qui reconnat le fragment du langage e tudie.


On part de letat 0, car cest letat initial de lAFD. On lit un caract`ere sur lentree,
puis on effectue une sequence de test :
Si le caract`ere lu est un symbole blanc (espace, tabulation ou retour a` la
ligne), on reste dans letat 0.
Si le caract`ere lu est une lettre, on transite a` letat 1. Cest la premi`ere lettre
dun identificateur ou dun mot-cle. On initialise le premier caract`ere de la
chane a` ce caract`ere lu.
Si le caract`ere lu est un chiffre, on transite a` letat 3. Cest le premier chiffre
dun nombre. On initialise la valeur du nombre avec la valeur decimale de
ce caract`ere.
Si le caract`ere lu est EOF (le caract`ere de fin de fichier), on quite lanalyse
lexicale.
Pour tout autre caract`ere lu, signaler une erreur lexicale et sortir.

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 :

3.6. La methode semi-automatique

F IG . 3.5 Pseudo-code du simulateur de lAFD de lanlyseur lexical.

57

Chapitre 4
Loutil Flex
4.1

Presentation de Flex

4.1.1 Le compilateur Flex


Flex (Fast Lexer) est un generateur danalyseurs lexicaux. Flex prends en
entree un fichier source (.lex ou .flex) contenant une description dun analyseur lexical et delivre un fichier nomme lex.yy.c qui contient le code C du future
analyseur lexical. Ce dernier, lorsquil est compile avec gcc produira lexecutable
de lanalyseur lexical. La compilation dun fichier Flex, seffectue via la commande flex. Voici le schema a` suivre pour construire un analyseur lexical avec le
compilateur Flex :

F IG . 4.1 Creation dun analyseur lexical avec Flex.


Le fichier de description contient princialement des expressions reguli`eres et des
actions a` executer lors de la reconnaissance de chaque lex`eme. Flex prends cette
description et gen`ere un AFND combine (reunion de tous les AFND de lensemble des expressions reguli`eres decrites). Ensuite Flex convertit cet AFND en

60

Loutil Flex

un AFD, puis le minimise et gen`ere le code C qui va simuler lautomate minimal.

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

Format dun programme Flex

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. Les r`egles

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]*

printf("ID : %s\n", yytext);

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

Variables et fonctions predefinies

. yytext : (char) chane de caract`eres qui contient le lex`eme courant reconnu.


. yyleng : (int) longueur de yytext (aussi de ce lex`eme).
. yylineno : (int) numero de la ligne courante.
. yyin : (File) fichier dentree a` partir duquel Flex va lire les donnees a`
analyser. Par defaut, yyin pointe vers stdin.
. yyout : (File) fichier de sortie dans lequel Flex va e crire les resultats de
lanalyse. Par defaut, yyout pointe vers stdin.
. unputc(car) : fonction qui remet le caract`ere car dans le flot dentree.
. main( ) : la fonction main par defaut contient juste un appel a` la fonction
yylex. Elle peut e tre redefinie par lutilisateur dans la section des procedures
auxiliaires.

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"

/* laction est absente */


;

. 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);

. REJECT : force lanalyseur a` se brancher a` la deuxi`eme meilleure r`egle


applicable. Par exemple, voici un exemple qui compte tous les mots dun
fichier, sauf les mots bon :

4.6. Comment Flex gen`ere un analyseur lexical

63

%{
int nb_mots = 0;
%}
%%
bon
REJECT;
[a-z]* nb_mots++;

. yymore() : demande a` lanalyseur de concatener le contenu du lex`eme


courant au lieu de le remplacer. Par exemple, e tant donnee lentree megaoctet, le programme suivant produira laffichage mega-mega-octet :
%%
mega- ECHO; yymore();
octet ECHO;

. 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;

. yyterminate() : termine lexecution de lanalyseur et renvoie la valeur


0 a` lappelant de lanalyseur, en indiquant le message all done. Par
defaut, yyterminate() est appelee lorsquun caract`ere EOF est lu. Par
example, le programme suivant affichera le premier entier rencontre dans
un fichier puis resumera lanalyseur :
%%
[0-9]+

4.6

ECHO; yyterminate();

Comment Flex gen`ere un analyseur lexical

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

et est genere automatiquement pour la routine yylex(). La figure 4.2 illustre le


pseudo-code de ce programme de simulation de lautomate.

F IG . 4.2 Programme de simulation de lAFD genere par Flex.

4.7

Situation a` larret de lautomate

Lorsque plusieurs expressions reguli`es peuvent sappliquer pour analyser le


debut de la chane dentree, les r`egles suivantes sont appliquees a` la construction
de lautomate pour enlever lambigute entre les r`egles :
. La r`egle conduisant a` la reconnaissance de la plus longue est dabord choisie.
. A e galite de longueur reconnue, cest la premi`ere r`egle qui est choisie.
Si aucun e tat final na e te atteint (aucune expression ne reconnat lentree) laction
par defaut est declenchee (recopie du caract`ere dentree sur la sortie).

4.8. Exemple dun analyseur lexical avec Flex

4.8

65

Exemple dun analyseur lexical avec Flex

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

F IG . 4.4 Fichier source a` analyser.


Voici le resultat obtenu :

F IG . 4.5 Resultat de lanalyse lexicale.

4.9

Quelques options de compilation avec Flex

. d : execute lanalyseur en mode de debougage.


. s : supprime leffet de la r`egle par defaut de Flex.

4.9. Quelques options de compilation avec Flex

67

. t : e crit le code de lanalyseur genere sur la sortie standard, au lieu de


lecrire dans le fichier lex.yy.c.
. T : affiche les informations sur lautomate fini deterministe cree par Flex.
. V : affiche la version de Flex utilise.

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 ).

Lemme 5.2.1 (fondamental) Soient G = (V, T, P, S) une GHC et , 0 , , 0 et


des formes sur G. Si 0 et 0 , alors :
1. 0 .
2. 0 .
3. 0 0 .
Inversement, si , alors il existe deux formes 0 et 0 telles que :
1. = 0 0 .
2. 0 .
3. 0 .

5.3

Langage engendre par une GHC

Definition 5.3.1 Soit G = (V, T, P, S) une GHC. Le langage engendre par G


est forme par tous les mots sur lalphabet T qui derivent en plusieurs e tapes de
laxiome S de G. On le note par L(G). Formellement :
L(G) = { T | S }
Exemple 5.3.1 Soit la GHC G = ({S}, {a}, P, S) definie par les productions :
S aaS | . Alors, L(G) = {(aa)n | n 0} = (aa) .
Exemple 5.3.2 Soit la GHC G = ({S}, {a, b}, P, S) definie par les productions
S aSb | . On a : L(G) = {an bn | n 0}.

5.4
5.4.1

Langages hors contexte


Definition

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

Exemple 5.4.1 Les langages suivants sont algebriques :


L = {(aa)n | n 0} = (aa) . En effet, il est engendre par la grammaire :
S | aaS.
L = {an bn | n 0}, car engendre par la grammaire : S | aSb.
Theor`eme 5.4.1 Tout langage regulier sur un alphabet A est algebrique sur A.
Remarque 5.4.1 La reciproque de ce theor`eme est fausse. Voici un contre exemple :
le langage L = {an bn | n 0} est algebrique, mais non regulier.

5.4.2

Proprietes de cloture pour langages algebriques

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.

5.5 Proprietes de non cloture


Theor`eme 5.5.1 La classe des langages algebriques nest pas close par les operations
dintersection et de complementation.
Contre-exemple pour lintersection. Soient les deux langages : L = {ap bp cq |
p, q 0} et M = {ap bq cq | p, q 0}. Les langages L et M sont algebriques, car
L = {ap bp | p 0}{cq | q 0} et M = {ap | p 0}{bq cq | q 0}, et lon
on sait que {ap bp | p 0}, {bq cq | q 0}, {ap | p 0} et {cq | q 0} sont
algebrique. Cependant, le langage LM = {ap bp cp | p 0} nest pas algebrique.
Preuve pour le complementaire. Si la classe des langages algebriques est close
par complementaire, alors elle le serait par intersection. En effet, la classe des
langages algebriques est close par reunion et lon a toujours : L M = L M .
Or, la classe des langages algebriques nest pas close par intersection.

74

Grammaires hors-contexte

5.6

Arbres de derivation

Definition 5.6.1 Soit G = (V, T, P, S) une GHC. Un arbre de derivation de G


est un arbre fini e tiquette par les e lements de (V T ) et verifiant les proprietes
suivantes :
. Les nuds internes de sont les symboles non-terminaux (X V ).
. Si un nud interne X de , a pour fils u1 , u2 , ..., un , alors X u1 u2 ...un
est une r`egle production de G.
Definition 5.6.2 Soient G = (V, T, P, S) une GHC et un arbre de derivation
de G. La fronti`ere de , notee F r(), est le mot obtenu par concatenation (de
gauche a` droite) des e tiquettes de ses feuilles.
Definition 5.6.3 Soient G = (V, T, P, S) une GHC et un arbre de derivation
de G. On dit que est complet (ou total) si :
La racine de est laxiome S de G.
1. La fronti`ere de est un mot terminal, cest-`a-dire un mot du langage T .
Un arbre de derivation est incomplet ou partiel sil nest pas complet.
Exemple 5.6.1 Soit G la GHC definie par : S aB et B bc|bB. On a la
derivation : S aB abc. Un arbre de derivation associe est le suivant :

F IG . 5.1 Un arbre 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

Definition 5.6.4 On appelle derivation la plus a` gauche, une derivation o`u la


variable substituee est systematiquement le symbole le plus a` gauche du second
membre de la r`egle de production.
Exemple 5.6.2 Soit G la GHC definie par : S AB|AC, A x|xy, B z
et C yz. Le mot = xz peut e tre obtenu par la derivation la plus a` gauche
suivante : S AB xB xz.
Definition 5.6.5 On appelle derivation la plus a` droite, une derivation o`u la
variable substituee est systematiquement le symbole le plus a` droite du second
membre de la r`egle de production.
Exemple 5.6.3 Soit G la GHC definie par : S AB|AC, A x|xy, B z
et C yz. Le mot = xz peut e tre obtenu par la derivation la plus a` droite
suivante : S AB Az xz.

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 :

F IG . 5.2 Deux arbres de derivation distincts pour le mot w = xyz.

76

5.8
5.8.1

Grammaires hors-contexte

Ree criture de grammaires

Elimination
de la recursivite immediate a` gauche

Definition 5.8.1 Une GHC G est immediatement recursive a` gauche (IRG), si


elle contient au moins une production de la forme X X, o`u est une forme
sur G.
Exemple 5.8.1 La grammaire : S ScB | C, B Ba | et C Cc | b | d,
est IRG (elle contient S ScB).
Methode delimination de la RIG :

F IG . 5.3 Algorithme de lelimination de la RIG.

Exemple 5.8.2 La grammaire precedente est e quivalente a` : S CX, X


cBX | , B Y , Y aY | , C bZ | dZ et Z cZ | .

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 :

5.8. Ree criture de grammaires

77

F IG . 5.4 Algorithme de lelimination de la RG.

Exemple 5.8.4 Soit la grammaire : S Ba | b et B Bc | Sd | .


1. On ordonne les non-terminaux : S, B (n = 2).
2. Iteration i = 1. La boucle sur j ne sexecute pas.
3. Iteration i = 2. La boucle sur j sexecute une seule fois (j = 1).
On remplace la production B Sd par B Bad | bd.
On e limine les recursivites immediates a` gauche B Bc et B Bad.
On obtient, B bdX | X et X cX | adX | .

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

F IG . 5.5 Algorithme de factorisation a` gauche.

Exemple 5.8.6 Pour la grammaire du dernier exemple, on obtient :


Iteration 1. On a les productions : S aEbSX | a, X eB | ,
E bcY , Y B | a, B ba.
Iteration 2. On a les productions : S aZ, Z EbSX | , X eB | ,
E bcY , Y B | a, B ba.

5.9

Precedence des operateurs

Certains analyseurs necessitent que la grammaire de base soit non ambigue. La


solution pour utiliser ce type danalyseurs consiste alors a` ree crire la grammaire
initiale sous une autre forme qui e vite toutes les sortes dambigute. Dans le cas
dune grammaire manipulant des operateurs, il faut exprimer la hierarchie des
operateurs dans la grammaire elle-meme.

5.9.1

Associativite des operateurs

Soit un operateur quelconque.


Definition 5.9.1 On dit que est :

5.9. Precedence des operateurs

79

1. associatif a` gauche, si lexpression a b c doit e tre e valuee de gauche a`


droite, cest-`a-dire comme (a b) c.
2. a une associatif a` droite, si lexpression a b c doit e tre e valuee de droite
a` gauche, cest-`a-dire comme a (b c).
3. est non associatif, si les expressions de la forme a b c sont illegales.

Voici des exemples :


Par la convention usuelle, les operateurs et / sont associatifs a` gauche.
Les operateurs + et sont associatifs au sens mathematique. Cela signifie
que lordre devaluation dune expression a + b + c, na pas dinteret. Cependant, pour interdire lambigute, il faut fixer une associativite pour ces
operateurs. Par convention, les operateurs + et sont associatifs a` gauche.
Les operateurs de construction de liste dans les langages fonctionels (comme
:: et @ en SM L, sont typiquement associatifs a` droite. Loperateur daffectation est e galement associatif a` droite (lexpression a = b = c est e valuee
comme a = (b = c)).
Dans certains langages (comme P ascal), les operateurs de comparaison
(comme < ou >) sont non associatifs, ce qui explique que lexpression
2 < 3 < 4 est syntaxiquement incorrecte en P ascal.

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 :

F IG . 5.6 Larbre de derivation de lexpression 2 3 4.


Si est associatif a` droite, nous transformons la grammaire comme suit :
E E0 E | E0
E 0 num
Avec cette transformation, lexpression 234 poss`ede maintenant un seul arbre
de derivation conforme avec lassociativite a` droite de loperateur :
Pour que ces r`egles marchent, il faut que les operateurs ayant la meme precedence
aient la meme associativite. En effet, melanger la recursivite a` gauche et la recursivite
a` droite pour un meme non-terminal rend la grammaire ambigue. Considerons la
grammaire define par :
E E E0
E E0 E
E E0
E 0 num

5.9. Precedence des operateurs

81

F IG . 5.7 Larbre de derivation de lexpression 2 3 4.


Cette grammaire donne aux operateurs et la meme precedence et des associativites differentes (`a gauche pour et a` droite pour ). Elle nest pas seulement
ambigue, mais elle nacc`epte meme pas le langage attendu. Par exemple, la chane
num num num nest pas derivable par cette grammaire.
En general, il ny a pas un moyen e vident pour resoudre lambigute dans une expression comme 1 2 3, o`u est associatif a` gauche et est associatif a` droite
(et vice-versa). Cest pour cette raison que la plupart des langages de programmation (et la plupart des generateurs danalyseurs syntaxiques) forcent cette r`egle :
Deux operateurs ayant un meme niveau de precedence (priorite) doivent avoir
la meme associativite.
Il est naturel de trouver des operateurs de priorites differentes dans une meme
expression, comme 2 + 3 4. La suppression dambigute dans les grammaires
favorisant ce type dexpressions est garantie par lutilisation dun symbole nonterminal pour chaque niveau de priorite. Lidee est que si une expression utilise un
operateur dun certain niveau de priorite n, alors ses sous-expressions ne peuvent
pas utiliser des operateurs de priorite inferieure a` n, sauf si elles sont incluses
dans des parenth`eses. Par consequent, les productions pour un non-terminal correspondant a` un niveau de priorite particulier feront reference uniquement aux
non-terminaux qui correspondent au meme niveau de priorite ou a` un niveau de
priorite superieur. Lexemple suivant montre comment ces considerations sont
prises en compte pour avoir une grammaire dexpressions arithmetiques o`u les
operateurs +, , et / ont tous une associativite a` gauche, + et ont une meme
priorite qui est inferieure a` celle de et /, en plus les parenth`eses ont la plus
grande priorite :

82

Grammaires hors-contexte

Exp Exp + Exp1


Exp Exp Exp1
Exp Exp1
Exp1 Exp1 Exp2
Exp1 Exp1 / Exp2
Exp1 Exp2
Exp2 num
Exp2 (Exp)
Avec cette grammaire, lexpression 2 + 3 4 na quune seule derivation possible :

F IG . 5.8 Larbre de derivation de lexpression 2 + 3 4.

Chapitre 6
Analyse syntaxique
6.1

Role de lanalyse syntaxique

Lanalyse syntaxique est assuree par un module appele analyseur syntaxique


(parser). Le role de lanalyseur syntaxique est de combiner les unites lexicales
fournits par lanalyseur lexical pour former des structures grammaticales. Ces
derni`eres sont typiquement de la forme dune structure de donnees darbre appellee arbre syntaxique abstrait (Abstract Syntax Tree, ou AST). Les feuilles de
cet arbre correspondent aux lex`emes delivres par lanalyseur lexical. La sequence
formee par ces feuilles lorsuqelles sont lues de gauche a` droite, est identique au
texte dentree initial. Une autre tache de lanalyseur syntaxique consiste a` detecter
les erreurs syntaxiques dans le texte source. Par exemple :
Oubli dun point virgule a` la fin dune instruction :
int a

Manque dune parenth`ese droite :


if(a == 0 printf("zero\n");

Operande attendu dans une operation :


a = b * ;
La figure suivante illustre la position de lanalyseur syntaxique dans le front-end

84

Analyse syntaxique

dun compilateur :

F IG . 6.1 Position de lanalyseur syntaxique.

6.2

Analyse syntaxique et GHC

Lanalyseur syntaxique doit verifier la validite grammaticale des phrases du


programme source et determiner leur structure sous forme darbres de syntaxe.
Le probl`eme de lanalyse syntaxique peut se formuler comme suit : e tant donnees
une grammaire hors-contexte G = (V, T, P, S) et un terminal T , un analyseur syntaxique doit repondre a` la question est-ce que T ? Si la reponse est
affirmativen il doit construire larbre de syntaxe de . Sinon, il doit signaler une
erreur de syntaxe.
On peut distinguer les methodes danalyse syntaxique par lordre dans lequel elles
construisent les nuds de larbre syntaxique. Il y a deux classes de methodes
danalyse syntaxique :

1. Les methodes danalyse descendante : elles construisent larbre syntaxique


en preordre, cest-`a-dire du haut en bas, en partant de la racine (laxiome)
vers les feuilles (les lex`emes).
2. Les methodes danalyse ascendante : elles construisent larbre syntaxique
en postrdre, cest-`a-dire du bas en haut, en partant des feuilles vers la racine.

6.3. Analyse descendante

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).

6.3.2 Un premier exemple


Considerons la grammaire definie par :
S aSbT | cT | d
T aT | bS | c
Faisons lanalyse du mot w = accbbadbc. On part avec larbre contenant le seul
sommet S. On lit le premier lex`eme du mot : a. Il y a une seule S-production
dont le corps commence par a : S aSbT . On applique cette production pour
developper larbre de derivation, ce qui donne :

F IG . 6.2 Premi`ere derivation du mot w = accbbadbc.


Nous avancons le pointeur de lecture vers le lex`eme suivant (`a gauche) : c. Cherchons a` faire une derivation la plus a` gauche. Le prochain symbole non-terminal
a` developper est S. La production a` appliquer est S cT , ce qui donne :

F IG . 6.3 Deuxi`eme derivation du mot w = accbbadbc.

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.

F IG . 6.4 Arbre de derivation totale du mot w = accbbadbc.

6.3.3

Un deuxi`eme exemple

Considerons maintenant la grammaire definie par :


S aAb
A cd | c
Faisons lanalyse du mot w = acb. On part de la racine S. On lit le premier lex`eme
du mot : a. Il y a une seule production a` appliquer : S aAb et le prochain nonterminal a` developper est A, ce qui donne :

F IG . 6.5 Premi`ere derivation du mot w = acb.


En lisant le prochan lex`eme c, on ne sait pas sil faut prendre la production A
cd ou la production A c. Pour le savoir, il faut lire aussi le lex`eme suivant b, ou

6.4. Analyse predictive

87

alors, il faut donner la possibilite de faire des retours en arri`ere (backtracking) :


on choisit la production A cd, on aboutit a` un e chec, ensuite on retourne en
arri`ere et on choisit la production A c qui convient cette fois-ci.

6.4

Analyse predictive

Lanalyse syntaxique predictive est une methode danalyse descendante dans


laquelle nous pouvons toujours choisir une production unique en se basant sur
le prochain symbole de lentree et sans effectuer aucun retour en arri`ere.
Il y a deux facons pour effectuer une analyse predictive :
. Lanalyse predictive recursive : implementee en utilisant des procedures
recursives.
. Lanalyse predictive iterative : dirigee par une table danalyse.
Lanalyse predictive necessite le calcul de certaines fonctions : Nullable, Premier
et Suivant.

6.4.1 Le predicat Nullable


Definition 6.4.1 Une forme dune grammaire est dite nullable, et lon e crit
N ullable() = V rai, si et seulement si lon peut deriver le mot vide a` partir de
(cest-`a-dire ).
Les r`egles suivantes permettent de calculer le predicat N ullable dune mani`ere
recursive :
1. N ullable() = V rai.
2. N ullable(a) = F aux, a T .
3. N ullable() = N ullable() N ullable(), , (V T ) .
4. N ullable(X) = N ullable(1 )...N ullable(n ), X 1 | 2 | ... | n .
Definition 6.4.2 Pour une grammaire G, on definit Null(G) comme e tant le sousensemble des symboles non-terminaux nullables. Formellement :
N ull(G) = {X V | N ullable(X) = V rai} = {X V | X }

88

Analyse syntaxique

Exemple 6.4.1 Soit la grammaire G definie par : T R | aT c et R bR |


On a le syst`eme suivant dequations boolee nnes :
N ullable(T ) = N ullable(R) N ullable(aT c).
N ullable(R) = N ullable() N ullable(bR).
N ullable(aT c) = N ullable(a) N ullable(T ) N ullable(c).
N ullable(bR) = N ullable(b) N ullable(R).
N ullable() = V rai.
La resolution de ce syst`eme montre que les non-terminaux T et R sont nullables :
N ullable(T ) = N ullable(R) = V rai. Par suite, Null(G) = {T, R}.

6.4.2

La fonction Premier (First)

Definition 6.4.3 Soit (V T ) une forme dune GHC G = (V, T, P, S). On


definit lensemble Premier() (ou First()), comme e tant lensemble des terminaux qui peuvent apparatre au debut dune forme derivable a` partir de . Dune
mani`ere formelle :
P remier() = {a T | a, (V T ) }
Les r`egles suivantes permettent de calculer cette fonction :
1. P remier() = .
2. P remier(a) = {a}, a T .
3. Si N ullable() = V rai, P remier() = P remier() P remier().
4. Si N ullable() = F aux, P remier() = P remier().
5. P remier(X) = P remier(1 ) ... P remier(n ), X 1 | 2 | ... | n .
Exemple 6.4.2 Soit la grammaire T R | aT c et R bR |
En appliquant les r`egles precedentes, on obtient le syst`eme suivant dequations
ensemblistes :
P remier(T ) = P remier(R) P remier(aT c).
P remier(R) = P remier(bR) P remier().

6.4. Analyse predictive

89

P remier(aT c) = P remier(a), car N ullable(a) = F aux.


P remier(bR) = P remier(b), car N ullable(b) = F aux.
P remier() = .
On en deduit alors : P remier(R) = {b} et P remier(T ) = {a, b}.

6.4.3 La fonction Suivant (Follow)


Definition 6.4.4 On definit lensemble Suivant(X) (ou Follow(X)) pour tout
symbole non-terminal X V , comme e tant lensemble des terminaux qui peuvent
apparatre juste apr`es le symbole X dans une derivation a` partir de laxiome S
de la grammaire G. Formellement :
Suivant(X) = {a T | S Xa avec , (V T ) }
Pour traiter correctement les conditions de fin de chane dentree, nous avons besoin de detecter si S N , cest-`a-dire sil existe des derivations dans lesquelles le symbole N peut e tre suivi immediatement par la fin de lentree. Pour
faciliter ce traitement, nous ajoutons une production supplementaire a` la grammaire initiale :
S 0 S$
o`u S 0 est un nouveau symbole non-terminal qui remplace laxiome S, et $ un
nouveau symbole terminal qui represente la fin de lentree. La nouvelle grammaire
est dite la grammaire augmentee de G. Nous savons que :
$ Suivant(N ) S 0 N $
ce qui est exactement le cas si S N .
Lemme 6.4.1 Soit la production M N dune GHC G, o`u M et N sont
deux symboles non-terminaux. On a :
1. P remier() Suivant(N ).
2. Si N ullable() = V rai, alors Suivant(M ) Suivant(N ).
La premi`ere propriete est e vidente, puisque vient juste apr`es le symbole nonterminal N . La seconde propriete resulte du fait que si a Suivant(M ), alors

90

Analyse syntaxique

par definition, il existe une derivation de la forme S M a. Mais, comme


M N et est nullable, alors nous pouvons e crite :
S M a N a N a
ce qui prouve que a Suivant(N ).
Si le corps dune production contient plusieurs occurrences de non-terminaux,
nous ajoutons des contraintes pour toutes ces occurrences, en fractionnant la partie droite de la production en les differents , N et .

Exemple 6.4.3 Soit la production A BcBdaAadC. Voici les 4 fractionnements possibles :


1. Premier B : = , N = B et = cBdaAadC, qui gen`ere la contrainte
{c} Suivant(B).
2. Deuxi`eme B : = Bc, N = B et = daAadC, qui gen`ere la contrainte
{d} Suivant(B).
3. Premier A : = BcBda, N = A et = adC, qui gen`ere la contrainte
{a} Suivant(A).
4. Premier C : = BcBdaAad, N = C et = , qui gen`ere la contrainte
Suivant(A) Suivant(C).

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. Analyse LL(1)

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).

6.5.1 Grammaires LL(1)


Definition 6.5.1 Une grammaire est dite LL(1), sil est possible de lanalyser en
utilisant une grammaire LL(1).
Toutes les grammaires ne sont pas des grammaires LL(1). Par exemple, une grammaire non factorisee a` gauche nest pas une grammaire LL(1). Par exemple, si
X est le symbole non-terminal a` developper et si a est le symbole de prevision et
si la grammaire contient les deux productions X aA | aB, alors le choix dune
X-production est non deterministe. Nous admettons le theor`eme suivant :
Theor`eme 6.5.1 Si une grammaire est LL(1), alors elle est non ambigue, non
recursive a` gauche et factorisee a` gauche.
La reciproque de ce theor`eme est fausse : il existe des grammaires non ambigues,
non recursives a` gauche et factorisees a` gauche, mais qui ne sont pas LL(1). Le
theor`eme suivant (admis) donne une caracterisation dune grammaire LL(1) :
Theor`eme 6.5.2 Une grammaire G est LL(1), si et seulement si, chaque fois que
X | sont deux X-productions distinctes de G, les conditions suivantes
sappliquent :
1. Premier() Premier() = : pour tout terminal a, et ne se derivent
toutes les deux en des chanes commencant par a.

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).

Exemple 6.5.1 La grammaire : T R | aT c et R bR | est LL(1).


Exemple 6.5.2 La grammaire : T R | aT c et R RbR | nest pas LL(1)
(violation de la troisi`eme r`egle, car RbR bR et bR commence par le terminal
b Suivant(R)).

6.5.2

Implementation dun analyseur LL(1)

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

Analyseur syntaxique LL(1) par descente recursive


Principe

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.

6.6. Analyseur syntaxique LL(1) par descente recursive

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 :

F IG . 6.6 Fonction exiger.


. La procedure T 0 () : le symbole non-terminal T 0 poss`ede une seule production
T 0 T $, et donc le choix est trivial. Cependant, on doit ajouter une verification
sur le prochain symbole de lentree (la variable suivant). Si suivant est
dans Premier(T 0 ) = {a, b, $}, alors on choisira la production T 0 T $. Cela
sexprime par lappel de la procedure danalayse associee au symbole T , suivi de
la verification du fin de lentree. Sinon, une erreur de syntaxe doit e tre raportee.

94

Analyse syntaxique

F IG . 6.7 Procedure associee au symbole T 0 .


. La procedure T () : il y a deux productions issues de T : T aT c et T R.
Comme Nullable(R) = V rai, alors on choisira la production T R, si
suivant Suivant(T ) ou suivant P remier(R), cest-`a-dire si suivant
{a, b, $}. Le choix de cette production se traduit alors par lappel de la
procedure danalyse associee a` R. La production T aT c sera choisie sur
le symbole de lentree a Premier(aT c). Cela se traduit par lappel de la
procedure danalyse associee a` T , mais on doit verifier le prefixe a et le suffixe c.
Dans les autres cas, une erreur de syntaxe doit e tre rapportee.

F IG . 6.8 Procedure associee au symbole T .


. La procedure R() : il y a deux R-productions : R bR et R . Comme
Nullable() = V rai, alors on choisira la production R , si suivant
Suivant(R) = {c, $}. Le choix de cette production se traduit par linstruction
vide (Ne rien faire). La production R bR sera choisie sur le symbole de lentree
b Premier(bR). Cela se traduit par lappel de la procedure danalyse associee

6.7. Analyseur syntaxique LL(1) dirige par une table

95

a` R, avant lequel on doit verifier le prefixe b. Dans les autres cas, une erreur de
syntaxe doit e tre rapportee.

F IG . 6.9 Procedure associee au symbole R.


. Une fonction parse() implementera lanalyseur syntaxique. Au depart, on initialise la variable suivant au premier symbole de lentree (le premier lex`eme
fournit) avant de lancer la procedure danalyse globale T 0 (), donc il faut realiser
une premi`ere lecture (on alors effectue un appel a` lanalyseur lexical). Ensuite,
la procedure danalyse T 0 () est appelee. Celle-ci va appeler les autres et ainsi de
suite. Sil ny a aucune erreur de syntaxe, la procedure finira par sexecuter sans
probl`eme.

F IG . 6.10 Procedure danalyse syntaxique.

6.7

Analyseur syntaxique LL(1) dirige par une table

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

pour effectuer lanalyse syntaxique.


Une table danalyse LL(1) est un tableau M a` deux dimensions (une matrice) :
. Les lignes sont indexees par les symboles non-terminaux.
. Les colonnes sont indexees par les symboles terminaux, en plus du symbole de fin de lentree $.
. Pour un symbole non-terminal X et un symbole terminal a, lentree M [X, a]
contient la r`egle de production qui sera appliquee lorsquon veut developper
X sachant que le prochain symbole de lentree est a.
Cette decision est faite exactement comme dans le cas de lanalyse syntaxique
LL(1) par descente recursive :
. La production X est dans M [X, a], si et seulement si :
a Premier(X).
a Suivant(X) si Nullable() = V rai.
Lalgorithme presente dans la figure 6.11 permet de construire la table danalyse
LL(1) dune grammaire G.
Exemple 6.7.1 Soit la grammaire definie par :
T R | aT c
R bR |
Le tableau suivant montre la table danalyse relative a` cette grammaire :
0

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

Proposition 6.7.1 Une grammaire G est LL(1), si et seulement si la table M


danalyse LL(1) relative a` G ne poss`ede que des entrees simples, cest-`a-dire
que X V , a T {$} : lentree M [X, a] contient une seule r`egle de
production ou bien elle est vide.

6.7. Analyseur syntaxique LL(1) dirige par une table

97

F IG . 6.11 Algorithme de remplissage de la table danalyse LL(1).


La presence de deux ou plusieurs productions dans une entree de la table danalyse
LL(1) montre que la grammaire nest pas LL(1). Pour une grammaire LL(1),
une entree vide dans la table danalyse LL(1) indique la presence dune erreur
syntaxique.

6.7.2 ALgorithme danalyse syntaxique LL(1) dirigee par table


Comme le montre la figure, un analyseur syntaxique predictif dirige par table
est compose de 4 e lements :

F IG . 6.12 Les e lements pour un analyseur LL(1) dirige par table.


. Un tampon dentree : contient la chane a` analyser, suivie du symbole $.

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.

Il y a trois cas possibles :


1. Si X = a = $, lanalyseur sarrete et annonce la reussite finale de lanalyse.
2. Si X = a 6= $, lanalyseur depile X et avance son pointeur dentree sur le
symbole suivant (`a droite).
3. Si X est un non-terminal, le programme consulte lentree M [X, a] de la
table danalyse M . Cette entree est soit une X-production, soit une erreur
(entree vide). Si par exemple, M [X, a] = X 1 2 ...n , lanalyseur
depile X et empile, dans lordre n , n1 , ..., 1 . On a suppose, pour simplifier, que lanalyseur se contente pour tout resultat, dimprimer la production utilisee. Cependant nimporte quelle autre action pourrait e tre executee
a` la place. Si M [X, a] = ERREU R (cas dune entree vide), lanalyseur
appelle une procedure de recuperation sur erreur.
On peut visualiser lanalyse predictive dirigee par table par un tableau a` 3 colonnes :
. La premi`ere colonne contient la configuration de la pile.
. La deuxi`eme colonne contient la configuration de la chane de lentree.
. La troisi`eme colonne contient la sortie qui est tout simplement la r`egle de
production appliquee.

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. Preparation dune grammaire pour une analyse LL(1)


Etat de la pile
$T
$cT a
$cT
$ccT a
$ccT
$ccR
$ccRb
$ccR
$ccRb
$ccR
$ccRb
$ccR
$cc
$c
$

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

Preparation dune grammaire pour une analyse


LL(1)

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

Bison est un constructeur danalyseurs syntaxiques ascendents de type LALR.


` partir dune specification dune grammaire hors-contexteen, Bison gen`ere le
A
code C dun analyseur syntaxique. Un fichier de specification Bison est un fichier texte portant lextension .y. La compilation de ce fichier seffectue par la
commande bison et produit un fichier de meme nom, mais avec une extension
.tab.c. Ce dernier contient le code C de lanalyseur. On obtient lexecutable de
lanalyseur en compilant ce dernier fichier via un compilateur C (gcc).

F IG . 7.1 Creation dun analyseur syntaxique avec Bison.


Il est possible dintegrer un analyseur lexical avec lanalyseur syntaxique genere
par Bison, soit en utilisant le compilateur Flex, soit en le programmant directement en C. Dans le cas dun analyseur lexical cree par Flex, il faut compiler le
fichier de specification avec loption -d afin de donner a` Bison la possibilite de
gerer automatiquement les codes des unites lexicales. Il faut e galement inclure le
fichier .tab.h dans le code de lanalysur Flex. Dans le cas o`u lanalyseur

102

Loutil Bison

lexical a e te e crit directment en C, alors il faut redefinir la fonction yylex.


Supposons que le code Bison est e crit dans le fichier CodeBison.y et que le code
Flex est e crit dans le fichier CodeFlex.lex. Voici comment compiler pour creer
lanalyseur syntaxique :
bison -d CodeBison.y
flex CodeFlex.lex
gcc -o Final CodeBison.tab.c lex.yy.c -ly -lfl

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

Format de specification Bison

Un fichier Bison comprend quatre sections :


%{
/*
%}
/*
%%
/*
%%
/*

Prologue : D
eclarations C */
D
eclarations Bison */
R`
egles de production de la grammaire */
Epilogue : Code C auxilliaire */

7.4. La section du prologue

103

. La section des declarations C inclue :


La declaration des types, les variables et les fonctions globales necessaires
pour e crire les actions.
Linclusion de fichiers den-tete par la commande du pre-processeur
#include.
La definition des macros par #define.
La declaration, si necessaire, des fonctions yylex (pour lanalyseur lexical e crit a` la main) et yyerror (pour reporter les erreurs syntaxique).
. La section des declarations Bison inclu :
La declaration des noms des symboles terminaux et non-terminaux.
La description de la precedence des operateurs.
La declaration des types de donnees des valeurs semantiques des differents
symboles.
. La section des r`egles de production de la grammaire decrit comment construire
chaque symbole non-terminal a` partir de ses composants.
. La section du code C auxilliaire contient nimporte quel code juge utile
pour lanalyseur, notamment la definition des fonctions declarees dans la
premi`ere section.
Remarque 7.3.1 Les commentaires / ... / et // peuvent e tre inseres dans
nimporte quelle section.

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

La section des declarations Bison


Symboles terminaux

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

7.5. La section des declarations Bison

105

qui consiste en un caract`ere individuel. Ainsi, + est utilise pour representer


le caract`ere + comme lex`eme.
3. Les tokens chanes de caract`eres : ils sont e crits comme en C en utilisant
les guillemets. Par exemple, <= est un token sous forme dune chane de
caract`eres. On peut associer un token de ce type avec un nom symbolique
comme un alias en utilisant la clause %token :
%token INFOUEGAL "<="
` defaut, lanalyseur lexical fait correspondre un code numerique au token
A
chane de caract`eres en consultant la table yytname.
Remarque 7.5.1 Les tokens caract`eres et chanes de caract`eres nont pas besoin
detre declares, sauf sil y a besoin de specifier les types de donnees de leurs
valeurs semantiques, leurs associativites, ou leurs precedences.
Bison convertira chaque declaration %token en une directive #define dans lanalyseur syntaxique afin que la fonction yylex puisse utiliser le code numerique de
chaque token. Les noms symboliques doivent e tre presents dans lanalyseur lexical. Par exemple, si lon a e crit dans Flex :
[a-zA-Z_][a-zA-Z0-9_]*
[0-9]+

return IDENT;
return NOMBRE;

alors, il faut e crire les declarations suivantes dans Bison :


%token IDENT NOMBRE
Il est possible de definir soi-meme un code numerique (decimal ou hexadecimal)
pour un token :
%token NUM
%token XNUM

300
0x12d

Cependant, il est generalement bon de laisser Bison choisir les codes numeriques
pour tous les tokens.

7.5.2

Precedence des operateurs

Bison offre des directives pour fixer la precedence et lassociativite pour les
operateurs :

106

Loutil Bison

. La directive %left permet de declarer un operateur dassociativite gauche.


. La directive %right permet de declarer un operateur dassociativite droite.
. La directive %nonassoc permet de declarer un operateur non associatif.
La priorite des differents operateurs est controllee par lordre de leurs declarations.
La premi`ere declaration %left ou %right dans le fichier donne la priorite faible
aux premiers operateurs declares, la derni`ere declaration %left ou %right donne
la priorite forte aux derniers operateurs declares. Les operateurs declares dans une
meme ligne auront la meme priorite.
Exemple 7.5.1
%left
%left
%left
%right

< > =
+ -
* /
/* 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 :

7.5. La section des declarations Bison

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

Les symboles non-terminaux

Par convention, le nom dun symbole non-terminal est e crit en minuscules.


Le nom dun symbole non-terminal est un identificateur C. Il ne faut pas donner aux symboles (terminaux ou non-terminaux) des identificateurs qui sont des
mots-cle de C.
Par defaut, le premier symbole non terminal dans la grammaire est laxiome. Si
lon veut specifier un autre symbole comme axiome, on doit e crire une directive
%start dans la section des declarations Bison. Par exemple, le code suivant :
...
%start prog
...
indique que le symbole non-terminal prog est laxiome de la grammaire.

7.5.4

Typage des symboles

Par defaut, les symboles (terminaux et non-terminaux) sont de type int. Il


est possible de le changer par un autre via la macro :
#define YYSTYPE
dans le prologue. Par exemple, si lon veut que tous les symboles soient de type
double, on ajoute la ligne suivante :

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

La section des r`egles de la grammaire

Ecriture
dune grammaire Bison

La forme generale dune r`egle de production dans Bison est :

7.6. La section des r`egles de la grammaire

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

Une action accompagne une r`egle de production et contient du code C qui


`
sera execute chaque fois quune instance de cette r`egle vient detre reconnue. A
travers une action associee a` une r`egle, il est posssible de calculer une valeur
semantique dun symbole de cette r`egle.
Une action consiste en une ou plusieurs instructions C entourees par { et } (qui
sont obligatoires, meme sil sagit dune seule instruction). Elle peut e tre placee a`
nimporte quel endroit dans le corps dune r`egle, et est executee a` cet endroit. La
plupart des actions ont juste une seule action a` la fin de la r`egle.
Le code C dans une action peut se referer aux valeurs semantiques des composants qui ont dej`a e te reconnus en utilisant la pseudo-variable $i, qui signifie
la valeur semantique du i-`eme composant de la r`egle. La valeur semantique
du tete de la r`egle est referencee par la pseudo-variable $$. Lorsque Bison copie les actions dans le fichier de lanalyseur syntaxique, il traduit chacune de ces
variables en une expression du type approprie. La pseudo-variable $$ est traduit a` une lvalue modifiable, donc elle peut employee a` gauche dune operation
daffectation. Ainsi, la r`egle :
exp : exp + exp { $$ = $1 + $3; } ;
reconnat une expression (le non-terminal exp) comme e tant une expression (le
premier non-terminal exp juste apr`es :), suivie du signe +, suivi dune expression (le second non-terminal exp). Laction asociee delivre (dans la pseudovariable $$) la valeur de la somme des valeurs des deux expressions referencees
par $1 et $3.
Remarque 7.6.2 Attention, le token + est e galement compte comme un composant de la r`egle (il est reference par la pseudo-variable $2).
Si aucune action nest specifiee pour une r`egle, Bison lui associe laction par
defaut :
$$ = $1;
Bien e videmment, laction par defaut nest valide que lorsque la tete et le premier
composant de la r`egle ont le meme type. Il ny a pas daction par defaut pour une
r`egle ayant un corps vide.

7.7. La section de lepilogue

111

7.6.3 La variable globale yylval


La valeur semantique dun token doit e tre stockee dans la variable globale
predefinie yylval. Cette variable est partagee par les deux analyseurs : lanalyseur
lexical lutilise pour stocker les valeurs semantiques des tokens et lanalyseur syntaxique lutilise pour consulter ces valeurs. Lorsque toutes les valeurs semantiques
sont tous du meme type, la variable yylval sera e galement de ce type. Ainsi, si le
type est int (type par defaut), on doit e crire le code suivant dans lanalyseur
lexical (ou dans yylex) :
...
yylval = valuer; return NOMBRE;
/* On met valeur dans la pile Bison,
puis on retourne le code du token. */
...
Lorsquon utilise plusieurs types pour les valeurs semantiques, le type de yylval
est lunion declaree en utilisant la directive %union. Par consequent, lorsquon
veut stocker la valeur dun token, on doit utiliser le nom adequat du champ de
lunion. Si par exemple, on a declare lunion comme suit :
%union {
int
intval;
double dblval;
}
alors, le code dans yylex doit e tre le suivant :
...
yylval.intval = valuer; return NOMBRE;
/* On met valeur dans la pile Bison,
puis on retourne le code du token. */
...

7.7

La section de lepilogue

Le contenu de cette section est recopie texto en fin du fichier de lanalyseur.


Si cette section est vide, on peut omettre le second symbole %%. Comme lanalyseur Bison contient lui-meme plusieurs macros et plusieurs identificateurs qui
commencent par yy or YY, alors une bonne idee est de ne pas utiliser de tels
noms dans lepilogue, a` lexception bien sur ceux cites dans le manuel dutilisation de Bison.

112

7.8

Loutil Bison

La fonction yyparse

Lanalyseur syntaxique construit par Bison contient une fonction C nommee


yyparse(). Lorsque cette fonction est appelee, elle met lanalyseur syntaxique
en marche. Elle lit les tokens delivres par lanalsyeur lexical, execute les actions
des r`egles reconnues, et se termine lorsquelle lit le token representant la fin de
de lentree ou une erreur de syntaxe non recouvrable. Le prototype de cette
fonction est :
int yyparse (void);
Selon letat de lanalyse syntaxique, la fonction yyparse retourne :
. 0, si lanalyse syntaxique se termine avec succ`es (le token fin de lentree a
e te rnecontre).
. 1, si lanalyse syntaxique se termine avec un e chec (une erreur de syntaxe,
ou une macro YYABORT a e te executee).
. 2, si lanalyse syntaxique se termine par une erreur causee par la memoire.
Il est possible dans une action associee a` une r`egle, de forcer la fonction yyparse
a` retourner une valeur en utilisant ces macros :
. YYACCEPT : retourner immediatemment la valeur 0 (reporter un succ`es).
. YYABORT : retourner immediatemment la valeur 1 (reporter un e chec).

7.9

Traitement des erreurs de syntaxe

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

7.9. Traitement des erreurs de syntaxe

113

r`egle pour reconnatre ce token dans le contexte courant, lanalyseur va continuer


son travail. On peut rajouter dans toute production de la forme A 1 | ... | n ,
une production de la forme :
A error
Dans ce cas, une r`egle de recuperation sur erreur sera traitee comme une r`egle de
production classique. On pourra donc lui associer une action semantique contenant un message derreur. D`es quune erreur est rencontree, tous les symboles
sont avales jusqu`a rencontrer le token correspondant a` . Par exemple :
instr : error ;
indique a` lanalyseur qu`a la vue dune erreur, il doit sauter jusquau del`a du
prochain symbole ; et supposer quun instr vient detre reconnue. La routine
yyerrok replace lanalyseur dans le mode normal de fonctionnement cest-`a-dire
que lanalyse syntaxique nest pas interrompue.

7.9.1

Un exemple

On va illustrer ce traitement des erreus en montrant un analyseur destine a`


verifier interactivement la saisie de donnees. Les donnees sont composees dun
nom suivi dun entier sur une ligne. En sortie, les donnees de chaque ligne sont
rangees de la facon suivante : lentier, un symbole :, et le nom. Voici le code
Bison de cet analyseur syntaxique (Fichier parser.y) :
%{
#include <stdio.h>
extern FILE* fp;
void yyerror(char*);
%}
%union {
int intval;
char* strval;
}
%token <intval> ENTIER
%token <strval> NOM

114

Loutil Bison

%%
axiome : entree { puts("Cest fini"); }
;
entree : ligne
| entree ligne
;
ligne

: NOM ENTIER \n { fprintf(fp, "%d:%s\n", $2, $1); }


| error \n { printf("Refaire : "); } 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); }