Support Comp Il
Support Comp Il
Support Comp Il
Techniques de
Compilation
Ait-Aoudia Samy
PG/0842
Sommaire
i
II.6.6. Utilisation des états d’analyse : exemple ........................................................... 36
II.7. Exercices................................................................................................................... 38
CHAPITRE III. Principes de l'Analyse Syntaxique ......................................... 41
III.1. Introduction ............................................................................................................. 41
III.2. Les grammaires non-contextuelles .......................................................................... 41
III.3. Dérivations et langages............................................................................................ 42
III.3.1. Dérivations ....................................................................................................... 42
III.3.2. Langages .......................................................................................................... 44
III.3.3. Arbre de dérivation .......................................................................................... 44
III.3.4. Ambiguïté ......................................................................................................... 45
III.4. Transformations de Grammaires non-contextuelles ................................................ 46
III.4.1. Propriétés particulières d'une grammaire ......................................................... 46
III.4.2. Elimination des symboles inutiles .................................................................... 46
III.4.3. Elimination des productions unitaires .............................................................. 48
III.4.4. Rendre une grammaire ε-libre .......................................................................... 48
III.4.5. Substitution des non-terminaux ........................................................................ 49
III.5. Principe des analyses descendantes et ascendantes ................................................. 50
III.6. Backus–Naur Form (BNF) ...................................................................................... 51
III.7. Exercices ................................................................................................................. 52
CHAPITRE IV. Analyse syntaxique descendante............................................. 55
IV.1. Introduction ............................................................................................................. 55
IV.2. Analyse syntaxique LL ............................................................................................ 55
IV.2.1. Analyse syntaxique LL(1) ................................................................................ 56
IV.2.2. Analyse syntaxique LL(k) ................................................................................ 65
IV.3. Analyse syntaxique par descente récursive ............................................................. 67
IV.3.1. Conditions préalables à une descente récursive ............................................... 67
IV.3.2. Ecriture des procédures .................................................................................... 67
IV.3.3. Exemple d'analyse ............................................................................................ 68
IV.4. Exercices ................................................................................................................. 71
CHAPITRE V. Analyses syntaxiques ascendantes .......................................... 75
V.1. Analyse par précédence d'opérateurs ........................................................................ 75
V.1.1. Grammaire d'opérateurs..................................................................................... 75
V.1.2. Relations de précédence d'opérateurs ................................................................ 75
V.1.3. Grammaire de précédence d'opérateurs ............................................................. 76
V.1.4. Algorithme d'analyse de précédence d'opérateurs ............................................. 77
V.1.5. Exemple d'analyse ............................................................................................. 78
V.2. Analyse par précédence simple ................................................................................ 78
V.2.1. Relations de précédence simple ......................................................................... 79
V.2.2. Grammaire de précédence simple ...................................................................... 79
V.2.3. Algorithme d'analyse de précédence simple ...................................................... 79
ii
V.2.4. Exemple d'analyse ............................................................................................. 80
V.2.5. Optimisation ...................................................................................................... 82
V.2.6. Transformations de grammaires simples ........................................................... 84
V.3. Analyse par précédence faible .................................................................................. 85
V.3.1. Grammaires de précédence faible...................................................................... 85
V.3.2. Analyse par précédence faible ........................................................................... 85
V.3.3. Exemple d'analyse ............................................................................................. 86
V.3.4. Optimisation ...................................................................................................... 87
V.4. Analyse par la méthode LR(k) .................................................................................. 88
V.4.1. Analyse Contextuelle ......................................................................................... 88
V.4.2. Méthode pratique pour la construction d'analyseurs LR .................................... 98
V.4.3. Utilisation des grammaires ambiguës .............................................................. 114
V.4.4. Gestion des erreurs en analyse LR................................................................... 116
V.4.5. Classification des grammaires ......................................................................... 117
V.5. Exercices ................................................................................................................ 119
CHAPITRE VI. Traduction Dirigée par la Syntaxe ....................................... 125
VI.1. Langages intermédiaires ........................................................................................ 125
VI.1.1. Notation post-fixée ......................................................................................... 125
VI.1.2. Qudruplets ...................................................................................................... 126
VI.1.3. Triplets et Triplets indirects ........................................................................... 127
VI.1.4. Arbres abstraits .............................................................................................. 128
VI.2. Définitions dirigées par la syntaxe ........................................................................ 128
VI.2.1. Attributs des symboles de la grammaire......................................................... 128
VI.2.2. Définitions n'utilisant que des attributs synthétisés ........................................ 129
VI.2.3. Définitions utilisant des attributs hérités ........................................................ 130
VI.3. Schémas de traduction........................................................................................... 131
VI.3.1. Définition ....................................................................................................... 131
VI.3.2. Conception d'un schéma de traduction ........................................................... 131
VI.3.3. Méthodologie de génération de code intermédiaire ....................................... 132
VI.4. Traduction descendante......................................................................................... 132
VI.4.1. Emplacement des routines sémantiques ......................................................... 132
VI.4.2. Cas général ..................................................................................................... 133
VI.4.3. Conception d'un traducteur descendant .......................................................... 134
VI.5. Traduction ascendante........................................................................................... 142
VI.5.1. Emplacement des routines sémantiques ......................................................... 142
VI.5.2. Définitions L-attribuées.................................................................................. 142
VI.5.3. Elimination des actions intérieures................................................................. 142
VI.5.4. Schéma de traduction générant des quadruplets ............................................. 143
VI.6. YACC ................................................................................................................... 149
VI.6.1. Grammaires YACC ........................................................................................ 149
VI.6.2. Structure d'un programme YACC .................................................................. 149
iii
VI.6.3. Exemple de programme YACC...................................................................... 151
VI.6.4. Variables et commandes de YACC ................................................................ 152
VI.6.5. Fonctionnement de l'analyseur ....................................................................... 152
VI.7. Exercices ............................................................................................................... 158
CHAPITRE VII. Environnements d'exécution ............................................... 163
VII.1. Introduction ......................................................................................................... 163
VII.2. Procédures et activations ..................................................................................... 163
VII.2.1. Procédures .................................................................................................... 163
VII.2.2. Arbre d'activation.......................................................................................... 165
VII.2.3. Pile de contrôle ............................................................................................. 166
VII.3. Organisation de l'espace mémoire........................................................................ 167
VII.3.1. Répartition de la mémoire à l'exécution ........................................................ 167
VII.3.2. Bloc d'activation ........................................................................................... 168
VII.4. Allocation de la mémoire ..................................................................................... 169
VII.4.1. Allocation statique ........................................................................................ 169
VII.4.2. Allocation en pile .......................................................................................... 170
VII.4.3. Allocation dans le tas .................................................................................... 173
VII.5. Accès aux noms non locaux ................................................................................. 174
VII.5.1. Blocs ............................................................................................................. 174
VII.5.2. Portée statique sans déclaration de procédures imbriquées .......................... 175
VII.5.3. Portée statique avec déclaration de procédures imbriquées .......................... 176
VII.5.4. Portée dynamique ......................................................................................... 177
VII.6. Passage de paramètres ......................................................................................... 177
VII.6.1. Passage par valeur......................................................................................... 178
VII.6.2. Passage par référence .................................................................................... 178
VII.6.3. Passage par copie-restauration ...................................................................... 178
VII.6.4. Passage par nom ........................................................................................... 179
VII.7. Exercices .............................................................................................................. 180
CHAPITRE VIII. Production de Code.......................................................... 183
VIII.1. Introduction ........................................................................................................ 183
VIII.2. Machine Cible .................................................................................................... 183
VIII.3. Blocs de base et graphes de flot de contrôle ....................................................... 185
VIII.3.1. Blocs de Base .............................................................................................. 185
VIII.3.2. Transformations sur les blocs de Base......................................................... 187
VIII.3.3. Construction du graphe de flot de contrôle.................................................. 188
VIII.4. Un générateur de Code simple............................................................................ 190
VIII.4.1. Informations d'utilisation ultérieure ............................................................. 190
VIII.4.2. Descripteurs de registres et d'adresses ......................................................... 191
VIII.4.3. Algorithme de production de code .............................................................. 192
VIII.4.4. Production de code pour d'autres types d'instructions ................................. 194
iv
VIII.4.5. Production de code à partir de DAG ........................................................... 195
VIII.4.6. Assignation globale de registres .................................................................. 205
VIII.5. Exercices ............................................................................................................ 209
BIBLIOGRAPHIE ............................................................................................. 212
ANNEXE A. Classification des Grammaires ................................................... 213
ANNEXE B. Formes Normales des Grammaires ............................................ 214
v
I. Introduction aux Compilateurs
I.1. TRADUCTION
Les programmes qui convertissent un programme de l'utilisateur écrit en
un langage quelconque en un programme écrit dans un autre langage sont
appelés des traducteurs.
Le langage dans lequel on écrit le programme originel est la langage
source, tandis que le langage après conversion est le langage cible (ou
langage objet).
Si le langage source est un langage de haut niveau (par exemple C ou
Pascal) et le langage objet est de bas niveau (langage machine) alors un tel
traducteur est appelé compilateur (figure I.1).
programme programme
→ COMPILATEUR →
source objet
Autres traducteurs
Lorsque le langage source est essentiellement une représentation
symbolique du langage d'une machine, le traducteur est appelé un
assembleur.
Le pré-processeur désigne un traducteur qui converti un programme
écrit dans un langage de haut niveau en un programme équivalent dans
un autre langage de haut niveau.
1
I. Introduction aux Compilateurs
Un interprète (ou interpréteur) est un traducteur qui ne génère pas un
exécutable comme un compilateur mais qui exécute les instructions du
programme source l'une après l'autre avec les données correspondantes.
Les langages LISP (LISt Processing) ou BASIC sont des langages
interprétés.
Remarque
Dans ce cours on utilisera le terme "Compilateur" au sens large i.e. les
notions étudiées peuvent être appliquées à d'autres traducteurs.
programme source
↓
Analyse
lexicale
↓
Analyse
syntaxique
↓
Analyse
sémantique
Table des gestion
↓
symboles des erreurs
code
intermédiaire
↓
optimisation
de code
↓
génération
de code
↓
programme objet
Figure I.2. Structure d’un compilateur.
2
I. Introduction aux Compilateurs
3
I. Introduction aux Compilateurs
Exemple :
• Considérons la grammaire suivante des expressions arithmétiques :
E→E+T|T
T→T*F|F
F → (E) | id
et soit à analyser la phrase suivante : A + B * C.
Analyse
A+B*C → → id1 + id2 * id3
lexicale
T T * F
F F id
id id
Figure I.3. Arbre syntaxique
4
I. Introduction aux Compilateurs
L’expression arithmétique (id1 + id2 * id3) peut être traduite en code à
trois adresses de la façon suivante (temp1 et temp2 sont des temporaires):
temp1 := id2 * id3
temp2 := id1 + temp1
I.2.6. Optimisation
Cette phase tente d’améliorer le code intermédiaire pour réduire le temps
d’exécution ou l’occupation mémoire. Quelques unes des opérations
d’optimisation sont :
• Déplacement de code invariant :
le but est d’extraire du corps de la boucle les parties du code qui sont des
invariants (nécessitent une seule exécution). Le nombre total d’instructions
exécutées est diminué.
• Elimination du code inutilisé :
le compilateur sait reconnaître les parties du code qui ne sont pas utilisées (la
raison peut être une erreur de programmation).
• Elimination des sous-expressions communes :
dans le cas où la valeur d’une expression est recalculée en plusieurs endroits
du programme, l’optimiseur gère le résultat de cette expression et le réutilise
plutôt que de refaire le calcul.
Dans la table des symboles, les étiquettes auront les valeurs suivantes
(tableau I.2) :
Identificateur Valeur Autres informations
Label 1 0
Label 2 10
TOTO 27
. .
. .
. .
6
I. Introduction aux Compilateurs
Lorsque la deuxième passe commence, les valeurs de tous les symboles
sont connues de telle sorte qu'il ne subsiste plus de référence en avant et que
chaque instruction puisse être traduite.
Dans cette seconde méthode, la traduction est faite en une seule passe :
lorsqu'on rencontre une instruction qui contient une référence en avant, on
ne génère pas de sortie, mais on met à jour une table indiquant que cette
instruction n'a pas été traduite. A la fin de la lecture du programme source et
donc lorsque tous les symboles ont été définis, on peut traduire ces
instructions.
Le problème est que si le programme contient beaucoup de référence en
avant, la table des instructions en attente de traduction peut devenir énorme,
au point de ne plus tenir en mémoire. Un compilateur à une passe est
également plus complexe qu'un compilateur à deux passes.
Remarques :
i. Les informations contenues dans la table des symboles sont collectées
durant les étapes d’analyse lexicale et syntaxique. Durant l’analyse
lexicale, chaque fois qu’un nom est rencontré, on le recherche dans la
table des symboles. S’il n y est pas déjà alors il est inséré. C’est durant
l’analyse syntaxique que les informations sont insérées.
ii. Ces informations sont utilisées durant l’analyse sémantique, la génération
de code, l’optimisation du code et la détection des erreurs.
iii. Le programme objet considère les objets liés aux noms et ignore les noms
qui les ont introduits. A la traduction chaque nom est remplacé par l’objet
à l’exécution qu’il désigne.
Il existe plusieurs méthodes pour structurer et gérer cette table des
symboles qui, toutes, tentent de simuler une mémoire associative, c'est à dire
un ensemble de doublés (symbole, valeur). L'objectif est de pouvoir accéder
rapidement à la table dans le but de rechercher ou insérer une information.
Lorsqu'on recherche un symbole, une procédure parcourt la table jusqu'à
trouver le symbole correspondant. Cette méthode est simple à programmer,
mais lente. Une autre méthode est de gérer la table des symboles par ordre
alphabétique et d'utiliser un algorithme de recherche dichotomique. Cette
méthode est plus rapide que la recherche linéaire.
Une autre façon de procéder est l'adressage dispersé (hash coding). Cette
méthode implique d'avoir une fonction de dispersion qui "mappe" les
symboles sur un intervalle d'entiers de 0 à k-1.
La fonction de dispersion peut être, par exemple, réalisée en multipliant
entre eux les codes ASCII des caractères composant le symbole, en ignorant
les dépassements de capacité éventuels et en prenant le résultat modulo k
(on peut imaginer n'importe qu'elle autre fonction, pourvu que la dispersion
obtenue soit à peu près uniforme).
Les symboles sont alors stockés dans une table comportant k
emplacements numérotés de 0 à k-1. Il se peut que lorsqu'on applique la
fonction de dispersion à plusieurs symboles, on obtienne la même valeur i.
On parle alors de problème de collision. Les symboles ayant une même clé
peuvent être stockés dans une liste chaînée à laquelle on accédera à partir de
l'entrée i de la table. Avec n symboles et k entrées, la longueur moyenne de
la liste sera n/k. Cette technique de hash coding est illustrée à la figure 1.4 (k
= 5).
8
I. Introduction aux Compilateurs
(a) (b)
Figure I.4. Suites d’instructions : (a) sans macro (b) avec macro
9
I. Introduction aux Compilateurs
Lorsque le compilateur rencontre une définition de macro, il l'enregistre
dans une table et chaque fois que le nom de la macro apparaît, il le remplace
par le corps de la macro. l'opération de remplacement est appelée expansion
de macro.
L'expansion se fait pendant la phase de traduction et non pendant la phase
d'exécution du programme. Les programmes des figures 1.5.a et 1.5..b
produisent le même code en langage machine.
10
2. Analyse Lexicale
I.1. INTRODUCTION
II.1.1.1. Chaîne
Définitions
a. Un alphabet (dénoté généralement par le symbole Σ) est un ensemble
fini de symboles.
b. Une chaîne est séquence finie de symboles (le terme mot est également
utilisé pour désigner une chaîne)
c. La longueur d’une chaîne ω est notée |ω| est désigne le nombre de
symbole de la chaîne ω.
d. La chaîne vide est notée ε et est de longueur 0.
11
2. Analyse Lexicale
Exemples :
Σ={0,1} est un alphabet
100010 est une chaîne de longueur 6
|100010|=6
II.1.1.2. Concaténation
Définition
Soient ϖ et ψ deux chaînes. La concaténation de ϖ et de ψ notée ϖ.ψ
ou ϖψ est la chaîne formée des symboles de ϖ suivis des symboles de ψ.
Exemples :
abc.de = abcde
ε.ϖ = ϖ.ε = ϖ
ϖ1 = ϖ et ϖ2 = ϖ.ϖ
ϖi : concaténation i fois de la chaîne ϖ
ϖ0 = ε
II.1.1.3. Langage
Définitions
a. On utilise le terme langage pour désigner un ensemble de chaînes
formées à partir d’un alphabet spécifique.
b. La concaténation de deux langages L et M notée L.M ou LM est définie
comme suit : L.M = {ϖψ | ϖ ∈ L et ψ ∈ M}.
c. L’union de 2 langages L et M est définie par L∪M = {ϖ| ϖ∈L ou
ϖ∈M}
d. Les opération de fermeture et de fermeture positive d’un langage L sont
notées L* et L+ est sont définies comme suit :
∞
L* = ∪ Li
i =0
L = L.Li
+
Exemples :
L={0,01,110} et M={10,110}
alors L.M = {010,0110,01110,11010,110110}
Li : concaténation i fois de L
L0 = {ε}
12
2. Analyse Lexicale
L.{ε} = {ε}.L = L
Exemples :
∞
a*= ∪ {ai } i.e. l’ensemble des chaînes de zéro ou plusieurs a.
i =0
Remarque
L'opérateur de concaténation . est souvent omis dans l'écriture des
expressions régulières. Le fait de faire suivre deux expressions sans
séparateur signifie leur concaténation.
II.2.1. Formalisme
Définition :
• Un automate fini M est un quintuple (Q, Σ, δ, q0, F) où :
∑ est un alphabet;
Q est un ensemble fini d'états;
δ: Q × Σ → Q est la "fonction" de transition;
q0 est l'état initial;
F est un ensemble d'états finaux.
Propriété :
• Le langage L(M) reconnu par l'automate M est l'ensemble { w | δ (q0, w)
∈ F} des mots permettant d'atteindre un état final à partir de l'état initial
de l'automate.
14
2. Analyse Lexicale
15
2. Analyse Lexicale
Remarques :
Les automates d'états finis non déterministes sont plus facile à obtenir
que les automates déterministes.
Mais il est "difficile" de simuler un automate d'états finis non
déterministe par un programme simple.
a b
{q0} {q1} {q2}
{q1} {q3} -
{q2} - {q2, q4}
{q3} {q4} {q3}
{q4} - -
17
2. Analyse Lexicale
Algorithme :
D-états : ensemble d'états de l'AFD à construire ;
D-Trans : la table de transition de l'AFD ;
Initialement ε-fermeture(e0) unique état de D-états et il est
non marqué ;
Tant que ∃ un état non marqué T dans D-états
Faire marquer T ;
Pour chaque symbole d'entrée a
Faire U:= ε-fermeture (Transiter (T, a));
Si U ∉ D-états
Alors ajouter U à D-états et U est non marqué
FinSi;
D-Trans [T, a] := U;
Fait;
Fait;
L'état initial de l'AFD est ε-fermeture(e0);
Un état f est un état final de l'AFD si ∃ g ∈ f et g état final
de l'AFN.
18
2. Analyse Lexicale
Exemple :
Transformation de l'automate d'états finis non déterministe suivant
correspondant à l'expression régulière a*b | b*a en un automate
déterministe :
a
b
1 3
ε
0
ε
2 4
a
b
Figure II.3. Automate non déterministe de a*b | b*a.
Construction de l'AFD :
√ ε-fermeture (0) = {0, 1, 3}
√ Table de transition de l'AFD donnée ci après :
a b
{0, 1, 3} {1, 4} {2, 3}
{1, 4} {1} {2}
{2, 3} {4} {3}
{1} {1} {2}
{2} - -
{4} - -
{3} {4} {3}
√ Etat initial : ε-fermeture (0) = {0, 1, 3}
√ Etats finaux : {1, 4}, {2, 3}, {2}, {4}
√ Table de transition de l'AFD avec états renommés :
a b
S T U
T V X
U Y Z
V V X
X - -
Y - -
Z Y Z
19
2. Analyse Lexicale
a Y
b
U a
b Z
b
Remarque :
L'automate d'états finis déterministe AFD obtenu ainsi n'est pas
forcément minimal en nombre d'états. Il faudrait donc minimiser le
nombre d'états de l'AFD par algorithme pour que la table de transition
sur machine soit la plus petite possible.
20
2. Analyse Lexicale
Algorithme de "minimisation" :
i) Construire une partition initiale ∏ des états de l'AFN avec 2
groupes ; les états d'acceptation et les autres états ;
ii) Obtenir une nouvelle partition ∏' par :
Pour chaque groupe G de ∏
Faire
iii) Si ∏' = ∏ Alors aller à iv) Sinon ∏ = ∏' ; aller à ii) FinSi ;
iv) L'état de départ de l'AFD est le représentant du groupe qui
contient l'état de départ de l'AFN ; les états d'acceptation de l'AFD
sont les représentants des états finaux de l'AFN;
v) Supprimer de l'AFD les états non accessibles.
Application 1:
Considérer l’automate d’états finis déterministe suivant :
a
a
S T
a a
b
b
U V
b b
La table de transition de l’AFD précédent est la suivante où S est l’état
initial et les états T et V sont finaux.
a b
S T U
T T V
U T U
V T V
21
2. Analyse Lexicale
S,U T,V
Remarque :
Il est à noter que dans cet exemple la première partition entre états finaux et
états non finaux a suffi pour obtenir l’AFD minimal.
Application 2:
Considérer la table de transition suivante d’un automate d’états finis
déterministe. Les états sont numérotés de 0 à 5. L’état 0 est l’état initial. Les
états 1, 3 et 5 sont les états finaux. Les transitions se font sur les caractères c
et p. La table est présentée de manière à faciliter la compréhension de
l’algorithme de minimisation.
c p
0 1 4
2 3 -
4 5 -
1 1 2
3 3 -
5 5 -
22
2. Analyse Lexicale
Remarques :
c
c
A C
a
p p
c
B D
c
L’utilisation d’un AFD ou d’un AFD minimal pour analyser un mot d’un
langage donné induit le même nombre de transitions.
L’utilisation de l’AFD minimal implique un gain en espace mémoire et
éventuellement en temps d’exécution si la table de transition non
"minimisée" entraîne des défauts de page par exemple.
23
2. Analyse Lexicale
r1 r2
I1 F1I2 F2
Application :
a. Construire l’AFN correspondant à l’expression régulière suivante par la
construction de Thompson : (a|b)* b b*
ε
a ε
3 4
ε ε ε ε ε
1 2 ε
7 8 9 10 11 12
b b
ε 5 6
b ε
ε
24
2. Analyse Lexicale
a S2
a b
S1 a
b S4
b b
S3
a
a b
S1,S2 S3,S4
25
2. Analyse Lexicale
26
2. Analyse Lexicale
a-z
a-z a-z
e1 e2
0-9
a-z
0-9 e3
-
e4
-
27
2. Analyse Lexicale
LEX
lex.yy.c *.c
routine
routines C
yylex()
Analyseur
Figure II.5. Génération d’analyseur lexical avec LEX
• Langage Lex :
√ permet de définir des expressions régulières. Il peut donc être utilisé
pour définir des unités lexicales qui sont spécifiées par des
expressions régulières.
Programme Lex :
√ constitué d ’un ensemble d ’expressions régulières écrites dans le
langage Lex.
√ est mis dans un fichier avec l’extension .l (exemple : scan.l).
28
2. Analyse Lexicale
Compilateur Lex :
√ génère l ’analyseur lexical à partir du programme Lex
√ exécuté à l ’aide de la commande lex (exemple : lex scan.l)
√ La commande lex génère le code C de l'analyseur qui est mis dans
un fichier lex.yy.c
√ Le fichier lex.yy.c est soumis au compilateur C pour générer le
code objet de l'analyseur
√ Peut être utilisé seul ou en conjonction avec YACC.
29
2. Analyse Lexicale
Déclarations
%%
Règles de traduction
%%
Procédures auxiliaires
30
2. Analyse Lexicale
i) bloc littéral
commence par %{ et se termine par %}
contient des déclarations et définitions en C
est copié tel quel dans le fichier lex.yy.c produit par la commande
lex
les définitions et déclarations qu’il contient sont globales au programme
produit par lex
Exemple :
%{
#include "calc.h"
#include <stdio.h>
#include <stdlib.h>
%}
ii) définitions
Associations d’identificateurs à des expressions régulières
Permettent de compacter l'écriture des expressions régulières.
Exemple :
separ [ \t\n]
espace {separ}+
lettre [A-Za-z]
chiffre [0-9]
ident {lettre}({lettre}|{chiffre})*
nbre {chiffre}+(\.{chiffre}+)?(E[+\-]?{chiffre}+)?
Remarque :
Utilisation des noms d’expressions régulières déjà définies entre
accolades {}
31
2. Analyse Lexicale
Exemple :
%start etat1 etat2 ….
√ Où etat1, état2 … sont les états possibles de Lex
√ Le fonctionnement de Lex avec le basculement entre états sera
traité ultérieurement.
32
2. Analyse Lexicale
Cas d’ambiguïtés :
En cas d’ambiguïté dans la reconnaissance (plusieurs chaînes peuvent
être reconnues par une même expression régulière) LEX choisit la plus
longue chaîne.
Exemple :
o Expression ‘.*’ et si l’entrée est :
‘first’ quoted string here, ‘second’ here
o LEX reconnaît ‘first’ quoted string here, ‘second’
Placer les cas spécifiques avant les cas généraux.
Exemple :
o integer action pour mot-clé
o [a-z]+ identificateur
33
2. Analyse Lexicale
/* définitions régulières */
delim [ \t\n]
bl {delim}+
lettre [A-Za-z]
chiffre [0-9]
id {lettre}+({lettre}|{chiffre})*
nombre {chiffre}+(\.{chiffre}+)?(E[+-]?{chiffre}+)?
35
2. Analyse Lexicale
%%
{bl} {/* pas d’action et pas de retour */}
si {return(SI);}
alors {return(ALORS);}
sinon {return(SINON);}
{id} {yylval = RangerId();return(ID);}
{nombre} {yylval = RangerNb();return(NB);}
"<" {yylval = PPQ;return(OPREL);}
"<=" {yylval = PPE;return(OPREL);}
"=" {yylval = EGA;return(OPREL);}
"<>" {yylval = DIF;return(OPREL);}
">" {yylval = PGQ;return(OPREL);}
">=" {yylval = PGE;return(OPREL);}
%%
RangerId()
{
/* procédure pour ranger dans la table des symboles l’entité lexicale dont le
premier caractère est pointé par yytext et dont la longueur est yyleng et
retourner un pointeur sur son entrée */
}
RangerNb()
{
/* procédure similaire pour ranger une entité lexicale qui est un nombre */
}
36
2. Analyse Lexicale
Exemple :
%start normal const_alpha
%%
<normal>[a-zA-Z]+ {printf(``identificateur : % s \n``,yytext);}
<normal>\`` {BEGIN const_alpha;}
<const_alpha>[a-zA-Z]+ {printf(``chaine : %s \n``,yytext);}
<const_alpha>\`` {BEGIN normal;}
<normal,const_alpha>. { /* aucune action */ }
<normal,const_alpha>\n { /* aucune action */ }
%%
main()
{ yyin = fopen(``toto.e``, ``r``);
yyout = fopen(``toto.s``, ``w``);
BEGIN normal;
yylex(); /* yylex appelé une seule fois car pas de return */
flcose(yyin);
fclose(yyout);
}
37
2. Analyse Lexicale
II.7. EXERCICES
Exercice 1.
• Donner un automate d’états finis déterministe pour chacun des langages
suivants sur l’alphabet {0,1} :
a) l’ensemble de toutes les chaînes commençant par 1 et qui interprétées
comme la représentation binaire d’entiers sont divisibles par quatre.
b) Ensemble des chaînes de 0 et de 1 avec un nombre impair de 0 et un
nombre pair de 1.
c) Ensemble des chaînes de 0 et de 1 qui ne contiennent pas la sous-chaîne
011.
Exercice 2.
• Considérer l’expression régulière suivante :
(a|b)* a b*
a) Construire un automate d’états finis non déterministe pour l’expression
régulière précédente en utilisant la construction de Thompson.
b) Transformer cet automate d’états finis non déterministe en un automate
d’états finis déterministe.
c) Minimiser le nombre d'états de l'automate obtenu.
Exercice 3.
• Les entités lexicales d’un mini-langage de programmation sont les
suivantes :
Mots-clés begin, end, if, then, else
Identificateurs chaînes composées d’une lettre suivie de zéro ou
plusieurs lettres ou chiffres
Constantes chaînes composées d’un chiffre suivi de zéro ou
plusieurs chiffres
Opérateurs <, < =, =, < >, >, > =, +, -
38
2. Analyse Lexicale
Exercice 4.
• Les règles de la construction de Thompson transforment une expression
régulière R1 en un automate d’états finis N1. Proposer des règles
analogues de construction d’automates d’états finis non déterministes
pour les opérateurs suivants :
R1+
R1? (dont la signification est R1|εε)
• On modifie la règle de Thompson de construction de l'automate pour
l'expression R* en ne rajoutant pas un état initial et un état final (on
rajoute juste deux transitions étiquetées ε, l'une de l'état final de
l'automate de R vers l'état initial de cet automate et l'autre de l'état initial
vers l'état final). Cette règle est-elle toujours valable pour l'expression
R* ? Dans le cas général (composition de règles), la modification
proposée affecte-elle la validité des constructions ? Donner un exemple
concis pour justifier votre réponse.
Exercice 5.
• On veut reconnaître les entités d’un langage L formées des lettres
alphabétiques, des chiffres et des tirets ‘-’. Un mot de L présente les
caractéristiques suivantes :
• il doit commencer obligatoirement par une lettre;
• il doit contenir au moins 2 caractères;
• il ne doit pas contenir 2 tirets consécutifs ni 2 chiffres consécutifs;
• il ne doit pas finir par un tiret;
• le nombre de chiffres est inférieur au nombre de lettres;
• la longueur d’un mot n’excède pas 20 caractères.
a) Peut-on contrôler la longueur des mots par un automate ? Que doit-on
contrôler par automate et que doit-on contrôler par programme ?
b) Donner l’automate déterministe qui accepte les mots du langage L.
c) Ecrire un programme d’analyse lexicale en vous aidant d’un automate.
Exercice 6.
• Comment reconnaître les mots des langages suivants :
a) Toutes les chaînes de lettres contenant les voyelles (a, e, i, o, u) dans
l’ordre (ex : cradetillotum, aceitou).
39
2. Analyse Lexicale
b) Toutes les chaînes de lettres contenant des lettres par ordre croissant
dans l’alphabet (ex : city, not, bel).
Exercice 7.
• Ecrire un programme Lex qui :
1. convertit un texte écrit en majuscules en minuscules,
2. supprime les blancs et tabulations en fin de ligne,
Exercice 8.
• Ecrire un programme Lex qui a en entrée un fichier de nombres entiers
et qui en sortie ajoute la valeur 3 à tous les entiers divisibles par 7. Les
entiers non divisibles par 7 ne sont pas transformés.
Indication :
Utiliser la fonction atoi() qui convertit une chaîne de caractères "chiffres"
en sa valeur numérique.
Exercice 9.
a) Que reconnaît Lex avec l'expression régulière '.*' sur l'entrée suivante et
pourquoi ?
'first' quoted string here, 'second' here
b) Si on voulait reconnaître d'abord 'first', quelle expression régulière
définir ?
Exercice 10.
• Soit un texte comprenant des mots sur les différents caractères du code
ASCII. La taille des mots alphabétiques (composées des lettres de
l'alphabet {a,b,…, z}) ne dépasse pas 20 caractères.
Question :
• Ecrire un programme Lex qui donne l'histogramme des mots i.e. pour
chaque longueur de mot on donne le nombre de mots présents dans le
texte de cette longueur.
40
3. Principes de l'Analyse Syntaxique
III.1. INTRODUCTION
L'objectif d'une analyse syntaxique est de reconnaître si un
programme donné (le programme source dont les entités lexicales ont été
codées) appartient au langage engendré par une grammaire hors-contexte
(de type 2 dans la classification de Chomsky). On appelle également ces
grammaires des grammaires non-contextuelles ou en terme anglo-saxon des
grammaires Context-Free.
L'utilisation des grammaires non-contextuelles dans la phase
d'analyse syntaxique est motivée par le fait que les langages de
programmation actuels admettent tous des grammaires Context-Free pour
les générer. L'analyseur syntaxique pourra donc être "construit" de manière
efficace et automatique. L'automate à pile est de fait sous-jacent à toutes les
analyses syntaxiques.
Il est à préciser que si on pouvait obtenir une grammaire régulière
pour engendrer un langage de programmation, la phase d'analyse syntaxique
n'en sera que plus facilitée (utilisation d'automate d'états finis au lieu de
l'automate à pile) mais ce n'est pas le cas pour les langages de
programmation.
Avant d'étudier les méthodes d'analyses syntaxiques, il est utile de
faire quelques rappels sur les grammaires non-contextuelles et les langages
algébriques. Nous donnerons ensuite le principe général des méthodes
d'analyse syntaxique.
Définition :
Une grammaire non-contextuelle G est un quadruplet <N,T,P,S,> où
N : ensemble fini non vide de symboles appelés symboles non
terminaux,
41
3. Principes de l'Analyse Syntaxique
Exemple :
La grammaire G suivante décrit les expressions arithmétiques :
√ G = <{E}, {+,-,*,/,(,),id}, P, E>
√ P: E→E+E
E→E-E
E→E*E
E→E/E
E→(E)
E → id
L'ensemble des productions peut être réécrit de la manière suivante :
√ P : E → E + E | E – E | E * E | E / E | ( E ) | id
III.3.1. Dérivations
42
3. Principes de l'Analyse Syntaxique
Exemple :
Considérer la grammaire suivante :
G = <{S,A}, {a,b}, P, S>
P : S → aAS | a
A → SbA | SS | ba
43
3. Principes de l'Analyse Syntaxique
III.3.2. Langages
Etant donné une grammaire G, on appelle langage engendré par G et on
le note L(G) le langage :
L(G) = { ω | ω ∈ T* et S ⇒+ ω }
Exemple :
Soit la grammaire G = <N,T,P,S,> avec :
N = {S} ; T = {a, b} ;
P = { S → aSb ; S → ab } ;
L(G) = { anbn | n ≥ 1 }
S b A a
a b a
Figure III.1. Arbre de dérivation.
III.3.4. Ambiguïté
Une grammaire non-contextuelle est dite ambiguë si une chaîne ω (∈
T*) possède deux (ou plus) arbres de dérivation.
Exemple :
√ Soit G = <{E}, {+,-,*,/,(,),id}, P, E>
√ P : E → E + E | E – E | E * E | E / E | ( E ) | id
E E
E + E E * E
id E * E E + E id
id id id id
45
3. Principes de l'Analyse Syntaxique
46
3. Principes de l'Analyse Syntaxique
Transformation 2 :
Transformer une grammaire G=<N,T,P,S> en
G'=<N',T',P',S> tel que pour chaque X ∈ (N' ∪ T') ∃ α,
β ∈ (N' ∪ T') et S ⇒* αXβ.
Algorithme :
Placer S dans N'; S est non marqué;
While ∃ un non-terminal A non marqué dans N' et A → α1| α2 | …| αm
Do
Begin
ajouter tous les non-terminaux de α1, α2, …, αm à N';
ajouter tous les terminaux de α1, α2, …, αm à T';
End;
P' est l'ensemble des productions contenant des symboles de (N' ∪ T').
Exemple
Elimination des symboles inutiles de la grammaire dont les productions
sont données ci-après:
S→A|B
A → aB | bS | ε
B → AB | BCc
C → AS | ε
Après l'application de la transformation 1, on obtient la grammaire dont
les productions sont données ci-après :
S→A
A → bS | ε
C → AS | ε
Après l'application de la transformation 2, on obtient la grammaire finale
sans symboles inutiles dont les productions sont données ci-après :
S→A
A → bS | ε
47
3. Principes de l'Analyse Syntaxique
48
3. Principes de l'Analyse Syntaxique
iii) Si (S ∈ Nε)
Alors Si S n'apparaît dans un aucun membre droit de production
Alors Ajouter à P' la production S → ε
Sinon Créer symbole Z (nouvel axiome) et ajouter production Z→S|ε
FinSi;
FinSi;
Lemme 2
Soit G=(N,T,P,S) une grammaire de type 2 (Context-Free).
Soient A→Aα1|Aα2|…|Aαn les A-productions où A est le terme le plus à
gauche des MDP (Membre Droit de Production).
Soient A→ β1| β2| …βs les autres A-productions.
Soit G1=(N∪{A’},T,P1,S) obtenue en remplaçant les A-productions par :
A→βi | βiA’ (1 ≤ i ≤ s) et A’→ αj | αjA’ (1 ≤ j ≤ n)
Alors L(G) = L(G1)
Remarques :
Deux grammaires différentes générant exactement le même langage sont
dites équivalentes.
Lors de l’analyse syntaxiques, une grammaire peut avoir de "meilleures"
propriétés qu’une grammaire équivalente.
49
3. Principes de l'Analyse Syntaxique
Analyse descendante :
On part de l'axiome de la grammaire pour retrouver le programme
source en effectuant des dérivations successives. Si on se place dans
l'arbre syntaxique représentant le programme source, cette stratégie
revient à partir de la racine (l'axiome) et à descendre vers les feuilles
représentant les symboles terminaux.
Analyse ascendante :
On part du programme source pour retrouver l'axiome de la grammaire
en effectuant des dérivations successives inverses. On va cette fois partir
des feuilles de l'arbre syntaxique pour remonter vers la racine.
Exemple
Soit la grammaire G dont les productions sont données ci-après et la
chaîne ω=abba à analyser syntaxiquement :
S → aA | bB
A → aBS | bS
B → bB | a
√ Le processus d'analyse descendante de la chaîne ω=abba peut illustré
par :
S ⇒ aA ⇒ abS ⇒ abbB ⇒ abba
√ Le processus d'analyse ascendante de la chaîne ω=abba peut illustré
par :
abba ⇐R abbB ⇐R abS ⇐R aA ⇐R S
où ⇐R désigne est une dérivation inverse
50
3. Principes de l'Analyse Syntaxique
Définition :
Une spécification BNF est un ensemble de règles de dérivation définie
par :
<symbole> ::= expression1 | expression2 | … | expressionN
où
<symbole> est un non-terminal, et expressioni est une suite de symboles de
la grammaires (terminaux et non-terminaux et éventuellement le mot vide)
Les symboles qui n’apparaissent jamais en membre gauche de production
sont des terminaux. Les symboles non terminaux sont toujours entre < >
Exemple :
<Expression> ::= <Expression> "+" <Terme>
| <Expression> "-" <Terme>
| <Terme>
<Terme> ::= <Terme> "*" <Facteur>
| <Terme> "/" <Facteur>
| <Facteur>
<Facteur> ::= "(" <Expression> ")"
| "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" |
51
3. Principes de l'Analyse Syntaxique
III.7. EXERCICES
Exercice 1.
a) Ecrire un programme en langage C qui permet de rendre une grammaire
ε–libre.
b) Rendre la grammaire G = <{S}, {a,b}, P, S> ε-libre. Les règles
productions P sont données ci-après.
P : S → aSbS | bSaS | ε
Exercice 2.
• Considérer la grammaire G=(N,T,P,S) dont les productions sont données
ci-après :
P : <Instr> → if <Condition> then <Instr> else <Instr>
| if <Condition> then <Instruction>
| begin <LI> end
| i
<Condition> → c
<LI> → <LI> ; i | i
a) Montrer que la grammaire G précédente est ambiguë en donnant deux
arbres syntaxiques pour le fragment de programme suivant :
if c then if c then i else i.
b) Comment pourra-t-on faire une analyse syntaxique "déterministe" pour
ce type d'instructions (instructions conditionnelles if).
Exercice 3.
Soit la grammaire G=({S,A,B},{a,b,c},P,S).
P: S → aSA | cA
A → cA | a
B → bB | b
a) Eliminer les symboles inutiles de la grammaire précédente.
b) Donner le langage engendré par la grammaire épurée.
c) Donner les dérivations les plus à gauche de la chaîne : aacaaca.
d) Donner les dérivations les plus à droite de la chaîne : aacaaca.
52
3. Principes de l'Analyse Syntaxique
Exercice 4.
• Une grammaire G = (T, N, P, S) sous forme normale de Chomsky
(FNC) est une grammaire dont toutes les productions sont de la
forme :
A → BC ; A → a
où a est un terminal et A, B, C sont des non terminaux
a) Montrez que toute grammaire G de type 2 peut être une grammaire sous
FNC.
b) Transformez la grammaire suivante en grammaire sous FNC :
S→A/B
A → aB / bS / ε
B → AB / BCc
c) Quel peut être l'intérêt de l'utilisation des grammaires sous FNC en
analyse syntaxique ?
Exercice 5.
Une grammaire G = (T, N, P, S) sous forme normale de Greibach (FNG)
est une grammaire dont toutes les productions sont de la forme :
A → aα α , α ∈ N*
où a est un terminal et α une chaîne de non terminaux
a) Montrez que toute grammaire G de type 2 peut être une grammaire sous
FNG.
b) Transformez les grammaires suivantes en grammaire sous FNG :
S → Ab / b / Sa
A → a / AS / Sb
Exercice 6.
Soit la grammaire des expressions arithmétiques G=({E},{+,-
,*,/,(,),i},P,E) dont les productions sont données ci-après (le terme i
représente un identificateur) :
P: E → E + E | E - E | E * E | E / E | (E) | i
a) Montrer que la grammaire précédente est ambiguë en donnant deux
arbres syntaxiques pour la chaîne : i + i * i.
b) "Interpréter" les deux arbres syntaxiques dans le sens "comment va être
évaluée l'expression suivante : i + i * i ? "
53
3. Principes de l'Analyse Syntaxique
54
4. Analyse syntaxique descendante
IV.1. INTRODUCTION
55
4. Analyse syntaxique descendante
Remarque :
Le nombre k est dans la majorité des cas égal à 1, car pour si k > 1
l'analyse devient moins intéressante. C'est pour cette raison que nous
détaillerons l'analyse LL(1) et nous survolerons l'analyse LL(k).
Remarque:
Ces règles sont appliquées jusqu'à ce qu'aucun terminal ni ε ne puisse
être ajouté aux ensembles DEBUT.
56
4. Analyse syntaxique descendante
Ensemble SUIVANT :
• Soit G une grammaire non contextuelle G=<N,T,S,P> et X un non-
terminal appartenant à N, alors :
SUIVANT(X) = {a | S ⇒* αXaβ, a ∈ T ∪ {#} et α,β ∈(T ∪ N)*}.
Calcul de l'ensemble SUIVANT :
• L'algorithme suivant montre comment calculer un ensemble SUIVANT :
SUIVANT(X)
i) Mettre # (qui est le marqueur de fin de l'entrée à analyser) dans
SUIV(S) où S est l'axiome de la grammaire ;
ii) S'il y a une production A → αXβ
Alors ajouter DEBUT(β) sauf ε à SUIVANT(X) ;
iii) S'il y a une production A → αX
ou une production A → αXβ avec ∈ DEBUT(β)
Alors ajouter SUIVANT(A) à SUIVANT(X) ;
Remarque:
Ces règles sont appliquées jusqu'à ce qu'aucun terminal ni # ne puisse
être ajouté aux ensembles SUIVANT.
Exemple 1 :
Calculer les ensembles DEBUT et SUIVANT pour les non-terminaux de
la grammaire dont les productions sont données ci-après :
S → aSSA | ε
A → aSb | b
B → bB | ε
DEBUT SUIVANT
S aε #ab
A ab #ab
B bε #ab
Exemple 2 :
Calculer les ensembles DEBUT et SUIVANT pour les non-terminaux de
la grammaire dont les productions sont données ci-après :
57
4. Analyse syntaxique descendante
S → ABSb | ε
A → aBb | b
B → bB | cS | ε
DEBUT SUIVANT
S abε #ba
A ab bca
B bcε ab
DEBUT(α.SUIVANT(A)) ∩ DEBUT(β.SUIVANT(A)) = ∅
58
4. Analyse syntaxique descendante
59
4. Analyse syntaxique descendante
Pile X
Y Programme Flot de Sortie
Z d'analyse
#
Table
d'analyse M
Figure IV.1. Schéma d'un analyseur LL(1).
Algorithme d'analyse :
i) Initialement la pile contient # et au-dessus l'axiome de la grammaire ;
ii) Soit X le symbole en sommet de pile et a le symbole d'entrée courant ;
iii)Si X = a = #, l'analyseur s'arrête et annonce la réussite finale de l'analyse
;
iv) Si X est un terminal ≠( a = #), l'analyseur s'arrête et signale une erreur ;
v) Si (X = a) ≠ #, l'analyseur enlève X de la pile et avance son pointeur de
flot d'entrée sur le symbole suivant ;
vi) Si X est un non-terminal
Alors consulter l'entrée de la table M[X,a]
Si M[X,a] = {A → α}
l'analyseur enlève X du sommet de la pile ;
et empile les symboles de α de droite à gauche
(si α = UVW alors empiler W, V et U dans cet ordre) ;
Si M[X,a] = "erreur" l'analyseur appelle une procédure de récupération
sur erreur.
vii) Aller à iii)
60
4. Analyse syntaxique descendante
DEBUT SUIVANT
E ( id #)
E' +ε #)
T ( id +#)
T' *ε +#)
F ( id *+#)
√ Table d'analyse pour la grammaire précédente :
+ * ( ) id #
E E → T E' E → T E'
E' E' → + T E' → ε E' → ε
E'
T T → F T' T → F T'
T' T' → ε T' → * F T' → ε T' → ε
T'
F F → (E) F → id
Tableau IV.1. Table d'analyse LL(1).
61
4. Analyse syntaxique descendante
62
4. Analyse syntaxique descendante
63
4. Analyse syntaxique descendante
64
4. Analyse syntaxique descendante
√ Etape 1:
Elimination de la récursivité gauche directe
S → A b S' | b S'
S' → b S' | ε
√ Etape 2:
Substitution
A → a | A S | A b S' b | b S' b
Elimination de la récursivité gauche directe
A → a A' | b S' b A'
A' → S A' | b S' b A' | ε
65
4. Analyse syntaxique descendante
Ensemble SUIVANT-k :
• Soit G une grammaire non contextuelle G=<N,T,S,P> et X un non-
terminal appartenant à N, alors :
SUIVANT(X) = { ω| S ⇒* αXωβ,
où ω ∈ (T ∪ {#})k, |ω| = k, et α,β ∈(T ∪ N)*}.
DEBUT-k(α.SUIVANT-k(A)) ∩ DEBUT-k(β.SUIVANT-k(A)) = ∅
66
4. Analyse syntaxique descendante
Définition :
L'analyse syntaxique par descente récursive n'est en fait que la version
récursive (au sens implémentation informatique) de l'analyse LL(1).
C'est à dire qu'au lieu de manipuler la pile explicitement, celle-ci sera
gérée implicitement lors des appels. L'analyseur (programme d'analyse)
est constitué d'une suite de procédures.
Pour faire une analyse syntaxique par descente récursive pour analyser
les mots d'un langage L(G), la grammaire G doit vérifier les conditions
LL(1) i.e.
Pour toute paire de règles de G tel que A → α | β on a :
DEBUT(α.SUIVANT(A)) ∩ DEBUT(β.SUIVANT(A)) = ∅
Soit G une grammaire vérifiant les conditions LL(1). Les étapes suivantes
montrent le principe d'écriture l'ensemble des procédures de l'analyseur :
A chaque non terminal de la grammaire correspond une procédure ;
On ajoute la règle de production suivante : Z → S # où S est l'axiome de
la grammaire et # le marqueur de fin de chaîne.
On utilisera les variables tc et ts pour désigner, respectivement, le
symbole courant du flot d'entrée à analyser et son symbole suivant dans
ce flot ;
67
4. Analyse syntaxique descendante
DEBUT SUIVANT
S aε #
A ca b
La grammaire G précédente vérifie les conditions LL(1).
Ecriture des procédures :
Procédure Z( )
Début
S( ) ;
Si tc ='#"
Alors "Chaîne syntaxiquement correcte"
Sinon "Erreur"
FinSi ;
Fin.
68
4. Analyse syntaxique descendante
Procédure S( )
Début
Si tc = 'a'
Alors
tc = ts ;
A( ) ;
Si tc = 'b'
Alors tc = ts
Sinon "Erreur"
FinSi ;
FinSi ;
Fin.
Procédure A( )
Début
Si tc = 'c'
Alors
tc = ts ;
A( ) ;
Sinon
Si tc = 'a'
Alors tc = ts
Si tc = 'b'
Alors tc = ts
Sinon "Erreur"
FinSi
Sinon "Erreur"
FinSi
FinSi
Fin.
69
4. Analyse syntaxique descendante
70
4. Analyse syntaxique descendante
IV.4. EXERCICES
Exercice 1.
Soient les grammaires G1 et G2 suivantes :
G1. S → aASb / bBSa / a
A → aA / ε
B → bB / abS
A → aA / ε
Exercice 2.
• Eliminer la récursivité à gauche dans les grammaires suivantes :
S → Aa / SSb / ε
A → Ba / Sb
B → Ab / Bba / a
Exercice 3.
• On définit la grammaire G=(N,T,P,S) suivante :
P: <bloc> → programme <LD> début <LI> fin
<LD> → <LD> ; d | d
<LI> → <LI> ; i | i
71
4. Analyse syntaxique descendante
Exercice 4.
• On définit la grammaire G=(N,T,P,S) suivante :
P: <Instr> → if <Condition> then <Instr> else <Instr>
| if <Condition> then <Instr>
| begin <LI> end
| i
<Condition> → c
<LI> → <LI> ; i | i
c) La grammaire G précédente est-elle LL(1) ? Que doit-on faire pour
essayer de transformer la grammaire G en grammaire LL(1) ? Votre
grammaire transformée est–elle LL(1) ? Pourquoi ?
d) Construire la table d'analyse LL(1) pour votre grammaire transformée.
e) Lever la multidéfinition de votre table d'analyse en utilisant la
convention du langage Pascal pour le traitement des instructions if
imbriquées.
f) Analyser la chaîne suivante : if c then if c then i else i.
Exercice 5.
• On définit la grammaire G=(N,T,P,S) suivante :
P: S → Aa | b
A → Ac | Sd |e
a) Ecrire l’algorithme d’analyse syntaxique qui reconnaît les mots du
langage L(G) par la méthode de la descente récursive.
b) Analyse la chaîne suivante : e adca
Exercice 6.
• On définit la grammaire Gi,j=({S,A,B},{a,b,c},P,S). i et j sont des entiers
positifs ou nuls.
P: S → aSbia | bA
A → bBb | ε
B → cB | bjc
72
4. Analyse syntaxique descendante
Exercice 7.
En s'inspirant de la méthode LL, on désire écrire un compilateur utilisant
une méthode descendante pour un langage de programmation en arabe.
Le compilateur est écrit dans un langage classique (C par exemple) et à
chaque étape de l'analyse on utilise la dérivation la plus à gauche.
a) Quel nom pourrais-t-on donner à la méthode d'analyse si on s'inspire
de la signification de LL? Quelles conditions doit vérifier la
grammaire pour faire une analyse descendante déterministe sans
retour arrière ?
On désire maintenant modifier le compilateur précédent pour qu'à
chaque étape de l'analyse on utilise la dérivation la plus à droite.
b) Quel nom pourrais-t-on donner à la méthode d'analyse si on s'inspire
de la signification de LL? Quelles conditions doit vérifier la
grammaire pour faire une analyse descendante déterministe sans
retour arrière ?
c) Appliquer les conditions et transformations décrites précédemment
pour faire une analyse descendante déterministe (construction de la
table d'analyse) reconnaissant les mots du langage engendrés par la
grammaire suivante :
G: E → T+E|T
T → T*F|F
F→ (E)|γ
a) Analyser la chaîne : 3γ * 2γ + 1γ et donner son arbre syntaxique.
73
4. Analyse syntaxique descendante
Exercice 8.
• Soit la grammaire G=({E},{,∧,∨,(,),i},P,E) :
P: E → E | E ∧ E | E ∨ E | (E) | i
74
5. Analyses syntaxiques ascendantes
CHAPITRE V. ANALYSES
SYNTAXIQUES ASCENDANTES
INTRODUCTION
L'analyse syntaxique ascendante est également appelée analyse par
décalage/ réduction. Dans ces analyses on tente de remonter vers l'axiome
de la grammaire depuis le programme d'entrée à analyser.
Nous abordons ce chapitre par les méthodes les plus simples pour
terminer avec la méthode la plus "élaborée".
75
5. Analyses syntaxiques ascendantes
76
5. Analyses syntaxiques ascendantes
+ * i ( ) #
+ > < < < > >
* > > < < > >
i > > > >
( < < < < =
) > > > >
# < < < <
Tableau V.1. Table des relations précédence d'opérateurs.
77
5. Analyses syntaxiques ascendantes
Remarques :
i) Pour pouvoir faire une analyse de précédence d'opérateurs, on a
imposé des restrictions sur les grammaires. Don la classe de grammaires
qui admettent une telle analyse est restreinte.
ii) Les non terminaux de la grammaire sont implicitement pris en
compte dans l'analyse de précédence d'opérateurs
78
5. Analyses syntaxiques ascendantes
…X… … S1 X … …XY…
… S1 S2 … S2 …….. … S1 S2 …
S1 = S2 S1 < S2 S1 > S2
Théorème :
Si une grammaire G est de précédence simple alors G est non ambiguë.
79
5. Analyses syntaxiques ascendantes
80
5. Analyses syntaxiques ascendantes
S A ( ) a #
S > > >
A = < < = =
( > > >
) > > >
a > > >
# < <
Tableau V.2. Table des relations de précédence simple.
81
5. Analyses syntaxiques ascendantes
V.2.5. Optimisation
Remarque :
L'opération d'optimisation concernant l'analyse par précédence simple
décrite ci-après concerne la réduction de l'espace mémoire nécessaire
pour stocker la table des relations. Cette table sera d'une certaine
manière compactée.
Principe :
Au lieu de garder la table des relations, on associera à chaque symbole
de la grammaire a, deux valeurs numériques désignées par fa et ga. Les
valeurs f et g seront choisies de telle sorte que si a < b (resp. =, >) dans
la table des relations alors fa < gb (resp. =, >). Au cours de l'analyse par
précédence simple, on testera alors la relation entre f(sommet_pile) et
g(terme_courant).
Avantage de l'utilisation des valeurs f et g :
Avec les entiers f et g on utilisera 2n cases mémoires alors qu'avec la
table des relations n2 cases mémoires sont nécessaires. En terme de
temps d'exécution, l'utilisation des valeurs f et g engendrera une
82
5. Analyses syntaxiques ascendantes
exécution plus rapide si la table des relations (grande taille) n'est que
partiellement en RAM alors la table des valeurs f et g (petite taille) est
entièrement en RAM.
Désavantage de l'utilisation des valeurs f et g :
Avec les entiers f et g, il existera toujours une relation entre le sommet
de pile et le terme courant. De ce fait, la détection des erreurs sera un
peu différée.
Comment trouver les valeurs fa et ga pour chaque symbole a de G ?
Etape 1 : Construction du graphe des symboles f et g
Parcourir la table des relations :
Si a = b alors alors fa et gb appartiennent à un même sommet du
graphe. S'il n'a pas de relation d'égalité, chaque symbole f ou g sera
un sommet distinct du graphe.
Si a < b alors tracer un arc partant du groupe de gb vers le groupe de
fa.
Si a > b alors tracer un arc partant du groupe de fa vers le groupe de
gb.
Etape 2 : Obtenir les valeurs des symboles f et g
Si le graphe des symboles contient un circuit alors pas de valeurs
possibles pour f et g.
Si le graphe ne contient pas de circuit alors :
- associer à fa la longueur du plus grand chemin commençant par le
groupe de fa.
- associer à ga la longueur du plus grand chemin commençant par le
groupe de ga.
Comment exploiter efficacement le graphe pour obtenir les valeurs
de f et g :
Tri topologique sur les sommets du graphe (algorithme linéaire) :
i) Utiliser un compteur initialisé à 0 ;
ii) Chercher les sommets du graphe n'ayant pas d'arcs sortants ;
iii) Attribuer la valeur du compteur aux symboles de ces sommets ;
iv) Supprimer ces sommets du graphe avec tous les arcs qui y entrent
;
v) Incrémenter le compteur ;
vi) Si graphe "non vide" alors aller à ii).
Exemple :
83
5. Analyses syntaxiques ascendantes
Remplacer la table des relations de la section 2.4 par une table de valeurs
f et g.
√ Graphe des symboles f et g :
fS gS ga g)
gA
fS fa f(
f)
g( f#
g#
84
5. Analyses syntaxiques ascendantes
Cas X > = a :
X = a i.e. ∃ A ∈ N | A → αXaβ, (α,β) ∈ (N ∪ T)* et a ∈ T
X > a i.e. ∃ B ∈ N | B → α'ZYβ', X ∈ DERNIER(Z) et a ∈
DEBUT(Y)
Transformation pour éliminer la relation = :
Transformer la règle A → αXaβ en A → A'aβ et A' → αX
Remarque :
Il faudra s'assurer après ces transformations qu'on a pas deux MDP
(membres droits de production) identiques.
S A b d a #
S > >
A =
b = < <
d = < > <
a = < > <= >
# < <
Tableau V.3. Table des relations.
86
5. Analyses syntaxiques ascendantes
Remarques :
Lors d'une analyse par précédence faible, il est inutile d'empiler le
symbole < car c'est la seule relation qui peut être présente dans la pile.
Si on devait ignorer la dernière condition pour qu’une grammaire soit de
précédence faible alors on aura des incohérences lors de l’analyse.
L’exemple suivant montre un tel cas.
Soit G une grammaire dont les productions sont :
o S → BA
o A → Ba | a
o B→b
Si on forçait l’analyse de la chaîne "ba" avec l’algorithme de
précédence faible on aura un blocage alors que la chaîne "ba"
appartient à L(G).
V.3.4. Optimisation
Remarque :
L'opération d'optimisation est similaire à celle décrite dans l'analyse
précédence simple.
Comment trouver les valeurs fa et ga pour chaque symbole a de G ?
Etape 1 : Construction du graphe des symboles f et g
Parcourir la table des relations :
Chaque symbole f ou g sera un sommet du graphe.
Si a < b alors tracer un arc partant du sommet gb vers le sommet fa.
Si a > b alors tracer un arc partant du sommet fa vers le sommet gb.
Transformation du graphe :
Chercher des sommets indépendants et les regrouper (2 sommets
sont indépendants s'il n'existe aucun chemin d'un sommet à l'autre);
Aucun groupe ne doit contenir des successeurs et des prédécesseurs
d'un autre groupe.
Etape 2 : Obtenir les valeurs des symboles f et g
Si le graphe des symboles contient un circuit alors pas de valeurs
possibles pour f et g.
Si le graphe ne contient pas de circuit alors :
- associer à fa la longueur du plus grand chemin commençant par le
groupe de fa.
- associer à ga la longueur du plus grand chemin commençant par le
groupe de ga.
87
5. Analyses syntaxiques ascendantes
Remarque :
L'opération de constitution des groupes sera faite de telle sorte à pouvoir
le plus possible regrouper des symboles f et g dans un même groupe (ces
symboles auront alors une même valeur numérique). Le but de cette
opération est d'accélérer la détection des erreurs (on a réintroduit des
relations = alors qu'elles n'existent pas dans l'analyse par précédence
faible).
Exemple :
Donner les contextes gauches et droits des règles de production de la
grammaire G=<{S,A,B},{a,b},S,P> dont les productions sont données
ci- après :
Règle Contexte gauche Contexte Droit
S → AB AB #
A → aA a*aA b+ #
A→a a*a b+ #
B → bB Ab*bB #
B→b Ab*b #
90
5. Analyses syntaxiques ascendantes
Théorèmes :
i) Si une grammaire G est LR(k) alors G est LR(k+1).
ii) Si une grammaire G est LL(k) alors G est LR(k).
Remarques :
Si la condition donnée en définition (section 5.1.3) n'est pas vérifiée
alors peut y avoir une indécision sur l'action à entreprendre avec un
contenu de pile et k symboles de prévision.
Si par exemple un contexte LR(k) est préfixe d'un autre contexte LR(k)
alors il y aura une indécision entre un décalage et une réduction. Quand
le plus contexte gauche est présent en pile doit-on effectuer une
réduction ou attendre que le plus grand contexte gauche soit en pile
(donc faire au moins un décalage supplémentaire) pour faire la réduction
?
Comment construire un analyseur LR(k) :
i) Trouver les contextes LR(k) de la grammaire ;
ii) Construire un automate d'états finis déterministe qui reconnaît les
contextes LR(k) ;
iii) Transcrire cet automate en table d'analyse ;
iv) Algorithme d'analyse qui utilise cette table pour décider des actions à
entreprendre.
Exemple :
√ La grammaire dont les productions sont données ci-après est LR(1) (voir
section 5.1.3) ; les productions sont numérotées pour les désigner avec
ces numéros :
(1) S → AB
(2) A → aA
(3) A → a
(4) B → bB
(5) B → b
91
5. Analyses syntaxiques ascendantes
A B #
0 2 3 R(1
)
b
b # R(5
)
a 6 B
a #
7 R(4
)
b
4 R(3
)
A
b
5 R(2
)
les numéros de règles correspondent aux contextes LR(1).
Figure V.2. Automate reconnaissant les contextes LR(1).
92
5. Analyses syntaxiques ascendantes
Pile Sn
Xn Programme Flot de
Sn-1 d'analyse LR Sortie
S0
ACTION SUCCESSEUR
Remarques :
93
5. Analyses syntaxiques ascendantes
94
5. Analyses syntaxiques ascendantes
Remarque :
Le programme d'analyse LR est le même pour toutes les analyses. Seules
les tables d'analyses changent.
Exemple :
En utilisant la table d'analyse de la section 5.1.4 analyser les chaîne
"aaabb" et "aabab" pour vérifier si elles appartiennent au langage
engendré par la grammaire dont les productions sont données ci-après :
(1) S → AB
(2) A → aA
(3) A → a
(4) B → bB
(5) B → b
Pile Restant de la chaîne Action
(sommet à droite) à analyser
0 aabab# D4
0a4 abab# D4
0a4a4 bab# R "A → a"
0a4A5 bab# R "A → aA"
0A2 bab# D6
0A2b6 ab# "Echec"
95
5. Analyses syntaxiques ascendantes
E→E+T|T
T→T*F|F
F→i
√ La grammaire G précédente est SLR(1).
SUIVANT
E #+
T #+*
F #+*
Règle Contextes gauche Contextes SLR(1)
E→E+T E+T E+T#
E+T+
E→T T T#
T+
T→T*F E+T*F E+T*F#
E+T*F+
E+T*F*
T*F T*F#
T*F+
T*F*
T→F E+F E+F#
E+F+
E+F*
F F#
F+
F*
F→i i i#
i+
i*
E+i E+i#
E+i+
E+i*
T*i T*i#
T*i+
T*i*
E+T*i E+T*i#
E+T*i+
E+T*i*
Tableau V.5. Table des contextes SLR(1).
97
5. Analyses syntaxiques ascendantes
Exemple :
Considérer la grammaire G dont les productions sont données :
E→E+T|T
T→T*F|F
F → (E) | i
√ La grammaire est augmentée de la règle E' → E ;
√ Fermeture ([E' → .E]) = { [E' → .E], [E → .E+T], [E → .T],
[T → .T*F], [T → .F], [F → .(E)], [F → .i] }
99
5. Analyses syntaxiques ascendantes
Exemple :
Construire la collection canonique pour la grammaire augmentée G'
dont les productions sont données :
E' → E
E→E+T|T
T→T*F|F
F → (E) | i
100
5. Analyses syntaxiques ascendantes
101
5. Analyses syntaxiques ascendantes
Remarques :
Si la table d'analyse ainsi construite est mono-définie alors la grammaire
est SLR(1). On pourra alors faire une analyse SLR(1).
L'état initial de l'analyseur est celui construit à partir de l'ensemble
d'items contenant l'item [S' → .S].
V.4.2.1.6. Exemples
Exemple 1 :
Construire la table d'analyse SLR(1) pour la grammaire augmentée G'
dont les productions numérotées sont données ci après :
E' → E
(1) E → E + T
(2) E → T
(3) T → T * F
(4) T → F
(5) F → (E)
(6) F → i
√ Calcul des ensembles SUIVANT :
SUIVANT
E +)#
T +)*#
F +)*#
√ La collection canonique étant déjà calculée, on obtient la table d'analyse
SLR(1) suivante :
+ * ( ) i # E T F
0 D4 D5 1 2 3
1 D6 Accepter
2 R (2) D 7 R (2) R (2)
3 R (4) R (4) R (4) R (4)
4 D4 D5 8 2 3
5 R (6) R (6) R (6) R (6)
6 D4 D5 9 3
7 D4 D5 10
8 D6 D 11
9 R (1) D 7 R (1) R (1)
10 R (3) R (3) R (3) R (3)
11 R (5) R (5) R (5) R (5)
Tableau V.6. Table d'analyse SLR(1).
102
5. Analyses syntaxiques ascendantes
103
5. Analyses syntaxiques ascendantes
104
5. Analyses syntaxiques ascendantes
[A → α.β
β, a]
où A → αβ est une production et a le symbole de prévision.
Remarques :
Un item LR(1) de la forme [A → α, a] implique de réduire par la
production A→α uniquement lorsque le prochain symbole d'entrée est a.
Avec un item LR(1) de la forme [A → α.β β, a] avec β≠ε, la prévision
n'a aucun effet.
Dans un item [A → α.β β, a], l'ensemble des symboles de prévision ⊆
SUIVANT(A).
105
5. Analyses syntaxiques ascendantes
Remarques :
Si la table d'analyse ainsi construite est mono-définie alors la grammaire
est LR(1). On pourra alors faire une analyse LR(1).
L'état initial de l'analyseur est celui construit à partir de l'ensemble
d'items contenant l'item [S' → .S, #].
Exemple
V.4.2.2.6.
Construire la table d'analyse LR(1) pour la grammaire augmentée G'
dont les productions numérotées sont données ci après :
S' → S
(1) S → AA
(2) A → aA
(3) A → b
√ Calcul de la collection canonique des ensembles d'items LR(1) :
- I0 = {[S' → .S, #], [S → .AA, #], [A → .aA, a/b], [A → .b, a/b]}
- I1 = GOTO(I0, S) = {[S' → S., #]}
- I2 = GOTO(I0, A) = {[S → A.A, #], [A → .aA, #], [A → .b, #]}
- I3 = GOTO(I0, a) = {[A → a.A, a/b], [A → .aA, a/b], [A → .b, a/b]}
- I4 = GOTO(I0, b) = {[A → b., a/b]}
- I5 = GOTO(I2, A) = { [S → AA., #]}
- I6 = GOTO(I2, a) = {[A → a.A, #], [A → .aA, #], [A → .b, #]}
- I7 = GOTO(I2, b) = {[A → b., #]}
- I8 = GOTO(I3, A) = {[A → aA., a/b]}
- GOTO(I3, a) = I3
- GOTO(I3, b) = I4
- I9 = GOTO(I6, A) = {[A → aA., #]}
- GOTO(I6, a) = I6
- GOTO(I6, b) = I7
√ Table d'analyse LR(1):
a b # S A
0 D3 D4 1 2
1 Accepter
2 D6 D7 5
3 D3 D4 8
4 R (3) R (3)
5 R (1)
6 D6 D7 9
7 R (3)
8 R (2) R (2)
9 R (2)
107
5. Analyses syntaxiques ascendantes
LALR(k)
SLR(k)
principe). Ce n'est pas cet algorithme qui sera implémenté pour obtenir les
tables LALR.
i) Construire C = {I0, I1, …, In} la collection canonique d'items LR(1) ;
ii) Rechercher les ensembles d'items ayant même cœur ; Les fusionner en
un seul état ;
iii) Soit C' = {J0, J1, …, Jm} la nouvelle collection d'items ;
L'opération GOTO est obtenue de la façon suivante :
Si J = I0 ∪ I1 ∪ … ∪ Is
Alors K = ∪ GOTO(Ii,X) , i=1..s ;
GOTO (J,X) = K ;
FinSi ;
iv) Les réductions sont obtenues en examinant C'.
Application :
Construire la table d'analyse LALR(1) pour la grammaire LR(1) de la
section 5.2.1.6.
√ Regroupement des états ayant même cœur :
- Les états I3 et I6 donnent l'état I36= {[A → a.A, a/b/#], [A → .aA,
a/b/#], [A → .b, a/b/#]}
- Les états I4 et I7 donnent l'état I47= {[A → b., a/b/#]}
- Les états I8 et I9 donnent l'état I89= {[A → aA., a/b/#]}
√ Table d'analyse LALR(1) :
a b # S A
"0" D "36" D "47" "1" "2"
"1" Accepter
"2" D "36" D "47" "5"
"36" D "36" D "47" "89"
"47" R (3) R (3)
"5" R (1)
"89" R (2) R (2)
Théorème :
Si la table d'analyse LALR(1) d'une grammaire G est mono-définie alors
G est LALR(1).
Remarques :
i) Un analyseur LR détecte une erreur le plus tôt au cours de l’analyse.
109
5. Analyses syntaxiques ascendantes
110
5. Analyses syntaxiques ascendantes
112
5. Analyses syntaxiques ascendantes
113
5. Analyses syntaxiques ascendantes
* = i # S L R
0 D4 D5 1 2 3
1 Accept
er
2 D6 R(5)
3 R(2)
4 D4 D5 8 7
5 R(4) R(4)
6 D4 D5 8 9
7 R(3) R(3)
8 R(5) R(5)
9 R(1)
114
5. Analyses syntaxiques ascendantes
Items LR(0) :
√ I0 = {[E' → .E], [E → .E+E], [E → .E*E], [E → .(E)], [E → .i]}
√ I1 = GOTO(I0, E) = {[E' → E.], [E → E.+E], [E → E.*E]}
√ I2 = GOTO(I0,() = {[E→(.E)], [E→.E+E], [E → .E*E], [E → .(E)], [E
→ .i]}
√ I3 = GOTO(I0, i) = {[E → i.]}
√ I4 = GOTO(I1, +) = {[E → E+.E], [E→.E+E], [E → .E*E], [E → .(E)],
[E → .i]}
√ I5 = GOTO(I1, *) = {[E → E*.E], [E→.E+E], [E → .E*E], [E → .(E)],
[E → .i]}
√ I6 = GOTO(I2, E) = {[E → (E.)], [E → E.*E], [E→E.+E]}
√ GOTO(I2, () = I2
√ GOTO(I2, i) = I3
√ I7 = GOTO(I4, E) = {[E → E+E.], [E → E.*E], [E→E.+E]}
√ GOTO(I4, () = I2
√ GOTO(I4, i) = I3
√ I8 = GOTO(I5, E) = {[E → E*E.], [E → E.*E], [E→E.+E]}
√ GOTO(I5, () = I2
√ GOTO(I5, i) = I3
√ I9 = GOTO(I6, )) = {[E → (E).]}
√ GOTO(I6, +) = I4
√ GOTO(I6, *) = I5
√ GOTO(I7, +) = I4
√ GOTO(I7, *) = I5
√ GOTO(I8, +) = I4
√ GOTO(I8, *) = I5
Calcul de l'ensemble SUIVANT :
SUIVANT
E *+)#
115
5. Analyses syntaxiques ascendantes
i + * ( ) # E
0 D3 D2 1
1 D4 D5 Accepter
2 D3 D2 6
4 D3 D2 7
5 D3 D2 8
6 D4 D5 D9
7 D4 D5 R(1) R(1)
R(1) R(1)
8 D4 D5 R(2) R(2)
R(2) R(2)
+ *
7 R(1) D5
8 R(2) R(2)
116
5. Analyses syntaxiques ascendantes
117
5. Analyses syntaxiques ascendantes
Théorème :
Si une grammaire G est LL, de précédence simple, de précédence fiable
ou LR (SLR,LALR ou LR) alors G est non ambiguë
Remarques :
La classe des grammaires qui peuvent être analysées par la méthode LR
est un sur-ensemble strict de la classe des grammaires qui peuvent être
analysées par les méthodes prédictives.
Une grammaire non ambiguë peut ne pas être LR(k) ∀ k ≥ 0. La
grammaire des palindromes dont les productions sont S→aSa|ε est non
ambiguë et non LR(k) ∀ k ≥ 0.
LALR(1)
SLR(1) LL(1)
118
5. Analyses syntaxiques ascendantes
V.5. EXERCICES
Exercice 1.
Soit G la grammaire dont les productions sont :
S → (L) | a
L→L,S|S
a) G est-elle d'opérateurs ? G est-elle de précédence d'opérateurs?
b) Analyser la chaîne : (a , (a , a))
Exercice 2.
Soit G la grammaire dont les productions sont :
S→{S,A}|A
A→A(-A)|a|b
a) G est-elle de précédence simple ?
b) Comment rendre la grammaire précédente de précédence simple ?
Exercice 3.
Soit G une grammaire régulière. L(G) peut-il être engendré par une
grammaire :
a) de précédence simple ?
b) de précédence d'opérateurs ?
Exercice 4.
Soit la grammaire G=({S,A},{`,´,c},P,S).
P: S → A´
A → ` | Ac | AS
a) G est-elle de précédence simple ?
b) Analyser la chaîne `c`c´´
c) Construire le graphe d’optimisation de G.
d) Analyser la chaîne `c`cc´´ avec la table optimisée.
119
5. Analyses syntaxiques ascendantes
Exercice 5.
• On définit la grammaire G=({S,A},{a,b,c,[,]},P,S).
P: S → AS | a S | [S] | t | f
A→S b |S c
a) Montrer que G n'est pas de précédence simple sans calculer les relations
de précédence.
b) Construire une grammaire équivalente G’ qui donne aux opérateurs a b c
les priorités et les associativités suivantes :
a est plus prioritaire que b
b est plus prioritaire que c
b et c sont associatifs à gauche
Les expressions sont évaluées en premier
L’évaluation se fait de gauche à droite
L’expression la plus simple a pour valeur f ou t
a) La grammaire G’ est-elle de précédence d’opérateurs ?
Exercice 6.
• Soit la grammaire G=({S,A,B},{0,1,a},P,S).
P: S → 0A1
A → aSB | 00
B→1
a) G est-elle de précédence simple ? est-elle de précédence faible ?
b) Construire le graphe d’optimisation de la table de précédence.
c) Construire la table de précédence optimisée.
Exercice 7.
• Soit la grammaire G=({S,A},{a,b,d},P,S).
P: S → bAdS | aa
A → aS | a | d
a) G est-elle de précédence simple ?
b) G est-elle de précédence faible ?
c) Construire le graphe d’optimisation de G.
d) Analyser la chaîne badaa
120
5. Analyses syntaxiques ascendantes
Exercice 8.
• On définit la grammaire G=(N,T,P,S) suivante :
P: S → aAB|d
A → bSB|a
B → b
a) Est-ce que G est une grammaire de précédence simple ? Si elle ne l'est
pas qu'elle modification permettrait de faire une analyse déterministe.
Exercice 9.
• Soit la grammaire G=({S,A,B},{a,b},P,S).
P: S → aSAB | BA
A → aA | B
B→b
a) G est-elle LR(1) ?
b) Analyser la chaîne abbbba.
Exercice 10.
• Considérer les grammaires suivantes :
Exercice 11.
• Soit la grammaire Gm,n=({S,A,B},{a,b,c},P,S) ; m,n >= 0
P: S → aA | aS
A → bn | Ban
B → cB | bm
• Gm,n est-elle LR(k) ? (Discuter).
121
5. Analyses syntaxiques ascendantes
Exercice 12.
• Soit la grammaire G=({S,A,B},{a,b},P,S).
P: S → AaAb | BbBa
A→ε
B→ε
a) G est-elle LL(1) ?
b) G est-elle SLR(1) ?
Exercice 13.
• Soit la grammaire G=({S,A},{a,b,c},P,S).
P: S → AA | cAc
A → aA | b
a) G est-elle LR(1) ?
b) G est-elle LALR(1) ?
Exercice 14.
• Soit G une grammaire LR(0).
a) Peut-on dire que G est SLR(0) ? LALR(0) ?
b) Peut-on dire que G est LL(1) ? LL(k) ? (k>1).
Exercice 15.
• Considérer la grammaire G suivante :
P: E→E+T|T
T→TF|F
F→F*|a|b
• Construire la table d’analyse SLR(1) pour cette grammaire.
• Construire la table d’analyse LALR(1)
122
5. Analyses syntaxiques ascendantes
Exercice 16.
On définit la grammaire G=(N,T,P,S) suivante :
P: <instr> → si cond alors <instr> sinon <instr>
| si cond alors <instr>
| autre
si, cond, alors, sinon et autre sont considérés comme des terminaux.
a) Réduire l'écriture de cette grammaire.
b) Cette grammaire est-elle SLR(1) ?
c) S'il existe des conflits, utiliser la convention du langage Pascal pour
supprimer ce (ou ces) conflits.
Exercice 17.
• Considérer la grammaire G suivante :
P: S → Aa | bAc | dc | bda
A→d
• Montrer que cette grammaire est LALR(1) mais pas SLR(1).
Exercice 18.
• Considérer la grammaire G suivante :
P: S → Aa | bAc | Bc | bBa
A→d
B→d
• Montrer que cette grammaire est LR(1) mais pas LALR(1).
Exercice 19.
• On définit la grammaire Gi,j=({S,A,B},{a,b,c},P,S). i, j entiers positifs
fixés.
P: S → aA | aS
A → (ab)i | Bab
B → Bc | (ab)j
a) La grammaire Gi,j est-elle SLR(0) ?
b) La grammaire G0,0 est-elle LR(1) ?
c) La grammaire Gi,j est-elle LR(k) ?
123
5. Analyses syntaxiques ascendantes
Exercice 20.
• Soit la grammaire G=({E},{⊗,⊕,(,),i},P,E) :
P: E → E ⊕ E | E ⊗ E | (E) | i
a) Construire la table d'analyse SLR(1) pour la grammaire précédente.
Exercice 21.
a) Donnez la grammaire ambiguë qui permet de générer les expressions
régulières définies sur l'alphabet {a,b}. Les symboles |, ., * et ∈
représentent respectivement l'union, la concaténation, la fermeture et
épsilon.
b) Construire la table d'analyse LALR(1) pour la grammaire trouvée en a)
en utilisant la méthode optimisée.
c) Lever les ambiguités de votre table en utilisant les priorités et
associativités liées aux opérateurs de la grammaire.
d) Analyser la chaîne : a|b.a*
124
6. Traduction Dirigée par la Syntaxe
Introduction
Dans ce chapitre, on traitera de l'aspect traduction dirigée par la syntaxe
c'est à dire qu'au fur à mesure que se déroule l'analyse syntaxique, on
effectue certains traitements. Ces traitements sont appelés des actions
sémantiques ou des routines sémantiques. Ces actions ont un "timing"
d'exécution. Les traitements à effectuer sont insérés directement dans la
grammaire. Le fait de procéder ainsi, permet d'éviter de faire des passes
répétées sur le programme à analyser.
Tout traitement nécessaire à la production d'un résultat (production de
code intermédiaire, contrôles sémantiques, …) est inséré dans la
grammaire à "un endroit adéquat". Les sections suivantes détailleront ce
principe.
125
6. Traduction Dirigée par la Syntaxe
Remarques :
i) La notation post-fixée est surtout utilisée pour représenter les
expressions arithmétiques. L'évaluation d'une expression arithmétique
devient "très facile".
ii) Aucune parenthèse n'est nécessaire en notation post-fixée.
Exemple :
• Représentation en notation post-fixée de : a := b * (a - c) * a
bac-*a*
VI.1.2. Qudruplets
Définition :
Un quadruplet est une structure à quatre champs :
(op, source1, source2, destination)
qui désigne : destination ← source1 op source2
Remarques :
• La représentation du code intermédiaire sous forme de quadruplets est
très utilisée dans la compilation des langages de programmation "haut
niveau". Car cette forme de représentation se rapproche du code
machine.
• Les instructions à opérateur unaire sont représentés par :
(op, source1, , destination)
• Les instructions de branchement sont représentés par :
(code-br, , , label)
Exemple :
• Représentation sous forme de quadruplets de l'expression : a := b * (- c)
+ b * (- c)
(0) (- , c , , T1 )
(1) (*, b , T1 , T2 )
(2) (- , c , , T3 )
(3) (*, b , T3 , T4 )
(4) (+, T2 , T4 , T5 )
(5) (=, T5 , , a )
126
6. Traduction Dirigée par la Syntaxe
VI.1.3.1. Triplets
Définition :
Un triplet est une structure à trois champs :
(op, Arg1, Arg2)
qui effectue : Arg1 op Arg2 ; le résultat de cette opération est référencé
par le numéro du triplet dans le code intermédiaire généré. Les
arguments peuvent également un triplet.
Exemple :
• Représentation sous forme de triplets de l'expression : a := b * (- c) + b *
(- c)
(0) (- ,c , )
(1) (*, b , (0) )
(2) (- ,c , )
(3) (*,b , (2) )
(4) (+,(1), (3) )
(5) (=, a , (4) )
127
6. Traduction Dirigée par la Syntaxe
• Code intermédiaire :
(0)
(1)
(0)
(1)
(2)
(3)
• Triplets :
(0) (- ,c , )
(1) (*, b , (0) )
(2) (+,(1), (1) )
(3) (=, a , (4) )
Définition :
La représentation sous format d'arbres abstraits est une forme condensée
de l'arbre syntaxique.
Exemple :
• Représentation sous forme de triplets de l'expression : 3 * 5 + 4
* 4
3 5
E.val=19 '\n'
E.val=15 + T.val=4
T.val=15 F.val=4
T.val=3 F.val=15 4
*
F.val=3 5
130
6. Traduction Dirigée par la Syntaxe
id1
VI.3.1. Définition
133
6. Traduction Dirigée par la Syntaxe
134
6. Traduction Dirigée par la Syntaxe
135
6. Traduction Dirigée par la Syntaxe
Procédure R( )
Début
Si tc = '+'
Alors
tc = ts ; T( ) ; print('+'); R( ) ;
FinSi ;
Fin.
Procédure T( )
Début
F( ) ; G( ) ;
Fin.
Procédure G( )
Début
Si tc = '*'
Alors
tc = ts ; F( ) ; print('*'); G( ) ;
FinSi ;
Fin.
Procédure F( )
Début
Si tc = '('
Alors
tc = ts ;
E( ) ;
Si tc = ')' Alors tc = ts
Sinon "Erreur"
FinSi
Sinon
Si tc = 'id' Alors tc = ts ; print (nb) ;
Sinon "Erreur"
FinSi
FinSi
Fin.
Exemple 2 :
Donner le schéma de traduction, dans le cas d'une analyse par descente
récursive, pour générer un code intermédiaire sous forme de quadruplets
pour les expressions logiques.
136
6. Traduction Dirigée par la Syntaxe
Pour l'expression logique suivante "a or b and not c" on générera les
quadruplets suivants:
(not, c, , tmp1)
(and, b, tmp1, tmp2)
(or, a, tmp2, tmp3)
√ Grammaire non récursive gauche générant les expressions logiques :
E → T E'
E' → or T E' | ε
T → F T'
T' → and F T' | ε
F → not F | G'
G → ( E ) | id | t | f
√ Schéma de traduction :
E → T {E'.h := T.s}
E' {E.s := E'.s}
E' → or
T {E'1.h := f(E'.h,T.s}
E'1 {E'.s := E'1.s}
E' → ε {E'.s := E'.h}
T → F {T'.h := F.s}
T' {T.s := T'.s}
T' → and
F {T'1.h := g(T'.h,F.s}
T'1 {T'.s := T'1.s}
T' → ε {T'.s := T'.h}
F → not
F1 {F.s := h(F1.s)}
F → G {F.s := G.s}
G → ( E ) {G.s := E.s}
G → id {G.s := id.s}
√ Les traitements f, g et h seront explicitées dans le corps des fonctions du
traducteur descendant décrites ci-dessous :
137
6. Traduction Dirigée par la Syntaxe
Fonction E( )
Début
val := T( );
val' := E'(val);
return (val');
Fin.
Fonction E'( e'h)
Début
Si tc = or
Alors
val := T( ) ; i++ ;
Générer-Quadruplet (or, e'h, val, tmpi) ;
val := E'(tmpi);
return(val);
Sinon
return(e'h);
FinSi ;
Fin.
Fonction T( )
Début
val := F( );
val' := T'(val);
return (val');
Fin.
Fonction T'( t'h)
Début
Si tc = and
Alors
val := F( ) ; i++ ;
Générer-Quadruplet (and, t'h, val, tmpi) ;
val := T'(tmpi);
return(val);
Sinon
return(t'h);
FinSi ;
Fin.
138
6. Traduction Dirigée par la Syntaxe
Fonction F( )
Début
Si tc = not
Alors
tc = ts ;
val := F( ) ; i++ ;
Générer-Quadruplet (not, val, , tmpi) ;
return(tmpi);
Sinon
val := G( ); return(val);
FinSi ;
Fin.
Fonction G( )
Début
Si tc = (
Alors
tc = ts ;
val := E( ) ;
Si tc = )
Alors
tc = ts ; return (val);
Sinon "Erreur"
FinSi
Sinon
Si tc = id
Alors
val := tc; tc = ts ; return (val);
Sinon "Erreur"
FinSi
FinSi ;
Fin.
139
6. Traduction Dirigée par la Syntaxe
Exemple 3 :
Donner le schéma de traduction, dans le cas d'une analyse par descente
récursive, pour générer un code intermédiaire sous forme de quadruplets
pour l'instruction if.
√ Grammaire factorisée générant l'instruction if :
<Instr-if> → if <Cond> then <Instr> <X>
<X> → else <Instr> | ε
Quadruplets Cond1
JZ
Quadruplets Cond2
JZ
Quadruplets I1
Jump
Quadruplets I2
140
6. Traduction Dirigée par la Syntaxe
Fonction Instr-if( )
Début
Si tc = if
Alors
tc := ts;
Cond(); /* Quadruplets de condition */
Si tc = then
Alors
Quad(Qc) := <JZ, , , >;
Save-JZ := Qc; /* Save-JZ est une variable locale */
Qc++;
Instr(); /* appel de la fonction qui traite l'instruction qui suit then */
X(Save-JZ);
Sinon "Erreur";
FinSi ;
Sinon "Erreur";
FinSi ;
Fin.
Fonction X(Save-JZ)
Début
Si tc = else
Alors
tc := ts;
Quad(Qc) := <Jump, , , >;
Save-Jump := Qc++;
Quad(Save-JZ).4 := Qc;
Instr(); /* appel de la fonction qui traite l'instruction qui suit else */
Quad(Save-Jump).4 := Qc;
Else
Quad(Save-JZ).4 := Qc;
FinSi;
Fin.
141
6. Traduction Dirigée par la Syntaxe
Définition :
Une définition dirigée par la syntaxe est dite L-attribuée si tout attribut
hérité de Xj (1≤j≤n)du MDP de la règle A → X1 X2 … Xn ne dépend que
:
√ des attributs des symboles X1 X2 … Xj-1
√ des attributs hérités de A.
T → id {Afficher (id)}
Schéma de traduction équivalent au précédent sans actions intérieures :
E → TR
R → + TMR | - TNR | ε
M→ ε {write ('+')}
N→ ε {write ('-')}
T → nb {write (id.val)}
suite :
Exemple d'instruction case et Forme Intermédiaire correspondante :
√ Instruction case :
Case var of
val1 : Instr1 ;
val2 : Instr2 ;
else : Instr3
end;
√ Forme Intermédiaire correspondante :
<:= , var, , tmp>
<- , tmp, val1 , >
JNZ
Quadruplets Instr1
Jump
<- , tmp, val2 , >
JNZ
Quadruplets Instr2
Jump
Quadruplets Instr3
Schéma de traduction :
√ Grammaire qui permet de générer les instructions case :
<inst-case> → case <exp> of <list> <default> end-case
<liste> → <list> val : instr ; | val : instr ;
<default> → else instr ;
√ On utilisera dans le schéma de traduction de l'instruction case, une
structure de données Tableau dénommée QUAD pour contenir les
quadruplets. Les éléments du tableau sont enregistrements à quatre
144
6. Traduction Dirigée par la Syntaxe
145
6. Traduction Dirigée par la Syntaxe
Quadruplets Cond1
JZ
Quadruplets Cond2
JZ
Quadruplets Instr1
Jump
Quadruplets Instr2
Jump
Quadruplets Cond3
JZ
Quadruplets Instr3
Jump
Quadruplets Instr4
146
6. Traduction Dirigée par la Syntaxe
Schéma de traduction :
√ Grammaire qui permet de générer les instructions case :
<inst-if> → if <cond> then <instr> else <instr>
| if <cond> then <instr>
√ Schéma de traduction :
<inst-if> → if <cond> M1 then <instr> M2 else <instr> M4
| if <cond> M1 then <instr > M3
M1 → ε { Quadruplets Cond1 ;
Empiler (Quadcourant, pile_JZ);
QUAD(QUADCOURANT++):=(JZ, , , ) }
M2 → ε { Dépiler (de pile_JZ dans Save);
Empiler (Quadcourant, pile_JMP);
QUAD(QUADCOURANT++):=(JMP, , , )
QUAD(Save).4 := Quadcourant; }
M3 → ε { Dépiler (de pile_JZ dans Save);
QUAD(Save).4 := Quadcourant; }
M4 → ε { Dépiler (de pile_Jump dans Save);
QUAD(Save).4 := Quadcourant; }
Remarques :
Il peut exister plusieurs formes intermédiaires pour une même
construction. Il faudra choisir la représentation intermédiaire qui
s'exécute le plus rapidement.
Dans une production "MGP récursive" faire attention à la position du
non terminal récursif car l'ordre de génération de code en dépend.
Exercice :
Donner le schéma de traduction d'une expression conditionnelle du
langage C.
Indications :
- la condition du langage C est toujours exprimée entre parenthèses
- Grammaire abrégée d'une expression conditionnelle de C :
Expr → Expr '||' T | T
T → T '&&' F | F
G → G '= =' R | G '!=' R | R
R → R '<' S | R '>' S | R '<=' S | R '>=' S | S
S → S '+' B | B
B → B '*' C | C
C → i | '(' Expr ')'
147
6. Traduction Dirigée par la Syntaxe
Quadruplets cond1
JZ
Quadruplets instr1
Quadruplets instr2
Quadruplets cond2
JZ
Quadruplets instr3
Jump
Quadruplets cond3
JNZ
Quadruplets instr4
JUMP
148
6. Traduction Dirigée par la Syntaxe
Schéma de traduction :
√ Grammaire qui permet de générer les instructions case :
<boucles-w> → while <cond> do <instr>
| do <instr> while <cond>
√ Schéma de traduction :
<boucles-w> → while M1 <cond> M2 do <instr> M3
| do M4 <instr> while <cond> M5
M1 → ε { M1.val = QUADCOURANT; }
M2 → ε {
QUAD(QUADCOURANT++):=(JZ, , , ) ;
M2.val:= (QUADCOURANT-1);
}
M3 → ε {
QUAD(QUADCOURANT++):=(JUMP, , ,M1.val ) ;
QUAD(M2.val).4 := QUADCOURANT;
}
M4 → ε { M4.val = QUADCOURANT; }
M5 → ε {
QUAD(QUADCOURANT++):=(JNZ, , ,M4.val ) ;
}
VI.6. YACC
149
6. Traduction Dirigée par la Syntaxe
déclarations
%%
productions
%%
code additionnel
Remarque :
Seul le premier séparateur %% et la deuxième partie étant obligatoires.
non_terminal:
corps_1 { action_sémantique_1 }
| corps_2 { action_sémantique_2 }
| ...
| corps_n { action_sémantique_n }
;
150
6. Traduction Dirigée par la Syntaxe
sachant que les corps_i peuvent être des symboles terminaux ou non
terminaux de la grammaire.
y.output
0 $accept : rhyme $end
1 rhyme : sound place
2 sound : DO RE
3 place : MI
^L
state 0
$accept : . rhyme $end (0)
DO shift 1
. error
rhyme goto 2
sound goto 3
state 1
sound: DO . RE (2)
RE shift 4
. error
state 2
$accept : rhyme . $end (0)
$end accept
. error
state 3
rhyme : sound . place (1)
MI shift 5
. error
place goto 6
state 4
sound: DO RE . (2)
. reduce 2
state 5
place : MI . (3)
. reduce 3
state 6
rhyme : sound place . (1)
. reduce 1
5 terminals, 4 non terminals
4 grammar rules, 7 states
153
6. Traduction Dirigée par la Syntaxe
Exercice :
Vérifier la table d'analyse LALR(1) précédente en la construisant vous-
même. Commentez.
Eléménts de réponse :
Items LR(0) après rajout de la règle S' → rhyme # :
√ I0 = {[S' → .rhyme], [rhyme → .sound place], [sound → .DO RE]}
√ I1 = GOTO(I0, rhyme) = {[S' → rhyme.]}
√ I2 = GOTO(I0, sound) = {[rhyme → sound . place], [place → .MI]}
√ I3 = GOTO(I0, DO) = {[sound → DO . RE]}
√ I4 = GOTO(I2, place) = {[rhyme → sound place .]}
√ I5 = GOTO(I2, MI) = {[place → MI .]}
√ I6 = GOTO(I3, RE) = {[sound → DO RE . ]}
Table d'analyse SLR(1) :
154
6. Traduction Dirigée par la Syntaxe
155
6. Traduction Dirigée par la Syntaxe
156
6. Traduction Dirigée par la Syntaxe
157
6. Traduction Dirigée par la Syntaxe
VI.7. EXERCICES
Exercice 1.
Traduire l'expression – (a + b) * (c +d) + (a +b +c) en :
a) quadruplets ;
b) triplets
c) triplets indirects
Exercice 2.
Traduire les instructions suivantes sous forme de notation polonaise
préfixée :
1. a – b * c + (a + b)
2. a + (b -c)*(a - b)*b/(c + 2) - ((a - b)**3 + 2)*c + a
Exercice 3.
• Traduire les instructions du programme C suivant :
main()
{ int i;
int a[10];
i=0;
while (i<10)
{ a[i] = 0;
i = i + 1; } }
en :
a) un arbre abstrait ;
b) une notation postfixée ;
c) un code à trois adresses.
Exercice 4.
Ecrire les routines sémantiques dans le cas d’une analyse descendante
(descente récursive) générant des quadruplets de :
a) L’expression logique et les opérateurs AND, OR et NOT.
b) L’instruction REPEAT PASCAL.
Forme générale :
REPEAT instruction UNTIL condition.
c) L’instruction For du langage C
158
6. Traduction Dirigée par la Syntaxe
Nb :
Prendre en compte les imbrications de structures dans les cas b) et c).
Exercice 5.
a) Ecrire les routines sémantiques dans le cas d’une analyse descendante
(descente récursive) pour évaluer les expressions arithmétiques avec
parenthèses. Utiliser obligatoirement les attributs synthétisés et les
attributs hérités dans votre schéma de traduction.
b) Appliquer votre schéma pour évaluer l'expression suivante : 5 + 10 * (9
– 4).
Exercice 6.
Considérer l'instruction suivante permettant de calculer la variance de
plusieurs expressions arithmétiques :
id := Variance (<Exp1>, <Exp2>, …, <Expn>)
a) Donner la grammaire permettant de générer l'instruction d'affectation
décrite ci-dessus (n≥1).
b) Donner le schéma de traduction sous forme de quadruplets dans le cas
d'une analyse descendante.
Exercice 7.
a) Ecrire les routines sémantiques dans le cas d’une analyse descendante
(descente récursive) pour générer les quadruplets correspondant à une
expression de condition.
Utiliser obligatoirement les attributs synthétisés et les attributs hérités
dans votre schéma de traduction.
Utiliser les priorités et associativités classiques des opérateurs
logiques et arithmétiques. Les opérateurs logiques sont moins
prioritaires que les opérateurs relationnels qui sont eux moins
prioritaires que les opérateurs arithmétiques.
b) Appliquer votre schéma pour générer les quadruplets correspondant à
l'expression suivante:
(( a + b >= 5) or ( c < a – b * 4) and (a > d))
Exercice 8.
Soit l’instruction CASE PASCAL. Donner le schéma de traduction de
l’instruction dans le cas d’une analyse descendante générant des
quadruplets.
159
6. Traduction Dirigée par la Syntaxe
Exercice 9.
• Soit l’instruction SELECT dont la forme générale est :
SELECT
<liste-val-1> : <liste-inst-1>;
<liste-val-2> : <liste-inst-2>;
: :
<liste-val-n> : <liste-inst-n>;
BY <expr>;
Exercice 10.
Soit l'instruction Select suivante (n ≥1):
Select max of Select min of
<exp1> : <inst1>; <exp1> : <inst1>;
<exp2> : <inst2>; <exp2> : <inst2>;
: :
<expn> : <instn> <expn> : <instn>
end; end;
où Select, min, max, of et end sont des mots réservés ;
<insti> est une liste d’instructions ;
<exp> est une expression arithmétique (entière ou réelle).
Fonctionnement :
L'instruction Select sélectionne les instructions telles que l'expression
correspondante est égale au maximum (resp. au minimum) des
160
6. Traduction Dirigée par la Syntaxe
Exercice 11.
Donner le programme YACC qui permet d'afficher la forme postfixée
d'une expression arithmétique lue en entrée.
Exemple : Si on lit : 5+4*3-2 alors on affiche : 5 6 3 * + 2 –
Nb :
Chaque opérande est un nombre.
Pas de moins unaire dans les expressions.
Considérer également les expressions avec parenthèses.
Exercice 12.
Ecrire un programme YACC qui permet de construire l'arbre syntaxique
d'une expression arithmétique.
Nb :
Considérer les expressions avec parenthèses.
Chaque opérande est un nombre.
Considérer également le moins unaire dans les expressions.
Indication :
On dispose d'une fonction C node() appelée avec trois arguments.
L'appel node( L, n1, n2 ) crée un nœud avec le label (ou étiquette L)
et deux descendants n1 et n2 et retourne l'adresse de la structure
nouvellement créée.
Exemple d'utilisation : expr : expr '+' expr { $$ = node( '+', $1,
$3 ); }
161
6. Traduction Dirigée par la Syntaxe
Exercice 13.
Considérer le programme YACC suivant :
%left '+' '-'
%left '*' '/'
↑'
%right '↑
%%
expr : expr ↑'
'↑ expr
| expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| NAME
;
Comment seront interprétées les expressions suivantes ; donner leurs
arbres syntaxiques :
a + c * d ↑ d ↑ b - e ↑ f * g
(a + b) * c ↑ d ↑ e - f ↑ g / h
Exercice 14.
Donner un programme YACC qui utilise une grammaire ambiguë pour
analyser et évaluer des expressions arithmétiques utilisant les opérateurs
classiques +, *, -, / et l'opérateur de puissance désigné par ^. L'opérateur
^ a la priorité et associativité classiques dans les expressions
mathématiques. On suppose qu'on dispose d'une fonction C power (a,b)
qui permet de calculer ab (a à la puissance b).
162
7. Environnements d'exécution
VII.1. INTRODUCTION
Avant d'entamer la phase de production de code, il faut établir le rapport
entre le texte source, statique, d'un programme et les actions qui doivent être
effectuées à l'exécution pour implanter ce programme. Lors de l'exécution,
un même nom dans le texte source peut dénoter des données différentes
dans la machine cible. Dans ce chapitre, nous nous intéresserons aux
relations qui existent entre noms et données.
L'allocation et la libération des données sont gérées par le paquetage
de soutien d'exécution, consistant en des routines chargées avec le code cible
produit. La conception du paquetage de soutient d'exécution est influencée
par la sémantique des procédures.
VII.2.1. Procédures
163
7. Environnements d'exécution
164
7. Environnements d'exécution
165
7. Environnements d'exécution
Exemple :
Arbre d'activation correspondant à l'exécution du programme de la
section 2.1 (l'étiquette T correspond à TriRapide et P à Partition) :
Trier
LireTableau T(1,9)
Flot de contrôle :
Le flot de contrôle durant l'exécution d'un programme donné correspond
à un parcours en profondeur de l'arbre d'activation.
Pile :
Pour garder trace des activations de procédures encore vives, la pile de la
machine est utilisée. Cette pile est appelée pile de contrôle. La gestion
des activations vives en pile permet de connaître facilement la portée des
noms. Pour chaque activation, on gardera un ensemble d'informations
(explicité ultérieurement).
Exemple :
Pendant de l'exécution du programme de la section 2.1, si l'appel
TriRapide(3,3) est en cours de traitement, le contenu de la pile sera le
suivant :
166
7. Environnements d'exécution
Trier
TriRapide (1,9)
TriRapide (1,3)
TriRapide (3,3)
Sommet de pile
167
7. Environnements d'exécution
Code
taille connue à la compilation
Données statiques
Pile
Tas
Remarque :
Le sens de croissance de la pile et du tas (illustrés par le tableau
précédent) est une convention prise par certains concepteurs de machine.
Définition:
Un bloc d'activation ou enregistrement d'activation est un bloc mémoire
contenant les informations nécessaires à l'exécution d'une procédure. Le
bloc d'activation est structuré en plusieurs parties.
Exemple d'organisation :
Le schéma suivant illustre, un exemple d'organisation du bloc
d'activation en différentes parties :
Adresse de retour
Paramètres effectifs
Lien de contrôle
Lien d'accès
Etat machine sauvegardé
Données locales
Temporaires
Remarques :
168
7. Environnements d'exécution
VII.4.2.1. Principe
Un bloc d'activation d'une procédure est empilé lorsque la procédure est
appelée et il est dépilé lorsqu'elle se termine. Un des registres de la machine
est dédié à la gestion de la pile. Il s'agit du registre SP (Stack Pointer) qui
pointe sur le sommet de la pile.
Avant d'exécuter une procédure, son bloc d'activation est empilé en
mémoire et le sommet de pile est incrémenté de la taille du bloc d'activation.
Après le retour de la procédure, le sommet de pile est décrémenté de la taille
du bloc.
Exemple d'évolution de la pile :
L'exemple suivant illustre l'évolution de la pile au cours de l'exécution
du programme de la section 2.1 :
Position dans l'arbre Blocs d'activation en pile
d'activation *
Trier* Trier
Trier
Trier
LireTableau* LireTableau
Trier
TriRapide (1,9) Trier
TriRapide(1,9)
TriRapide (1,9)*
TriRapide(1,3)
170
7. Environnements d'exécution
paramètres et
valeurs de retour
lien de contrôle bloc d'activation
sauvegarde de l'état de l'appelant
données locales et
temporaires responsabilité
paramètres et appelant
valeurs de retour
lien de contrôle bloc d'activation
sauvegarde de l'état responsabilité de l'appelé
données locales et appelé
temporaires
171
7. Environnements d'exécution
Stratégie d'allocation :
Les données de ce type doivent être dans la pile puisque après le retour
d'une procédure ils n'ont plus d'existence. Ces données ne peuvent être
stockées dans le bloc d'activation car (en général) l'espace réservé à un bloc
d'activation est fait durant la compilation.
Ces données sont stockées en fait juste après le bloc d'activation de la
procédure appelée qui les "contient". Dans ce bloc d'activation, doit figurer
impérativement des pointeurs vers ces données (la taille des pointeurs est
déterminé à la compilation).
Exemple :
Une procédure P ayant trois tableaux locaux A, B, C est appelée. Cette
procédure appelle une procédure Q. Le schéma suivant illustre l'état de
la pile pendant l'exécution de la procédure Q :
172
7. Environnements d'exécution
Tableau A
Tableau B Tableaux de P
Tableau C
Tableaux de Q
173
7. Environnements d'exécution
Règles de portée :
Les règles de portée d'un langage déterminent le traitement à effectuer
pour les références à des noms non locaux. Une règle courante, appelée
règle de portée statique ou lexicale, permet de déterminer quelle
déclaration s'applique à un nom par le seul examen du texte source du
programme.
Les langages Pascal, C et Ada font partie des nombreux langages qui
utilisent la portée statique, en ajoutant la règle de "l'englobant le plus
imbriqué".
VII.5.1. Blocs
Remarques :
• Les blocs peuvent être imbriqués ou disjoints. Il n'y a jamais de
chevauchement entre deux blocs.
• La portée dans les langages à structure de blocs est donnée par la règle
de l'englobant le plus imbriqué.
Portée d'une déclaration :
La portée d'une déclaration dans un bloc B inclut B.
Si un nom x n'est pas déclaré dans un bloc B, une occurrence de x dans
B est dans la portée d'une déclaration de x dans un bloc englobant B' tel
que :
i) B' comporte une déclaration de x et,
ii) B' est le plus imbriqué des blocs contenant B et ayant une déclaration
de x.
174
7. Environnements d'exécution
Exemple :
Blocs dans un programme C :
main ( )
{
int a = 0;
int b = 0;
{
int b = 1;
{
int a = 2;
printf("%d %d\n", a , b);
}
{
int b = 3;
printf("%d %d\n", a , b);
}
printf("%d %d\n", a , b);
}
printf("%d %d\n", a , b);
}
175
7. Environnements d'exécution
176
7. Environnements d'exécution
Lien d'accès :
Une implantation directe de la portée statique pour les procédures
imbriquées est obtenue en ajoutant à chaque bloc d'activation, un
pointeur appelé lien d'accès. Si une procédure P est imbriquée
immédiatement dans une procédure T dans le texte source, le lien
d'accès d'un bloc d'activation de P référence le lien d'accès du bloc
associé à l'activation la plus récente de T.
Comment retrouver une donnée non locale ?
Une procédure P, qui a une profondeur d'imbrication Np, référence une
donnée non locale t de profondeur d'imbrication Nt avec Nt ≤ Np. Pour
retrouver cette donnée, il suffit de suivre Np – Nt liens d'accès.
177
7. Environnements d'exécution
C'est la manière la plus simple pour passer des paramètres. Les arguments
sont évalués et leurs valeur_d sont passées à la procédure appelée. Le
langage C ne connaît que le passage par valeur. On peut réaliser le passage
par valeur comme suit :
Un paramètre formel est traité exactement comme un nom local, et les
emplacements des paramètres se trouvent donc dans le bloc d'activation
de la procédure appelée.
L'appelant évalue les arguments et place leurs valeur_d aux
emplacements réservés aux paramètres.
178
7. Environnements d'exécution
179
7. Environnements d'exécution
VII.7. EXERCICES
Exercice 1.
Dessiner l'arbre d'activation du programme Pascal suivant :
program param (input,output) ;
procedure b (function h (n : integer) : integer);
var m : integer;
begin m := 3 ; writeln (h(2)) end ;
procedure c;
var m : integer;
function f(n:integer):integer;
begin
f:= m + n;
end ;
procedure r;
var m : integer;
begin
m:= 7; b(f);
end ;
begin
m:= 0; r;
end ;
begin
c;
end.
Exercice 2.
Quel est le résultat du programme suivant, dans le cas d'un passage de
paramètres
(a) par valeur,
(b) par référence,
(c) par copie-restauration,
(d) par nom ?
180
7. Environnements d'exécution
Exercice 3.
En utilisant les règles de portée du langage Pascal, déterminer pour
chaque occurrence de a et b du programme suivant, quelle déclaration s'y
applique :
program a (input,output) ;
procedure b (u,v,x,y : integer);
var a : record a,b : integer end ;
b : record b,a : integer end ;
begin
with a do begin a := u; b := v;
end ;
with b do begin a := x; b := y;
end ;
writeln (a.a, a.b, b.a, b.b)
end ;
begin
b(1,2,3,4);
end.
181
7. Environnements d'exécution
Exercice 4.
Lorsqu'on passe une procédure en paramètre dans un langage à portée
statique, son environnement peut être passé au moyen d'un lien accès.
Donner un algorithme qui détermine ce lien.
182
8. Production de code
VIII.1. INTRODUCTION
programme Générateur
Code programme
source de
Analyse intermédiaire cible
Code
Lexicale
op source destination
qui a la signification destination ←destination op source
Exemples d'instructions :
Instruction Signification
Mov src, dst Charger la source src dans destination dst
Add src, dst Ajouter la source src à destination dst
Sub src, dst Sostraire la source src de la destination dst
Instruction Signification
Mov #1, R0 Charger la constante 1 dans registre R0
Mov 4(R0), M Charger contenu(4+contenu(R0)) dans M
Remarque :
Avant toute étape de génération de code, le coût des instructions (en
terme de temps d'exécution) doit être connu. S'il y a possibilité de choisir
entre plusieurs instructions, il faudra toujours prendre la moins coûteuse en
vue de produire le meilleur code possible.
184
8. Production de code
Exemple :
Considérer le programme suivant de calcul d'un produit scalaire :
Début
prod :=0;
i:=1;
Faire
Début
prod:= prod + a[i]*b[i];
i:= i + 1;
Fin
Tanque i<= 20
Fin.
186
8. Production de code
prod := 0 Bloc B1
i := 1
t1 := 4 * i Bloc B2
t2 := a [t1]
t3 := 4 * i
t4 := b [t3]
t5 := t2 * t4
t6 := prod + t5
prod := t6
t7 := i + 1
i := t7
si i <= 20 aller à (3)
189
8. Production de code
190
8. Production de code
Pour générer le code, il faudra ensuite faire une passe sur le bloc de base
du début vers la fin.
191
8. Production de code
192
8. Production de code
Exemple 2 :
Remarques :
√ Ri est un registre et Mi est un emplacement mémoire.
√ Rp est un registre et Mp est un emplacement mémoire.
√ R est le registre retourné par la fonction GETREG( ).
194
8. Production de code
Exemple :
Considérer l'instruction de branchement suivante :
Si x < y alors aller à Label
Son code machine sera le suivant :
CMP x, y
JL Label
où
CMP compare deux opérandes et positionne les bits indicateurs de la
machine ;
JL (pour Jump if Less) est un mnémonique pour brancher si plus petit.
VIII.4.5.1. DAG
Le terme DAG est l'acronyme de "Directed Acyclic Graph" (ou graphe
orienté sans cycles). Dans le contexte de la production de code, le DAG sera
utilisé pour essayer d'améliorer la qualité du code produit.
Dans cette section on s'intéressera aux DAG associés aux blocs de base.
Dans ce type de DAG, les feuilles sont étiquetées avec des identificateurs
uniques qui sont soit des noms de variables soit des constantes. Les
nœuds internes sont les symboles d'opérateurs. Les nœuds internes
portent également des étiquettes qui représentent les calculs
intermédiaires effectués dans un bloc de base.
cours de traitement) ont été déjà calculés (nœuds existant déjà dans le
DAG). Si c'est le cas, on ne créera pas de nœud pour ce type d'opérande,
mais on dirigera l'arc du nœud opérateur vers le nœud déjà crée.
Exemple 1 :
Quadruplets d'un bloc de base :
t1 := a + b
t2 := c + d
t3 := e – t2
t4 := t1 – t3
DAG correspondant :
t4 -
t1 + -
t3
a b e
t4 +
c d
Exemple 2 :
Quadruplets d'un bloc de base :
t1 = b * c
t2 = a + t1
t3 = d + e
t4 = t3 – e
t5 = t3 * t4
t6 = t2 – t5
196
8. Production de code
DAG correspondant :
t6 -
t2 + t5 *
a t4
t3 + -
t1 *
b c d e
Faire
Marquer M;
N:= M;
Fait
Fait.
Remarque :
L'algorithme précédent entame son analyse avec "la racine" (ou point
d'entrée) du DAG.
Exemple 1 :
√ Considérer les quadruplets suivants et supposons que la machine ne
dispose que deux registres R0 et R1.
t1 := a + b
t2 := c + d
t3 := e – t2
t4 := t1 – t3
√ L'application de l'algorithme simple de production de code donne le code
suivant :
Mov a, R0
Add b, R0
Mov c, R1
Add d, R1
Mov R0, t1
Mov e, R0
Sub R1, R0
Mov t1, R1
Sub R0, R1
Mov R1, t4
√ Application de l'heuristique sur le DAG ; le numéro de l'étoile donne
l'ordre de marquage :
*1
t4 -
t1 *2 *3
+ t3 -
198
a b e *4
t2 +
c d
8. Production de code
Exemple :
Considérer les quadruplets suivants et supposons que la machine ne
dispose que deux registres R0 et R1.
t1 := b + c
t2 := a + t1
199
8. Production de code
t3 := d + e
t4 := t2 + t3
√ L'application de l'algorithme simple de production de code donne le code
suivant :
Mov b, R0
Add c, R0
Mov a, R1
Add R0, R1
Mov d, R0
Add e, R0
Add R0, R1
√ Application de l'heuristique sur le DAG ; le numéro de l'étoile donne
l'ordre de marquage :
*1
t4 -
*2
*4
t2
+ t3 -
*3
a t1 + d e
b c
200
8. Production de code
Add c, R1
Mov R0, t3
Mov a, R0
Add R1, R0
Add t3, R0
Le code produit après application de l’heuristique d’ordonnancement est
plus mauvais que le code produit par l’algorithme simple.
Après ce constat, il ressort que l’heuristique proposée n’est pas fiable et
privilégie systématiquement l’évaluation d’un nœud juste après l’ évaluation
de son fis gauche. Nous proposons l’algorithme d’ordonnancement suivant
dont l’idée est que l’évaluation d’un nœud suit l’évaluation de la branche
fille la moins chargée en terme d’opérations. Un traitement supplémentaire
est donc nécessaire pour évaluer en chaque nœud le nombre d’opérations de
sa descendance.
L'algorithme suivant donne l'ordre inverse d'évaluation des
quadruplets pour un bloc de base. L'algorithme entame son analyse avec "la
racine" (ou point d'entrée) du DAG
Algorithme :
Initialement tous les nœuds sont non marqués ;
Tant que des nœuds internes n'ont pas été marqués
Faire
Choisir un nœud N marqué dont tous les parents ont été marqués ;
Marquer (N) ;
Fait.
Marquer(N)
{ Marquage de N par un compteur ; incrémenter compteur ;
ng=nombre opérations branche gauche ;
nd=nombre opérations branche droite ;
Si (ng<nd) et (fils gauche G feuille et tous ses parents marqués)
Alors Marquer (G) ;
Si (fils droit D feuille et tous ses parents marqués)
Alors Marquer (D) ;
201
8. Production de code
FinSi ;
Sinon
Si (ng≥nd) et (fils droit D feuille et tous ses parents marqués)
Alors Marquer (D) ;
Si (fils gauche G feuille et tous ses parents marqués)
Alors Marquer (G) ;
FinSi ;
Remarque :
L'algorithme précédent donne de meilleurs résultats que l’heuristique
donnée précédemment.
Exemple :
Considérer les quadruplets suivants et supposons que la machine ne
dispose que 3 registres R0,R1 et R2.
t1 := b + c
t2 := a + t1
t3 := d + e
t4 := t2 + t3
t5 := f + g
t6 := j – k
t7 := i + t6
t8 := e – t7
t9 := t5 – t8
t10 := t4 + t9
ADD e, R2
ADD R2, R1
MOV f, R0
ADD g, R0
MOV j, 2
SUB k, R2
MOV R1, t4
MOV i, R1
ADD R2, R1
MOV e, R2
SUB R1, R2
SUB R2, R0
MOV t4, R1
ADD R0, R1
203
8. Production de code
*2
*6
t4 -
+ t9
*3
*5 *7 *8
t2
+a t3 + t5 + t8 -
*4 *9
t7
a t1 + d e f g +a
*1
b c i t6 -
j k
b. Ordonnancement des quadruplets
L’ordre d'évaluation des quadruplets est l'inverse de l'ordre de
marquage (* suivie de la chronologie du marquage). Dans l’exemple
considéré, l’ordre d’évaluation est : t6 t7 t8 t5 t9 t3 t1 t2 t4 t10
c. Production de code :
MOV j, R0
SUB k, R0
MOV i, R1
ADD R0, R1
MOV e, R2
SUB R1, R2
MOV f, R0
ADD g, R0
SUB R2, R0
MOV d, R1
ADD e, R1
MOV b, R2
ADD c, R2
MOV R0, t9
MOV a, R0
ADD R2, R0
ADD R1, R0
204
8. Production de code
ADD t9, R0
iii. Production du code à partir de la méthode proposée :
a. Ordonnancement des quadruplets
Le DAG considéré est le même que celui construit précédemment. Le
marquage du DAG est fait en appliquant la méthode proposée.
L’ordre d'évaluation des quadruplets est: t6 t7 t8 t5 t9 t1 t2 t3 t4 t10
b. Production de code
MOV j, R0
SUB k, R0
MOV i, R1
ADD R0, R1
MOV e, R2
SUB R1, R2
MOV f, R0
ADD g, R0
SUB R0, R2
MOV b, R0
ADD c, R0
MOV a, R1
ADD R0, R1
MOV d, R0
ADD e, R0
ADD R0, R1
ADD R2, R1
iv. Comparaison des codes générés :
On constate que l’application de l’heuristique d’ordonnancement
proposée par Aho et al [1] avant la production de code améliore dans ce
cas le code produit par rapport à l’algorithme simple. L’application de
l’ordonnancement proposé permet d’améliorer d’avantage le code
produit.
205
8. Production de code
bcdf
a := b + c
d := d - b Bloc B1
e := a + f
acdef
acde acdf
f := a - d Bloc B2 Bloc b := d +f
B3 e := a - c
bcdef
cdef
Bloc B4 b := d + c b d e f "vivantes"
bcdef
bcdef
"vivantes"
a est vivante en sortie de B1 mais n'est plus vivante en sortie de
B2, B3 et B4.
206
8. Production de code
USE(b,B1)=2 LIVE(b,B1)=0
USE(b,B2)=0 LIVE(b,B2)=0
USE(b,B3)=0 LIVE(b,B3)=1
USE(b,B4)=0 LIVE(b,B4)=1
USE(c,B1)=1 LIVE(c,B1)=0
USE(c,B2)=0 LIVE(c,B2)=0
USE(c,B3)=1 LIVE(c,B3)=0
USE(c,B4)=1 LIVE(c,B4)=0
USE(d,B1)=1 LIVE(d,B1)=1
USE(d,B2)=1 LIVE(d,B2)=0
USE(d,B3)=1 LIVE(d,B3)=0
USE(d,B4)=1 LIVE(d,B4)=0
USE(e,B1)=0 LIVE(e,B1)=1
USE(e,B2)=0 LIVE(e,B2)=0
USE(e,B3)=0 LIVE(e,B3)=1
USE(e,B4)=0 LIVE(e,B4)=0
207
8. Production de code
USE(f,B1)=1 LIVE(f,B1)=0
USE(f,B2)=0 LIVE(f,B2)=1
USE(f,B3)=1 LIVE(f,B3)=0
USE(f,B4)=0 LIVE(f,B4)=0
Code machine :
MOV b, R1
MOV d, R2
MOV R1, R0
ADD c, R0
SUB R1, R2
MOV R0, R3
ADD f, R3
MOV R3, e
MOV R1, b
MOV R2, d
208
8. Production de code
VIII.5. EXERCICES
Exercice 1.
En utilisant l'algorithme de la section 4, produire le code pour les
instructions C suivantes :
i) x = a +b * c;
ii) x = a / (b + c) – d * (e + f);
Nb. On suppose qu'on dispose des instructions MUL et DIV pour effectuer
la multiplication et la division.
Exercice 2.
Produire le code relatif au programme C suivant :
main()
{ int i;
int a[10];
i = 1;
while (i <= 10)
{
a[i] = 0;
i = i + 1;
}
}
Exercice 3.
Produire le code relatif au programme Pascal suivant :
Begin
A: array [1..10, 1..5, 1..2] of integer;
B: array [1..10, 1..5] of integer;
i:= 1; j:= 1;
While j ≤ 5
Begin
alpha:= 3,14; n:= E;
gamma:= alpha*2 + 3*j;
A[i,j,2]:= B[i,j] + gamma;
j:= j + 1;
end;
end.
209
8. Production de code
Exercice 3.
a) Expliquer comment construire le DAG correspondant à une expression
arithmétique.
b) Construire le DAG pour l'expression arithmétique suivante :
t = (a+b*c) +d*e-b*c +(a+b*c)/(b*c)+d*e
c) Générer à partir du DAG, les quadruplets permettant d'évaluer
l'expression précédente (les sous expressions communes ne seront pas
réévaluées) sans optimiser le nombre de temporaires.
d) Générer le code machine correspondant à ces quadruplets en appliquant
l'algorithme vu en cours. Trois registres R0, R1 et R2 sont disponibles.
On dispose des codes opérations suivants : MOV, ADD, SUB, DIV et
MUL. Chaque instruction arithmétique a la forme suivante :
OP src, dest
dont la signification est :
dest ← dest OP src
e) Appliquer l'heuristique sur le DAG pour trouver un autre ordre
d'évaluation des quadruplets. Générer alors le code machine
correspondant. Comparer les codes machines obtenus ; commenter.
Exercice 4.
Considérer le code intermédiaire suivant :
t1 = b * c
t2 = a + t1
t3 = d + e
t4 = t3 – e
t5 = t3 * t4
t6 = t1 – t5
a) Générer le code machine correspondant à ces quadruplets en appliquant
l'algorithme vu en cours. Trois registres R0, R1 et R2 sont disponibles.
On dispose des codes opérations suivants : MOV, ADD, SUB, DIV et
MUL. Chaque instruction arithmétique a la forme suivante :
OP src, dest
210
8. Production de code
211
BIBLIOGRAPHIE
[1] Titre : Compilateurs : Principes, techniques et outils
Auteurs : Aho, Ullman & Sethi.
Edition : DUNOD 2000.
[2] Titre : Principles of compiler design
Auteurs : Aho & Ullman.
Edition : Addison Wesley, 1977.
[3] Titre : Yacc: Yet Another Compiler-Compiler
Auteur : Stephen C. Johnson
Computing Science Technical Report No. 32, Bell Laboratories, Murray
Hill, NJ 07974.
[4] Titre : Modern Compiler Design.
Auteur : D. Grune
Edition : John Wiley & Sons, 2000.
ISBN : 0 471 97697 0.
[5] Titre : Introduction to Automata Theory, Languages and
Computation
Auteurs : J.E. Hopcroft & J.D. Ullman.
Edition : Addison Wesley, 1979.
[6] Titre : Compiler Construction : Principles and Practice
Auteur : K.C. Louden
Edition : Course Technology, 1997.
ISBN : 0 534 93972 4.
[7] Titre : Réaliser un compilateur, les outils Lex et YACC
Auteur : N. Silverio.
Edition : Eyrolles, 1994.
[8] Titre : Lex & Yacc
Auteur : J. Levine, T. Mason, D. Brown
Edition : O(Reilly), 1992.
ISBN : 1 56592 000 7.
[9] Titre : Generating Parsers with JavaCC
Auteur : Tom Copeland
Edition : Centennial Books, Alexandria, VA, 2007.
ISBN : 0-9762214-3-8
ANNEXE A. Classification des Grammaires
Le linguiste Noam Chomsky a classifié les grammaires en quatre classes
données par le tableau suivant :
Grammaire Forme des productions : α→β
Type 0 α,β chaînes arbitraires de symboles de la grammaire avec α≠ε
Nom associé
Grammaires Non Restreintes (Unrestricted Grammars)
Outil formel associé
Machine de Turing
Grammaire Forme des productions : α→β
Type 1 avec |α| < |β|
Nom associé
Grammaires Contextuelles (Context Sensitive Grammars)
Outil formel associé
Automate Linéaire Borné
Grammaire Forme des productions : A→α
Type 2 avec A∈N et α∈ (T∪N)*
Nom associé
Grammaires à Contexte Libre (Context Free Grammars)
Outil formel associé
Automate à Pile
Grammaire Forme des productions
Type 3 Grammaires linéaire droite (resp. gauche)
A→ωB | ω ou A→Bω | ω avec (A,B)∈ N2, ω∈T*
Grammaires régulière à droite (resp. à gauche) normalisée
A→aB | a ou A→Ba | a avec (A,B)∈ N2, a∈T
Nom associé
Grammaires Régulières (Regular Grammars)
Outil formel associé
Automate d’Etats Finis
ANNEXE B. Formes Normales des
Grammaires
Etape 3 :
Pour chaque production A’k→Aiγ
Faire Remplacer dans cette production Ai par ses membres droits de
production
Fait