Vous êtes sur la page 1sur 54

Université Chouaib Doukkali

Faculté des Sciences d’El Jadida


Département d’Informatique

Cours
De Compilation

Version 1

Année universitaire 2020-2021

1
Contenu
I.1. Langages ....................................................................................................................................................................................... 5
I.2. Opérations sur les langages ....................................................................................................................................................... 5
I.3. Expression régulières ................................................................................................................................................................. 6
I.3.1 Définition .............................................................................................................................................................................. 6
I.3.2 Propriétés .............................................................................................................................................................................. 6
I.3.3 Diagramme régulier ............................................................................................................................................................. 6
II.1. Automate d'états Finis Non déterministe : AFN................................................................................................................. 8
II.1.1 Définition ............................................................................................................................................................................ 8
II.1.2 Graphe d'un automate....................................................................................................................................................... 8
II.2. Automate d’états Fini Déterministe AFD ............................................................................................................................ 9
II.2.1 Définition ............................................................................................................................................................................ 9
II.2.2 Simulation d'un AFD (Algorithme) ................................................................................................................................ 9
II.3. Liaison entre une expression régulière et un automate .................................................................................................... 10
II.3.1. Diagramme régulier associé à un automate ................................................................................................................ 10
II.3.2 Automate associé à un diagramme régulier ................................................................................................................. 10
II.3.3 Langage régulier associé à un automate .............................................................................................................. 11
III.1. Définitions de base ................................................................................................................................................................ 13
III.2 Arbre de dérivation ................................................................................................................................................................ 14
III.3 Ambiguïté ................................................................................................................................................................................. 14
III.4. Productions particulières ...................................................................................................................................................... 15
Partie 2
I.1. Introduction ............................................................................................................................................................................... 17
I.2. La structure d'un compilateur ................................................................................................................................................. 17
I.2.1 Analyse lexical .................................................................................................................................................................... 17
I.2.2 Analyse syntaxique ............................................................................................................................................................ 17
I.2.3 La production du code objet ........................................................................................................................................... 17
2-4 L'optimisation du code ....................................................................................................................................................... 18
2.5 La gestion de la table des symboles ................................................................................................................................... 18
2.6 Le traitement des erreurs .................................................................................................................................................... 18
II.1. Introduction ............................................................................................................................................................................. 19
II.2. Approche algorithmique ........................................................................................................................................................ 19
II.3 Automates.................................................................................................................................................................................. 21
II.3.1 Introduction ...................................................................................................................................................................... 21
II.3.2 Générateur d'analyseur syntaxique ................................................................................................................................ 22
III.1. Introduction............................................................................................................................................................................ 23
III.1.1 La méthode ascendante ................................................................................................................................................. 23
III.1.2 La méthode descendante............................................................................................................................................... 24
III.1.3 Déterminisme et ambiguïté .......................................................................................................................................... 25
III.1.4 Classe de grammaire LL(1) ........................................................................................................................................... 25
III.1.4.1. Premiers et suivants .............................................................................................................................................. 25
III.1.4.2 Classe de grammaire LL(1) ................................................................................................................................... 26
III.2. Descente récursive................................................................................................................................................................. 26
III.2.1 Construction de l'arbre syntaxique .............................................................................................................................. 29
III.2.1.1. Représentation de l'arbre: liens explicites (pointeurs) ............................................................................ 29
III.2.1.2 Représentation de l'arbre: liens implicites .......................................................................................................... 32
III.3. Analyse syntaxique prédictive .............................................................................................................................................. 35
III.3.1 Simulation ........................................................................................................................................................................ 35
III.3.2 Construction de la table de transition ......................................................................................................................... 36
III.3.3.Construction de l’arbre syntaxique .............................................................................................................................. 38
Partie 3
I.I. Introduction ............................................................................................................................................................................... 40
I.2. Contrôles sémantiques ............................................................................................................................................................. 40
I.2.1. Variable non déclarée ....................................................................................................................................................... 40
I.2.2 Variable non affectée ........................................................................................................................................................ 40
I.2.3 Conflits entre types ........................................................................................................................................................... 41
I.3. Arbre sémantique: coloration de l'arbre syntaxique ............................................................................................................ 41

2
I.3.1 Instruction affectation ...................................................................................................................................................... 41
I.3.2 Instruction conditionnelle ................................................................................................................................................ 42
I.3.2 Instruction itérative ........................................................................................................................................................... 42
II.1. introduction .............................................................................................................................................................................. 44
II.2. Algorithme générale d'un interpréteur ................................................................................................................................. 44
II.3. Exemple .................................................................................................................................................................................... 45
III.1. Introduction............................................................................................................................................................................ 48
III.2. Déclarations ............................................................................................................................................................................ 48
III.3. Algorithme générale d'un générateur de code machine .................................................................................................. 50
III.3.1 Méthode itérative: liens implicites ............................................................................................................................... 50
III.3.2 Méthode itérative : liens implicites .............................................................................................................................. 50
III.4. Exemple: Instruction d'affectation ..................................................................................................................................... 51
III.4.1 Expression sans parenthèses et sans priorité entre les opérateurs ........................................................................ 51
IV.1. Introduction ............................................................................................................................................................................ 53
IV.2. Structure de données ............................................................................................................................................................. 53
IV.3. Grammaire ......................................................................................................................................................................... 54

3
Partie 1
Introduction à la théorie des langages

4
Chapitre 1
Définitions et concept de base

I.1. Langages
Qu'est-ce qu'un langage ? Le dictionnaire Larousse répond «emploi de la parole pour exprimer
les idées. Tout moyen de communiquer la pensée »
Chomsky (1957) donne une définition formelle: « un langage L(G) engendré par une
grammaire G est l'ensemble de toutes les phrases possible, c'est à dire l'ensemble de toutes les
formes syntaxiques constituées uniquement des symboles terminaux ».
Aho et Ulllmm (1973) ont donné des définitions aux termes suivants:
 Un vocabulaire ou alphabet V est un ensemble non vide de symboles.
Exemple: lettre= {A, B,…,Z} ; chiffre= { 0, 1, . . . ,9 } et binaire={ 0, 1} sont des alphabets .
 Une chaîne (phrase ou mot) α sur un alphabet est une séquence finie de symboles extraits de
V. On note la longueur d’une chaine |α|. La chaine vide est désignée par l’un des symboles ^
ou ε.
Exemple : |banane|=6 la chaine banane a pour longueur 6. Remarquons aussi que |ε|= 0.
 Un langage désigne un ensemble quelconque de chaines construites sur un alphabet fixé.
Exemple: {ε} est langage vide ; Pascal dont l'alphabet V={ program, begin, if, then ... end }
est un langage ; la langue française est un langage (langage naturel).
Si α et β sont deux chaines alors on définit :
 la concaténation de α et β : αβ, par exemple α= porte et β= mine alors αβ=portemine.
αε=εα=α : ε est l’élément neutre pour la concaténation.
 si on considère la concaténation comme un produit, alors : α0=ε et ∀𝑖 > 0 αi= αi-1 α et
comme εα=α alors α1=α.
I.2. Opérations sur les langages
A partir des langages bien définit, on construit d’autres langage en appliquant les opérations
décrites par le tableau ci-dessous:
Opération Définition
Union de L et M notée 𝐿 ∪ 𝑀 ou L+M 𝐿 ∪ 𝑀 = {𝛼 / 𝛼𝜖𝐿 𝑜𝑢 𝛼 ∈ 𝑀 }
Concaténation de L et M notée LM 𝐿𝑀 = {𝛼𝛽 / 𝛼𝜖𝐿 𝑒𝑡 𝛽 ∈ 𝑀 }
*
Fermeture de Kleene de L notée L 𝑖=∞ 𝑖
𝐿∗ = 𝑈𝑖=𝑛 𝐿
+
Fermeture positive de L notée L 𝑖=∞
𝐿 = 𝑈𝑖=1 𝐿𝑖
+

Exemple : Soit L={A, B, …, Z, a,b,…,z} et C={0,1,…,9} deux alphabets, alors L et C peuvent


être considéré comme des langages finis dont la longueur de toutes les chaines est égale à 1.
En appliquant les opérations de tableau ci-dessus. On crée de nouveau langages:
 L+C ensemble des lettres et des chiffres
 LC ensemble des chaines formées d’une lettre suivie d’un chiffre
 L4 ensemble de chaines de 4 lettres
 L* ensemble de toutes les chaines de lettres, y compris la chaine vide ε
 L(L+C)* ensemble de toutes les chaines de lettres et de chiffres commençant par une lettre :
c’est un identificateur
 C+ ensemble de toutes les chaines d’au moins un chiffre

5
I.3. Expression régulières
I.3.1 Définition
Dans cette section, on va présenter une notation, appelée expressions régulières, qui nous
permettra de définir avec précision les langages de ce genre.
Exemple : En Pascal un identificateur est définit comme suivant : lettre(lettre |chiffre)*.
Voici les règles qui définissent les expressions régulières sur un alphabet v :
1. ε est une expression régulière qui dénote le langage L = {ε}.
2. Si 𝑎 ∈ 𝑉, alors a est une expression régulière qui dénote le langage L ={ a}.
3. Supposons que r et s soient des expressions régulières dénotant les langages L (s) et L(r) :
 (𝑟) ∪ (𝑠) est une expression régulière dénotant 𝐿(𝑟) ∪ 𝐿(𝑠)
 (𝑟)(𝑠) est une expression régulière dénotant L(r)L(s)
 (𝑟)∗ est une expression régulière dénotant L(r).
 (r) est une expression régulière dénotant L(r).
Exemple: Soit V= {a,b} alors
 a|b est une expression régulière dénotant L={a,b} ;
 (a|b) (a|b)=aa|ab|ba|bb est une expression régulière dénotant L={aa,ab,ba,bb} ;
 a* est une expression régulière dénotant L={ε, a, aa, aaa, …} ;
 (a|b)* est une expression régulière dénotant L={ε, a, aa, aa, …,b,bb,…,ab,aab,…,ba,bba,…} ;
 a|a*b dénote L={a,b,ab,aab,aaab,…} ;
Si deux expressions r et s régulières dénotent le même langage, on dit que r et s sont
équivalentes et on écrit r=s. Par exemple (a|b)=(b|a)
I.3.2 Propriétés
Les expressions régulières obéissent à un certain nombre de propriétés algébriques:
Axiome Description
r|s=s|r | est commutatif
r|(s|t)=(r|s)|(r|t) | est associatif
(rs)t=r(st) La concaténation est associative
r(s|t)=rs|rt La concaténation est distributive à gauche par rapport à |
(s|t)r=sr|tr La concaténation est distributive à droite par rapport à |
εr=rε=r ε est l’élément neutre pour la concaténation
r*=(r|ε)* Relation entre * et ε
r** * est idempotent
Pour commodités de notation on donne des noms aux expressions régulières:
di → ri qui se lit: l'expression régulière ri a pour nom di
Exemples :
Pour lettre ={A=, 8 , . . . , Z , a , b , . . . , Z} on a lettre → A|B|...|Z|a|b|…|z .
Pour chiffre = (0, 2, ..., 9) on a chiffre → 0|1|2|2|…|9.
Pour un identificateur on écrit: id → lette (lettre |chiffre)*.
I.3.3 Diagramme régulier
Un diagramme régulier D sur l’alphabet V est un quintuple (P, I, F, ψ, μ) :
1. P est un ensemble fini non vide appelé ensemble des places ;

6
2. I ⊂ P P appelé ensembles des places initiales,
3. F ⊂ P ensembles des places finales ;
4. ψ : P →P qui associe à chaque place p un sous-ensemble ψ(p) ⊂ P, noté p!
5. μ : P →V application qui associe à chaque place p un élément μ(p) ∈ 𝑉 appelé marque de p
Exemple: Soit l'expression régulière A→ (al ac)+|bc, le diagramme correspondant est :

Place p Ensemble de p μ(p)


0 {0,1} a
1 {3} a
2 {4} b
3 {0,1} c
4 {ε} c

Proposition 1 : si deux langages L(r) et L(s) sur l'alphabet V sont représentables chacun par un
diagramme régulier alors :
1. 𝐿(𝑟) ∪ 𝐿(𝑠) = 𝐿(𝑟) + 𝐿(𝑠) est représentable par un diagramme régulier ;
2. L(r)L(s) est représentable par un diagramme régulier;
3. (L(r))+ est représentable par un diagramme régulier;

Tout langage régulier sur un alphabet V peut être représenté par un diagramme régulier

Proposition 2 : Tout langage régulier sur un alphabet V peut être représenté par un diagramme
régulier.

7
Chapitre 2
Automates d'états finis

II.1. Automate d'états Finis Non déterministe : AFN


II.1.1 Définition
Un AFN est un quintuple (E, ∑, e0, T, F) tel que :
1. E est l'ensemble des états.
2. ∑ est l'ensemble des symboles d'entrée (Alphabet ).
3. e0 est l'état de départ ou état initial.
4. F est l'ensemble d'états d'acceptation ou états finals.
5. T est une fonction (fonction de transition) définit de ExS vers E :
T : E*S→ E
p.s → q
Si je suis dans l'état p et je rencontre le symbole s, alors je passe dans l'état q.
II.1.2 Graphe d'un automate
Un AFN peut être représenté graphiquement comme un graphe orienté et étiqueté
Exemple 1: Soit un AFN qui reconnaît le langage (a|b)*abb. Le graphe de transition
correspondant est :

E={0,1,2,3}, ∑={a,b}, F={3},


T={0a→0, 0b→0,0a→1,1b→2,2b→3}
Exemple 2 : Soit un AFN reconnaissant le langage aa*|bb*

E={0,1,2,3,4}, ∑={a,b}, e0=0 et F={2,3}


T={ 0ε→1 , 0ε→3, 1a→2, 3b→4, 2a→2, 4b→4}
la chaine aaa est acceptée via les états 0,1,2,2. En effet, 0ε→1, 1a→2, 2a→2, 2a→2

8
II.2. Automate d’états Fini Déterministe AFD
II.2.1 Définition
Un AFD est un cas particulier de l'AFN dans lequel:
1. Aucun état n'a de ε-transition: ∀ 𝑞𝜖𝐸, il n’existe pas e ∈ 𝐸,/ 𝑒𝜀 → 𝑞
2. Si ea →q et ea →p, alors p=q: il y a au plus un arc étiqueté a qui quitte e.
Un AFD accepte une chaine d'entrée s si et seulement s'il existe au moins un chemin dans le
graphe de transition entre l'état de départ et un état final.
II.2.2 Simulation d'un AFD (Algorithme)
Données: Une chaine d'entrée x et un AFD D = (E, ∑, e0 T, F).
Résultat: La réponse « oui » si D accepte x, « non » sinon
debut
etat=e0
Répéter
sym=Symbole() ;
etat=Transit[etat,sym] ;
Jusqu’à (etat ∈ 𝐹) ou(etat=erreur)
si (etat=erreur) alors afficher( « non » sinon afficher « oui » ) ;
Fin

S1 S2 Sl Sp
0
1
.
.
i
.
.
n-1
Transit est la matrice de transition de l'AFD; elle est de la forme:
 L'entrée d'une ligne représente un état, celle d'une colonne représente le numéro symbole.
 un élément de la matrice est soit un état, soit un code d’erreur.
Le rôle de la fonction Symbole est simplement le codage des symboles:
fonction Symbole( s : caractère) :entier
code : entier0
debut
selon s faire
‘s1’ : code=1 ;
‘sj’ : code =j ;
.
.
‘sp’ : code =p ;
finselon
Symbole=code ;
finfonct
Exemple : Soit AFD qui accepte le langage (a|b)*abb

9
II.3. Liaison entre une expression régulière et un automate
II.3.1. Diagramme régulier associé à un automate
A tout automate A=(E, ∑, e0, T, F), on peut associer un diagramme régulier D=(P, I ,F,ψ,μ)
qui de façon évidente représente le même langage. Pour fixer les idées, nous considérons
l’automate suivant :

Le diagramme régulier associé est :

II.3.2 Automate associé à un diagramme régulier


Nous allons voir qu’à tout diagramme D={P,I,F,ψ,μ} sur un alphabet ∑ , on peut associer un
automate A={E,∑,e0,T,F) sur ∑, qui représente le même langage. Pour fixer les idées, nous
considérons le diagramme suivant représentant l’expression a*b|ba|b (a*b|cb|c)

Place p Ensemble p μ(p)


0 {0,2} a
1 {2} c
2 {ε} b

Le graphe correspondant est :

10
L’ensemble des sommets du graphe de transition E est l’ensemble des places du diagramme
régulier P augmenté d’un élément e0 distinct de toutes les places E=P+{e0}
L’ensemble des transitions du graphe T est construit selon les règles suivantes :

 ∀𝑝 ∈ 𝐼, 𝑒0 𝜇(𝑝) ⟶ 𝑝 est une arête du graphe (exemple e0a →1 et e0c →2) ;


 ∀𝑝 ∈ 𝑃, ∀𝑞 ∈ 𝑝!, 𝑝𝜇(𝑞) → 𝑞 est une arête du graphe (exemple 1a →1 , 1b →3) ;
 Toute arête du graphe est obtenue par l’une des règles précédentes.
 Enfin a est l'unique sommet initial de l'automate, et les sommets finals sont les places
terminales du diagramme (ens. F).

II.3.3 Langage régulier associé à un automate


Dans ce paragraphe, on remplace le mot vide ε par 1 et le caractère | par +.
Soit i ∈ 𝐸 , un mot de langage Li est obtenu par deux règles :
 le mot vide 𝑙 ∈ 𝐿𝑖
 si 𝑢 ∈ 𝐿𝑖 alors 𝑢𝑎 ∈ 𝐿𝑖
Exemple : soit l’automate suivant :

On définit quatre langages, un pour chaque état :


(1) Lx=1+Lxa (état x)
(2) Ly=Lxb+Lyb+Lza (état y)
(3) Lz=Lya (état z)
(4) Lt=Lzb+Lt(a+b) (état t)
On a donc un système d’équation linéaire à résoudre.
(1) donne
Lx(1-a) =1,
donc Lx=1/1-a = 1+a+a2+…+an+…=a* donc Lx=a*
Sachant que Lx =a* et Lz=Lya, alors
(2) devient :
Ly= a*b+Ly(b+a2) équivalent à
Ly(1-(b+a2))=a*b donc
Ly=a*b(b+a2)*
(4) donne

11
Lt(1-a-b)=Lzb
c’est à dire
𝐿𝑧 𝑏
𝐿𝑡 = = 𝐿𝑧 𝑏(1 + (𝑎 + 𝑏)2 + ⋯ + (𝑎 + 𝑏)𝑛 + ⋯ ) = 𝐿𝑧 𝑏(𝑎 + 𝑏)∗
(1 − (𝑎 + 𝑏))
or Lz=Lya donc 𝐿𝑡 = 𝐿𝑦 𝑎𝑏(𝑎 + 𝑏)∗
et 𝐿𝑧 = 𝑎 ∗ 𝑏(𝑏 + 𝑎2 )∗ 𝑎
Attention la multiplication n’est pas commutative

12
Chapitre 3
Les Grammaires

III.1. Définitions de base


Nous rappelons quelques termes définit dans le chapitre 1
Un vocabulaire V est un ensemble non vide de symboles.
Une chaîne est une suite finie de symboles.
L'ensemble V * est l'ensemble de toutes les chaînes formées de symboles appartenant au
vocabulaire V, y compris la chaîne vide dénotée ε.
Une grammaire G est un quadruple <VT,VN, S , P> où:
 VT est l'ensemble des symboles appelés terminaux.
 VN est l’ensemble des symboles appelés non-terminaux
 S est le symbole initial.
 P est un ensemble de règles de la forme: α→β
On a les propriétés suivantes:
𝛼𝜖𝑉 ∗ 𝛽𝜖𝑉 ∗ 𝑆 ∈ 𝑉𝑁
𝑉𝑇 ∪ 𝑉𝑁 = 𝑉 𝑉𝑇 ∩ 𝑉𝑁 = ∅
Notations souvent utilisées :
A..Z symboles non-terminaux.
a..z symboles terminaux.
α.. β chaînes.
Une dérivation est une suite de chaînes:
α0, α1, α2, …, αn n ≥ 0 telle que αi-1→αi pour i=1..n
On notera cela α0 →*αn
ou si n≥1 α0 →*αn
Toute chaîne η dérivable de S, c'est à dire telle que S → *αn une forme syntaxique.
Une phrase est une forme syntaxique qui n'est constituée que de symboles terminaux.
Un langage L(G) généré par une grammaire G est l'ensemble de toutes les phrases.
𝐿(𝐺) = {𝜂/𝜂𝜖𝑉𝑇∗ 𝑒𝑡 𝑆 →+ ℎ }
Les langages ont été classés par Chomsky (1 957) selon la classification suivante:

Type restriction sur les productions Langage ou grammaire


0 Aucune Récursivement énumérant
1 |α|≥|β| Sensible au contexte
2 A→α Indépendant du contexte (context-free)
3 A→a|aβ (linéaire droite) régulier
A→a|Ba (linéaire gauche)

13
De ces 4 types, le plus important pour nous est le type 2. En effet, une grammaire indépendante
du contexte peut être utilisée pour spécifier la structure syntaxique des langages de programmation
et la base des schémas de traduction.
III.2 Arbre de dérivation
Un arbre de dérivation D (ou arbre syntaxique) pour une grammaire G(VT,VN,S,P) est arbre
ordonné dont les nœuds appartiennent à 𝑉𝑇 ∪ 𝑉𝑁 ∪ {𝜀} et tel que:
 S est la racine de D.
 Si les Di (i=1..k) sont les sous arbres de racines Xi et les Xi sont les descendants directs de la
racine de D alors (S → X1X2…Xk ) ∈ P.
𝑋𝑖 ∈ 𝑉𝑁 ⇒ 𝐷𝑖 est arbre de dérivation pour G(VT,VN,S,P)
𝑋𝑖 ∈ 𝑉𝑇 ⇒ 𝐷𝑖 est le nœud Xi
 D1 est le seul sous arbre issu de la racine de D1 et la racine de D1 est ε : (S→ε) 𝜖 P.

Exemple : G: ({a, +,*, (,) }; {E,T,F},E,P ) avec


P= {E →E+T, E →T, T →T * F, T→F, F→(E), F →a}
Alors l'arbre de dérivation de la chaîne a + a est :

On remarque sur cet arbre qu'il y a l0 dérivations possible de a+ a : E →*a+a


Considérons 2 cas particuliers:
 On remplace toujours le symbole le plus à gauche (série de productions à gauche) :
𝐺 𝐺 𝐺 𝐺 𝐺 𝐺
𝐸→ 𝐸 + 𝑇 →𝑇 + 𝑇 →𝐹 + 𝑇→𝑎 + 𝑇→𝑎 + 𝐹→𝑎 + 𝑎
𝐺
 c'est la dérivation la plus à gauche, on la note 𝐸 → ∗ 𝑎 + 𝑎. Plus généralement, elle s'écrit
𝐺
𝑆 →* α
 On remplace toujours le symbole le plus à droite (série de productions à droite) :
𝐷 𝐷 𝐷 𝐷 𝐷 𝐷
𝐸→ 𝐸 + 𝑇 →𝐸 + 𝐹 →𝐸 + 𝑎→𝑇 + 𝑎→𝐹 + 𝑎→𝑎 + 𝑎
𝐷 𝐷
C’est la dérivation la plus à droite, on la note : 𝐸 →*a+a. Plus généralement, elle s’écrit 𝑆 → *β
III.3 Ambiguïté
Une grammaire G(VT,VN,S,P) est ambiguë s'il y a dans L(G) une phrase w qui a plusieurs
arbres de dérivation distincts.
Exemple :

14
E→E+E
E→E*E
E→(E)
E→ id
G est ambigus, car dans la phrase « a+b*c », elle ne spécifie ni l'associativité ni la priorité des
opérateurs + et *. On a donc pour cette phrase au moins deux arbres de dérivations :

Les règles de la grammaire deviennent alors:


E→E+T|T
T→T*F|F
F→(E)|id
Cette grammaire engendre le même langage, mais donne à + une priorité plus faible que celle
de * et définit une associativité à gauche pour ces deux opérateurs.
III.4. Productions particulières
A→Aα A est directement récursif à gauche.
A→αA A est directement récursif à droite.
A→* Aα A indirectement récursif à gauche.
A→*αA A est indirectement récursif à droite.
Dans tous les cas, A est récursif.
A →αAβ avec α≠ε et β≠ε A est directement auto-imbriquant.
A→+αAβ avec α≠ε et β≠ε A est indirectement auto-imbriquant
Différence entre une grammaire régulière et une grammaire indépendante de contexte:
La principale différence entre les 2 grammaires est que la grammaire régulière ne permet pas
auto-imbrication et par conséquent, elle permet les répétitions et les alternatives mais pas les
emboîtements du type:
((….)) ou begin begin ….. end end

15
Partie Il
Analyse des chaines

16
Chapitre 1
Etapes d'un compilateur

I.1. Introduction
L'analyse syntaxique peut être définie comme la détermination de l'arbre syntaxique d’une
chaine d'entrée donnée.
Cette analyse doit déterminer si la chaine d’entrée est syntaxiquement correcte, c'est à dire si
elle peut être dérivée par les productions du langage.
Il faut distinguer la syntaxe qui détermine si la chaine appartient au langage de la sémantique
qui analyse le sens de la chaîne.
Enfin, en général ce ne sont pas les chaines d'un programme source qui constituent l'entrée de
l'analyseur syntaxique, mais les chaines de sortie d'un premier analyseur : l'analyseur lexical
Cette brève introduction à la théorie des langages permet maintenant une description plus
complète d'un compilateur.
I.2. La structure d'un compilateur
Un compilateur peut être subdivisé en différentes phases:
1. L'analyse lexicale.
2. L’analyse syntaxique.
3. La production du code objet
4. t'optimisation du code.
5. La gestion d'une table des symboles.
6. le traitement d’erreurs.
Nous distinguerons les passes qui nécessitent à chaque fois un lecture du texte source des
phases qui constituent des opérations logiquement distinctes.
I.2.1 Analyse lexical
Son rôle est de lire le texte, de former des entités (tokens), d’éliminer les informations inutiles
(blancs, commentaires,...) et de présenter une chaine d’entrée à l’analyseur syntaxique.
I.2.2 Analyse syntaxique
Son rôle est de construire l'arbre syntaxique de deux manières différentes:
 Analyse syntaxique ascendante (bottom up):
L'analyseur cherche, à partir de la phrase et à l’aide des règles de la grammaire, à remonter
à l'élément initial (on monte des feuilles vers la racine).
 Analyse syntaxique descendante (top down)
L’analyseur cherche, à partir de l'élément initial et à l'aide des règles de la grammaire, à former
la phase donnée (on descend de la racine vers les feuilles).
I.2.3 La production du code objet
Le but final d'un compilateur est la production d'un texte en langage objet. Ce dernier peut
être un code intermédiaire, le code machine d'un ordinateur fictif. le code machine d'une machine
réelle (langage d'assemblage ou binaire) ou du macro code

17
2-4 L'optimisation du code
Le code produit par le compilateur doit être performant. C’est-à-dire rapide à l’exécution et
peu couteux en place mémoire. Nous pouvons distinguer ;
 Les optimisations indépendantes de la machine ;
 Les optimisations dépendantes de la machine.
2.5 La gestion de la table des symboles
Un compilateur doit construire, modifier et consulter une table des identificateurs tout au long
de son action. L'accès à cette table doit être très rapide et se baser sur des techniques telles que la
recherche dichotomique (binaire), les arbres binaires ou le <hash coding>.
2.6 Le traitement des erreurs
Un compilateur doit détecter des erreurs lexicales, syntaxiques ou sémantiques, donner des
diagnostics clairs et précis et être capable de poursuivre l'analyse du texte source.

18
Chapitre 2.
Analyse lexicographique

II.1. Introduction
L'analyse lexicale consiste à segmenter un texte source en un ensemble de mots que l'on
appelle traditionnellement « tokens »(leur terme exact est « lexème », ce qui signifie unité lexicale).
Il s'agit d'une part de déterminer la suite des caractères comprenant le token, et d'autre part
d’identifier le type de token (identificateur, nombre, opérateur, etc.).
Par exemple le texte suivant:
Totol :=T25+2.5*Totol ;
L'analyseur lexical doit être capable de comprendre qu'il s'agit de la suite de tokens suivants
Ident Toto1
Affectation :=
Identf T25
Op.arith +
Flottant 2.5
Op.arith *
Ident Toto1
Séparateur ;
Comment faire ? Nous envisagerons d'abord des techniques simples d:ns lesquelles l'analyseur
lexical sera construit « à la main ». Puis, nous verrons que cette analyse peut être formalisée par
l'intermédiaire d'automates finis. Enfin, Nous essayerons d'en dégager une technique générale
pour construire ce que l’on appelle des générateurs d'analyseurs (ex : le programme lex sur Unix).
II.2. Approche algorithmique
L'approche algorithmique consiste à traiter le problème directement en essayant d'écrire
directement à partir d'une idée intuitive de ce qu'est un mot.
On suppose que l'on dispose des fonctions suivantes:
 Lire(car) : lit un caractère car à partir du clavier ou d'un fichier.
 Chiffre(car) : teste si le caractère car et un chiffre.
 Lettre(car) : teste si le caractère car est un caractère.
L’exemple 1 présente une action traduisant l’analyseur lexicale d’un langage dont l’alphabet
contient :
 les identificateurs, les constantes entières;
 les symboles de comparaison : <,>, ≤,≥, == et !=
 les opérateurs : +,-,* et /
 le symbole d’affectation : =,
 la ponctuation: , et ; ...

L’exemple 2 présente est une fonction en langage C traduisant l'analyseur lexicale d'un langage
dont l'alphabet est semblable est celui de l'exemple 1. Les fonctions Lire(car), Chiffre(car) et

19
Lettre(car) sont remplacées respectivement par les instructions car=getc(pf), isdigit(car) et
isalpha(car).
Exemple 1 Exemple 2

Action Lexico #define MAXUS 10


us=ε #define FAUX 0
DebUS=Faux ; #define VRAI 1
Tanque DebUS= Faux faire char car, us[MAXUS] ;
int prem_synt ;
Tantque car=’_’ faire Lire(car) FILE*pf ;
Si car=’@’ alors Lire(car) ; void lexico()
Tanque car =’@’ faire Lire(car) ; {
Sinon int i ;
DebUS=Vrai ; if(prem_synt)
FinSi { car=getc(pf) ;
FinTanque prem_synt=FAUX ;
Si Lettre(car) alors }
Tanque Lettre(car) ou chiffre(car) faire while( isspace(car)) car=getc(pf) ;
us=us+car ; while(car==’%’)
Lire(car) ; { car=getc(pf);
while(car !=’%’) car=getc(pf);
Fintanque car=getc(pf);
Sinon while( isspace(car)) car=getc(pf);
Si Chiffre(car) alors }
Tanque Chiffre(car) faire i=0 ;
us=us+car ; if(isdigit(car))
lire(car) ; { while( isdigit(car) && (i<MAXUS-1))
FinTanque {
Sinon us[i++]=car;
us=car ; car=getc(pf);
Lire(car) ; }
Si ( car=’=’) et ( (us=’<’) us[i]=’\0’;
while( isdigit(car)) car=getc(pf);
ou(us=’>’)ou(us=’ !’)) }
alors Lire(car) ; else
Finsi {
Finsi while( isspace(car)) car=getc(pf);
Fin if(car==-1) {strcpy(us,”eof”);}
else
{
us[i++]=car;
car=getc(pf);
if(us[0]==’<’ &&( car==’=’))
{
us[i++]=car;
car=getc(pf);
}
us[i]=’\0’ ;
}
}
}

20
II.3 Automates
II.3.1 Introduction
Idée: on considère que lire un caractère peut faire passer d'un état dans un autre. Par exemple :
si je viens de lire le symbole = et suis à l'état 5, alors je passe à l'état 8. Les automates sont
souvent donnés sous la forme d'un graphe : les états sont les nœuds du graphe et les arcs
correspondent à la fonction de transition.
Graphe d’automate

Matrice de transition
, ; . % L C > > = | + - * / ≠
T 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
0 1 1 1 2 3 4 5 5 5 5 6 6 6 6 -1
1 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
2 2 2 2 7 2 2 2 2 2 2 2 2 2 2 2
3 9 9 9 9 3 3 9 9 9 9 9 9 9 9 9
4 9 9 9 9 9 4 9 9 9 9 9 9 9 9 9
5 9 9 9 9 9 9 9 9 8 9 9 9 9 9 9
6 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
7 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
Action Lexico
us=^ ; /*initialisation de l’unité lexicale à vide */
Etat=0 ; /* on commence à l’état intial 0 */
Répéter
Lire(car) ;
Sym=Symbole(car) ;
Etat=T[Etat, Sym] ;
Selon Etat faire
1 : us=us+car ; /* ponctuations : , ; et . */
2:; /*commentaire */
3 : us=us+car ; /*identificateur */
4 :us=us+car ; /* nombre entier */
5 : us=us+car ; /* >,<,= et | */
6: us=us+car ; /* operateurs +,-,* et / */
7: ; /fin commentaire */
8: us=us+car ; /* ≥ , ≤, == et != */
9 : si car ∈ {‘ ‘, \n, \t, fin fichier} alors ecrire(« Erreur Lexicale ») ;

21
-1 : ecrire(« Erreur lexicale ») ;
FinSelon
Jusqu’à (Etat=-1) ou (Etat=9) ; /*Etats finaux */
Fin Action
Remarque : la fonction symbole fait correspondre à chaque symbole un numéro
II.3.2 Générateur d'analyseur syntaxique
A partir des expressions régulières, on peut définir des automates qui reconnaissent le lexique
d’un langage particulier. Et -il existe des algorithmes permettant de passer d'une expression
régulière à un automate.
Malheureusement, il est difficile d’écrire simplement les automates qui correspondent à une
expression régulière un peu compliquée. Pour cela, on utilise souvent un générateur d’automates
finis déterministes comme par exemple lex sous Unix
Pour plus de détail sur lex, exécuter la commande" man lex" sous Unix.

22
Chapitre 3
Analyse Syntaxique

III.1. Introduction
Définition intuitive: l’analyse syntaxique est l'une des opérations majeures d’un compilateur
qui consiste à indiquer si un texte est grammaticalement correct et à en tirer une représentation
interne, que l'on appelle arbre syntaxique ou arbre abstrait.
Définition formelle: Soit G=(VN,VT,P,S) la grammaire d'un langage L et 𝑥 ∈ 𝑉𝑟∗ une chaîne,
l'analyse syntaxique de L est le processus d'établissement d'une correspondance entre l’axiome S
et la chaine terminale x.
A l'issue de ce processus on a deux possibilités:
 𝑥 ∈ 𝐿(𝐺) : on peut construire l'arbre syntaxique associé à la chaîne terminale.
 𝑥 ∈ 𝐿(𝐺) : la chaine est refusée

En générale il existe deux manière d''analyser une chaine:


1. En partant du la racine et en descendant jusqu’aux feuilles de l’arbre.
2. En partant des feuilles et en remontant jusqu’à la racine de l’arbre
A la première manière correspondent les méthodes dites descendantes ("top-down"); à la
seconde, les méthodes dites ascendantes (" bottom-up")
III.1.1 La méthode ascendante
La méthode ascendante consiste à partir de la chaine et à appliquer à chaque pas une réduction
à gauche jusqu'à ce qu’on arrive au symbole initial.
Exemple :
Soit G=(VN,VT,P,S) avec VN={A,B,R,E}, VT={= , +, (, ), a, b}, S=A
et
P= { A→B, B→R|(B), R→E=E, E→a|b|(E+E) } une grammaire
et
x=(a=(b+a)) une chaine à analyser.

23
Régle de réduction Chaine intermédiaires Arbre Syntaxique
E→a (a=(b+a))
E→b (E=(b+a))
E→a (E=(E+a))
E→(E+E) (E=(E+E))
R→E=E (E=E)
R→B (B)
A→B A
(a= (b+a))
Parmi les techniques ascendantes, on trouve:
 La présidence d'opérateur (Floyd. 1963)
 La simple précédence (W irth, 1966)
 La précédence faible (lchbiah et Morse, 1970)
 Les matrices de transitions (Sarnelsone t Bauer, I960)
 Les analyseurs LR(k) (Aho et Johnson1,1974, Aho et Ullman, 1973).
L'entier k dans les analyseurs LR(k) désigne que pour une chaine terminale x, il suffit de
connaître k éléments à partir de l'élément courant pour décider sans ambiguïté de la règle de
production à appliquer. Il existe des générateurs d’analyseurs syntaxiques ascendants LR (l), dont
le plus connu est le yacc (Yer another compiler compiler) sous unix. Pour plus d'informations sur
le yacc tapez la commande suivante sur un terminal unix: man yacc
III.1.2 La méthode descendante
Dans la méthode descendante on part de l'axiome de la grammaire pour arriver à la chaîne
donnée par la dérivation la plus à gauche.
Exemple :
Soit G=(VN,VT,P,S) avec VN={<ESS>, <CH>}, Vr={0,1,…,9}, S={<ESS>}
et
P={<ESS> ::=<CH>, <ESS> :=<CH><ESS>, <CH> :=0|1|…|9 } une grammaire
et
x=7346 une chaine à analyser.
Les étapes de processus d’analyse syntaxique de la chaine x=7346 est résumé par le
tableau suivant :
Règles appliquées Chaine intermédiaires ESS syntaxique
<ESS> :=<CH><ESS> <ESS>
<ESS> :=7 <CH><ESS>
<ESS> :=<CH><ESS> 7<ESS>
<ESS> :=3 7<CH><ESS>
<ESS> :=<CH><ESS> 7 3 <ESS>
<ESS> :=4 7 3 <CH> <ESS>
<ESS> :=<CH> 7 3 4 <ESS>
<ESS>:=6 7 3 4 <CH>
6 3 4 6

Parmi les techniques descendantes, on trouve:

24
 La descente récursive (Lucas, 1961; Wirth, l97l)
 Les analyseurs LL(k) dite prédictives ( Knuth,l 97l) qui sont des versions non récursives de la
descente récursive.
L'entier k dans les analyseurs LL(k) désigne que pour une chaîne terminale x, il suffit de
connaitre k éléments à partir de l'élément courant pour décider sans ambigüité de la règle de
production à appliquer.
III.1.3 Déterminisme et ambiguïté
Analyseur syntaxique non déterministe : on ne dispose pas de critères pour choisir la règle
à appliquer. Dans ce cas, on n’aura pas plusieurs arbres syntaxiques possibles pour une même
chaine terminale.
Analyseur syntaxique déterministe : on dispose de critères permettant de choisir sans
ambigüité la règle de production à appliquer à chaque fois que le problème de choix se pose.
L'arbre syntaxique est unique dans ce cas.
Grammaire ambiguë : si l’analyseur syntaxique est non déterministe, on dit que la grammaire
associée est ambiguë.
Exemple :
Soit G=({S,A}, {a,b,c,+,$} ,{S’ :=S$, S :=A+A, A :=S|a|b|c},S’ ) et la chaine x=a+b+c;
Pour cette chaine, on deux arbre syntaxiques dans la grammaire est ambiguë.

III.1.4 Classe de grammaire LL(1)


III.1.4.1. Premiers et suivants
Soit G=(VN,VT,P,S), 𝛼𝜖𝑉 ∗ , 𝛽𝜖𝑉 ∗ , 𝑥𝜖𝑉𝑇∗ et 𝐴 ∈ 𝑉𝑁 , nous définissons deux ensembles Prem et
Suiv de terminaux très utiles pour rendre une grammaire sous sa forme déterministe :
L’ensemble des premiers d’une chaine intermédiaire 𝛼 est constitué de terminaux susceptibles
de commencer la chaine 𝛼 :
Prem(𝛼)={ a ∈ 𝑉𝑇 , 𝛼 →*aβ}
L'ensemble des suivants d'un non terminal A est constitué de terminaux susceptibles de suivre
A:
𝑆𝑢𝑖𝑣(𝐴) = {𝑎 ∈ 𝑉𝑇 , 𝛼 →∗ 𝑥𝐴𝛽 𝑒𝑡 𝑎𝜖 𝑃𝑟𝑒𝑚(𝛽)}

25
III.1.4.2 Classe de grammaire LL(1)
Soit G=(VN,VT,P,S) , 𝛼𝑖 𝜖𝑉 ∗ , 𝑖 = 1 à 𝑛 𝑒𝑡 𝐴 ∈ 𝑉𝑁

𝑃𝑟𝑒𝑚(𝛼𝑖 ) ∩ 𝑃𝑟𝑒𝑚(𝛼𝑗 ) = { }, 𝑖 ≠ 𝑗 𝑒𝑡 𝛼𝑖 , 𝛼𝑗 ≠ { }
𝐺 𝑒𝑠𝑡 𝐿𝐿(1) ⇔ ∀𝐴 → 𝛼1 |𝛼2 | … 𝛼𝑛 {
𝑆𝑖 ∃𝑖 𝛼𝑖 →∗ ^^𝑎𝑙𝑜𝑟𝑠 𝑃𝑟𝑒𝑚(𝛼𝑗 ) ∩ 𝑆𝑢𝑖𝑣(𝐴) = { }, ∀𝑖 ≠ 𝑗

Exemple : S→AabCc, A→^|a|b|C et C→aA|b

Prem(AabCc)=Prem(A)={a,b} et Suiv(A)={a,c}, on voit bien que l’intersection entre


Prem(A) et Suiv(A) n’est pas vide. Donc cette Grammaire n’est pas LL(1).
Les conditions pour qu’une grammaire G soit LL(1) impliquent entre autre que :
 La grammaire ne doit pas comporter de règles de production récursives à gauche : A→A𝛽|𝛼
 La grammaire ne doit pas comporter de règles de production de type : A → Bα|Bβ

Donc, pour rendre une grammaire LL(1), il faut transformer les règles citées de la façon
suivante :
𝐴 → Aβ|α ⟹ A → αX et X → ^|βX

𝐴 → Bα|Bβ ⟹ A → BX et X → α|β

III.2. Descente récursive


La descente récursive est une méthode très simple pour traduire une grammaire(en particulier
LL(1)) sous forme d’un algorithme ou un programme écrit dans une langue évoluée. Ainsi, pour
chaque règle de production de la grammaire, on parcourt ses éléments de gauche à droite :
Symbole(s) rencontré(s) Action (s) à générer Exemple
A la rencontre d'un On génère l'entête d'une action nommée A Pour la règle A ::=BabC, on
symbole non terminal génère : Action A
𝐴 ∈ 𝑉𝑁 à gauche
A la rencontre d'un On génère l'entête d'une action nommée B Pour la règle A ::=BabC, on
symbole non terminal génère : appel B
𝐵 ∈ 𝑉𝑁 à droite de la
règle
A la rencontre d’un Ou génère un texte: si us=a alors on passe à Pour la règle A ::=BabC
symbole terminal l’élément suivant, aussi bien dans la règle de Action A
𝑎 ∈ 𝑉𝑇 à droite. production que dans la chaine terminale, sinon Si us=a alors Lexico(us)
on génère une erreur syntaxique. Sinon Err(1)
Si us=b alors Lexico(us)
Sinon Err(2)
Fin
Si pour une règle de Le choix de l'alternative se fait d'après A ::=α1| α2|… |αn
production, il existe l'ensemble Prem(α) des premiers de chaque Action A
plusieurs alternatives alternative. Si plus, ∃𝛼𝑖 /𝛼𝑖 →∗ 𝜀, alors le choix Si 𝑢𝑠 ∉ 𝑆𝑢𝑖𝑣(𝐴) alors
de l'alternative se fait aussi d'après l’ensemble Selon us faire
Suiv(A) des suivants du symbole non terminale 𝑢𝑠 ∈ 𝑃𝑟𝑒𝑚(𝛼1 ) :
de la partie gauche. traiter(α1);
……
𝑢𝑠 ∈
𝑃𝑟𝑒𝑚(𝛼𝑛 ) :traiter(αn) ;

Autre : Erreur ;

26
finSelon
FinSi
Fin
Exemples
(1) <prog> ::= ‘debut’<ListeInst>’fin’
(2) <ListeInst> ::= <Inst>|<Inst>’ ;’<ListeInst>
(3) <Inst> ::= idf ‘ :=’<expa>
(4) <expa> ::= <expa1>|<expa>’+’ <expa1>|<expa>’-’ <expa1>
(5) <expa1> ::= <expa2>|<expa1>’*’ <expa2>|<expa1>’/’ <expa2>
(6) <expa2> ::= ‘(‘ <expa> ‘)’ | idf| cste

Transformons d’abord la grammaire pour qu’elle soit LL(1). En effet, les regles (2), (4) et (5)
ne sont pas LL(1). En appliquant les transformations vues dans le paragraphe 1.4.2, la grammaire
devient :
(1) <program> ::= ‘debut ‘ <liste inst> ‘fin’
(2’) <list inst> ::= <instruction><suite inst>
(2’’) <suite inst> ::= ^|’ ;’ <liste inst>
(3) <instruction> ::= idf ‘ :=’ <expa>
(4’) <expa> ::= <expa1><suite expa1>
(4’’) <suite expa1> ::= ^|’+’<expa1><suite expa1> |’-‘<expa1><suite expa 1>
(5’) <expa1> ::= <expa2><suite expa2>
(5’’) <suite expa 2> ::= ^|’*’<expa2><suite expa2>|’/’<expa2><suite expa2>
(6) <expa2> ::= ‘(‘<expa>’)’|idf|cste

Exercice : Montrer que la grammaire est donc LL(1) ?


Maintenant, on peut écrire l’analyseur syntaxique descendant
Action program
Si us= ‘debut’ alors
Lexico(us) ;
ListInst ;
Si us !=’fin’ alors Erreur(« fin attendu ») ;
Sinon Erreur(« début attendu ») ;
Fin
Action SuiteInst
Si us≠’fin’ alors
Si us=’ ;’ alors
Lexico(us) ;
ListeInst ;
Sinon Erreur ( ‘fin attendu’) ;
Sinon Erreur(‘attendu’) ;
Fin
Action Instruction
Si identif(us) alors
Lexico(us) ;
Si us=’ :=’ alors
Lexico(us) ;
Expa ;
Sinon Erreur(‘ := attendu ‘) ;
Sinon Erreur(‘identificateur attendu’) ;
Fin

27
Action SuiteExpa1
Si us≠’fin’ et us ≠’ ;’ alors
Si us=’+’ alors
Lexico(us) ;
Expa1 ;
SuiteExpa1 ;
Sinon
Si us=’-‘ alors
Lexico(us) ;
Expa1 ;
SuiteExpa1 ;
Sinon Erreur(‘+ ou – attendu ‘) ;
Finsi
Finsi
Fin
Action ListeInst
Instruction ;
SuiteInst ;
Fin
Action Expa
Expa1 ;
SuiteExpa1 ;
Fin
Action Expa1
Expa2 ;
SuiteExpa2 ;
Fin
Action SuiteExpa2
Si us ∉{‘fin’, ‘+’,’-‘} alors
Si us=’*’ alors
Lexico(us) ;
Expa2 ;
SuiteExpa2 ;
Sinon
Si us=’/’ alors
Lexico(us) ;
Expa2 ;
SuiteExpa2 ;
Sinon Erreur(‘* ou / attendu’) ;
FinSi
FinSi
Fin
Action Expa2
Si identif(us) ou conste(us) alors
Lexico(us) ;
Sinon
Si us=’(‘ alors
Lexico(us) ;
Expa ;
Si us=’)’ alors Lexico(us)
Sinon Erreur(‘) attendu’) ;

28
Sinon Erreur(‘opérande ou ( attendu’) ;
FinSi
Fin
III.2.1 Construction de l'arbre syntaxique
En vérifiant la syntaxe d’un programme, on en profite pour rassembler le maximum
d’informations sous forme des structures de données qui permettent ultérieurement d’être
indépendant de l’analyseur syntaxique et de faciliter les autres phases de construction d’un
compilateur telles que l’analyse sémantique, la génération de code et l’optimisation de code.
Ces structures sont en générale :
 Une ou plusieurs tables des symboles, qui rassemblent les informations sur les variables, les
constantes, les noms des procédures et des fonctions, les étiquettes, .... En générale, il s'agit
d'informations sur la partie déclarative d'un programme.
 Un arbre syntaxique, qui représente la partie dynamique du programme. En générale, il s'agit
de la liste d'instructions

Cette traduction ce fait par l'insertion des appels d'actions supplémentaires au sein des
procédures de l'analyseur syntaxique, à des endroits adéquats. Ainsi, on associe à chaque règle de
production de la grammaire, une ou plusieurs actions qui permettent d'obtenir l'arbre. Les noms
des actions insérées dans une règle sont traités comme des non terminaux. Lorsque le nom d'une
action suit immédiatement un élément terminal, l'acquisition de l'unité syntaxique suivante suit
l'appel de l'action correspondant au nom de l'action.
Exemple A ::=BafC
B
Si us=’a’ alors
f;
Lexico(us) ;
C
Sinon
Erreur(‘ a attendu’) ;
Finsi
La définition de ces actions nous entraînent à choisir ses structures de données (problème
algorithmique). Ainsi, on a plusieurs stratégies pour représenter l’arbre et les tables des symboles:
 représentation de l'arbre avec des liens explicites (pointeurs) ou avec des liens implicites
(tableaux);
 représentation des tables avec des liens explicites (pointeurs), avec des liens implicites
(tableaux)
 Représentation hybrides où on mélange les deux stratégies

III.2.1.1. Représentation de l'arbre: liens explicites (pointeurs)


Exemple: Reprenant la grammaire du paragraphe précédents sous sa forme LL(l) en y ajoutant
des règles représentant la liste de déclaration des variables et en y insérant des actions de créations
de l'arbres syntaxique.
(1) <prog> ::= ‘debut ‘f000<ListeDec><ListeInst>f21’fin’
(2) <ListeDec> ::= <Dec><SuiteDec>f22
(3) <SuiteDec> ::= ^fnil | <ListeDec>
(4) <Dec> ::= <Type><ListeVar>f23
(5) <ListeVar> ::= idf f01<SuiteVar>f24
(6) <SuiteVar> ::= ^fnil | ‘;’<ListeVar>

29
(7) <Type> ::= entire f11|reel f12
(8) <ListeInst> ::= <Instruction><SuiteInst>f25
(9) <SuiteInst> ::= ^fnil|’;’<ListeInst>
(10) <Instruction> ::= idf f02 ‘ :=’ <Expa> f26
(11) <Expa> ::= <expa1><SuiteExpa1>f27
(12) <SuiteExpa1> ::= ^fnil | ‘+’<Expa>fplus|‘-’<Expa>fmoins
(13) <Expa1> ::= <Expa2><SuiteExpa2>f28
(14) <SuiteExpa2> ::= ^fnil | ‘*’<Expa1>fmul|‘/’<Expa1>fdiv
(15) <expa2> ::= ‘(‘<exp>’)’|idff03|cstef04|cster f05
Construisons d'abord pour chaque règle le sous arbre correspondant:
Sous arbre Racine Nb fils Valeurs possibles
<prog> Prog 2 {prog}
<ListeDec> Listedec 2 {listedec}U{valeurs possibles d’un nœud <Dec>}
<Dec> Entier/reel 1 {entier, réel }
<ListeVar> Listevar 2 {idf, listevar}
<ListeInst> ListeInst 2 {listeInstr}U{valeur possibles d’un nœud <Instr>
<Inst> Affect 2 {affect>
<expa> Plus/moins 2 {plus, moins}U{valeur possibles d’un nœud <expa1>}
<expa1> Mul/div 2 {mul,div}U{valeurs possibles d’un nœud <expa2> }
<expa2> Idf/cste/cstr 0 {idf,cste,cstr}U{valeurs possibles d’un nœud <Expa> }
Ce tableau, nous amène à définir les structures suivantes:
Déclaration en Pascal Declaration en C
NomNoeud={prog, listedec, listevar, listeinst, Enum NomNoeud
entier, reel, affect, plus, moins, mul, div, idf, cste) { prog,
Pnoeud=^Nœud ; listedec,
Nœud=Record listevar,
Nom :NomNoeud listeinst,
Case Nom of entier,
idf, cste : (val :integer) ; reel,
cstr : (val:reel) affect,
entier, reel :(fils :Pnoeud) ; plus,
prog, affect, moins,
listedec,listevar mul,
listeinst, div,
plus,moins idf,
mul,div : (fg, fd :Pnoeud) ; cste
end ; }
Typedef struct Nœud
{
int Nom ;
Union
{
int idf, cste ;
float cstr ;
struct Nœud*fils ;
struct Nœud*fg,*fd ;
}
}Tnoeud ;

Les actions de création de l’arbre sont définies donc de la manière suivante:


Action f21
Nouveau(p)
p^.fg=pg ;

30
p^.fd=pd ;
p^.Nom=prog ;
fin ;
Action f22
Nouveau(p)
p^.fg=pg ;
p^.fd=pd ;
p^.Nom=listedec ;
fin ;
Action f23
p^.fils=pd ;
fin ;
Action f24
Nouveau(p)
p^.fg=pg ;
p^.fd=pd ;
p^.Nom=listevar ;
fin ;
Action f25
Nouveau(p)
p^.fg=pg ;
p^.fd=pd ;
p^.Nom=listeinst ;
fin ;
Action f26
si (fd <>NIL) alors
Nouveau(p) ;
Selon cus faire
plus : p^.Nom=plus ;
moins : p^.Nom=moins ;
Fselon
p^.fg=pg ;
p^.fd=pd ;
Fsi;
fin
Action f27
si (fd <>NIL) alors
Nouveau(p) ;
Selon cus faire
mul : p^.Nom=mul ;
div : p^.Nom=div ;
Fselon
p^.fg=pg ;
p^.fd=pd ;
Fsi;
fin
Action f11
Nouveau(p)
p^.Nom=entier ;
fin ;
Action f12
Nouveau(p)
p^.Nom=réel ;
fin ;
Action f01
Nouveau(p)

31
p^.Nom=idf ;
fin ;
Action f02
Nouveau(p)
p^.Nom=idf ;
fin ;
Action f03
Nouveau(p)
p^.Nom=idf ;
fin ;
Action f04
Nouveau(p)
p^.Nom=cstc ;
p^.Val=ConvEntier(us) ;
fin ;
Action f05
Nouveau(p)
p^.Nom=cstc ;
p^.Val=ConvReel(us) ;
fin ;
Action fnil
p=NIL ;
fin ;
Action fplus
cus=plus ;
fin ;
Action fmoins
cus=mul ;
fin ;
Action fdiv
cus=div ;
fin ;
III.2.1.2 Représentation de l'arbre: liens implicites
Dans ce cas, l’arbre syntaxique est représenté par des tableaux à une ou plusieurs dimensions.
En générale :
 la partie dynamique de la grammaire est représentée par un vecteur
 la partie statique est représentée par une ou plusieurs tables d’enregistrement (table des
symboles, table des constantes,...)

Et pour faciliter le parcourt et/ou le traitement des informations stockées dans l’arbre, on
représente ce dernier sous sa forme post fixée généralisée:
Voici quelques exemples d'expressions sous forme post fixée:
B+C est remplacée par BC+
A=(A*B+C))-1 est remplacée par AABC+*1:=
Exemple : reprenant l’exemple du paragraphe précédent.
Ecrivons un programme dans ces langages :
debut
entier i,j ;
réel x,y,z ;
i=4,j=9 ;

32
x=0.5 ;
y=x*(i+j) ;
z=y+x*(i-j) ;
fin
a) Déclarations
Les déclarations sont utilisées pour:
 indiquer la nature des objets utilisés dans un langage de programmation, ce qui permet de
détecter des opérations interdites et/ou de signaler le besoin d'une conversion
 réserver la place en mémoire pour chacun des objets.

D'où les structures suivantes, constituées d’une table des symboles, d’une table des constantes
et d’un vecteur représentant l’arbre :
tds

Idf Type Taille …


i Entier 1
j Entier 1
x Réel 2
y Réel 2
z Réel 2
.
.
tcst
Valeur …
4
9
0.5

b) instructions
Représentons l’arbre sous sa forme post fixe
Arbre i 4:= j9:= x0.5:= yxij+*:= zyxij-*/:=
Cet arbre est construit aussi par l’insertion des actions à des endroits adéquats.
(1) <prog> ::= ‘debut’f1<ListeDec><ListeInst>’fin’f2
(2) <ListeDec> ::= <Dec><SuiteDec>
(3) <SuiteDec> ::= ^|<ListeDec>
(4) <Dec> ::= <Type><ListeVar>
(5) <ListeVar> ::= idf ft1 <SuiteVar>
(6) <SuiteVar> ::= ^|’ ;’<ListeVar>
(7) <Type> ::= entier ft2|réel ft3
(8) <ListeInst> ::= <Instruction><SuiteInst>
(9) <SuiteInst> ::= ^|’ ;’<ListeInst>
(10) <Instruction> ::= idf fa1 ‘ :=’ <expa> fa2
(11) <Expa> ::= <Expa1><SuiteExpa1>
(12) <SuiteExpa1> ::= ^|’+’<Expa>fa3|’-’<Expa>fa4
(13) <Expa1> ::= <Expa2><SuiteExpa2>
(14) <SuiteExpa2> ::= ^|’*’<Expa1>fa5|’/’<Expa1>fa6
(15) <Expa2> ::= ‘(‘<Expa>’)’|idffa7|cste fa8|cstr fa9

L’action f1 initialise les variables.


L'action f2 range un symbole indiquant la fin dans l'arbre.

33
Les actions ftl, ft2 et ft3 rangent un identificateur, son type et sa taille dans la table des
identificateurs tds.
Les actions fa1 à fa9 rangent les codes des opérandes ( idf, cste, cstr) et ceux des opérateurs ( :=,
+, -, *,/).
Les actions fa8 et fa9, rangent en plus les constantes dans la table des constantes tcst.
Action f1
Initialisation d’Arbre,
Initialisation de TabSym;
it=1 ; ic=1 ; ia=i ;
Maxlt=1000 ;
fin ;
Action f2
/* on range dans l’arbre un symbole de fin */
Arbre[ia]=code(‘$’) ;
Fin
Action ft1
/* on range dans la table des symboles d’idf */
tds[it].idf=us ;
tds[it].type=typeidf ;
tds[it].taille=tailleidf ;
it=it+1 ;
Fin ;
Action ft2
typeidf=entier ;
tailleidf=2 ;
Fin ;
Action ft3
typeidf=reel ;
tailleidf=4 ;
Fin ;
Action fa1
iidf=Chercher(us,tds) ;
Arbre(ia)=iidf ;
ia=ia+1 ;
Fin ;
Action fa2
Arbre(ia)=affect ;
ia=ia+1 ;
Fin ;

Action fa3
Arbre(ia)=plus ;
ia=ia+1 ;
Fin ;
Action fa4
Arbre(ia)=moins ;
ia=ia+1 ;
Fin ;
Action fa5
Arbre(ia)=mul ;
ia=ia+1 ;
Fin ;
Action fa6
Arbre(ia)= ;

34
ia=ia+1 ;
Fin ;
Action fa7
iidf=Chercher(us,tds) ;
Arbre(ia)=iidf ;
ia=ia+1 ;
Fin ;
Action fa8
test[ic].val=ConvEntier(us);
Arbre(ia)=MaxIt+ic ;
ic=ic+1;
ia=ia+1 ;
Fin ;
Action fa9
test[ic].val=ConvReel(us);
Arbre(ia)=MaxIt+ic ;
ic=ic+1;
ia=ia+1 ;
Fin ;

III.3. Analyse syntaxique prédictive


Un analyseur LL(l) prédictif est une version non récursive de la descente récursive.
Un analyseur prédictif utilise un ensemble de structures, à savoir:
 Une pile pour contenir les symboles en cours pendant les appels récursifs.
 Une table de transition contenant les relations entre les non terminaux et les terminaux

D'une autre manière, l'analyseur syntaxique prédictif est simplement un automate à pile.
III.3.1 Simulation
On suppose que la chaine à analyser se termine par un symbole terminal $ et qu'on dispose de
six actions:
 Vide(Pile) qui teste si la pile est vide ou non-
 Sommet(Pile) qui retourne le symbole (terminal ou non terminal) se trouvant au sommet de la
pile.
 Lexico(us) qui est l'analyseur lexicale, donc qui lit le symbole courant us.
 Empiler(PileX) qui empile un symbole X dans la pile Pile.
 Dépiter (Pile,Y) qui dépile un symbole Y de la pile Pile
 Terminal(X) qui teste si un symbole X est un terminal ou un non terminal.

Analyseur prédictif
Debut
Lexico(us);
Empiler ($);
Tant que non( vide(Pile)) faire
X=Sornmet(Pile);
Si Termina(X) alors
Si X=us alors
/* X est le symbole attendu */
Lexico(us) ;
Dépiler(Pile,X);

35
Sinon Erreur(‘us attendu’ ); .
Fsi
Sinon
Si Transition[X,us]='X→Y1Y2…Yk' alors
Dépiler (Pile ;X) ;
Empiler(Pile,Yk);
.
.
Empiler(Pile,Yl);
Sinon Erreur('us attendu\;
Fsi
Fsi
FinTanque
Fin

III.3.2 Construction de la table de transition


Pour chaque régle A→α faire
si a∈Prem(α) alors
Transition[A,a]=’A→α’ ;
Fsi ;
Si α=^ alors
Pour chaque b∈Suiv(A) faire
Transition [A,b]=’A→α’ ;
FinPour
Fsi ;
FinPour ;
Exemple:
Soit la grammaire donnée par le tableau ci-dessous. Construisons la table de transition et
analyser la phrase suivante : {j=0 ; j=i+3}

36
N° Régle de production
1 PROG →’{‘LI’}’
2 LI → I SI
3 SI →^
4 SI → I SI
5 I → ‘;’
6 I → idf ‘=’ E
7 E → F SF
8 SF →^
9 SF → ‘+’ F SF
10 SF → ‘-’ F SF
11 F → P SP
12 SP →^
13 SP → ‘*’ P SP
14 SP → ‘/’ P SP
15 P → idf
16 P → cste
17 P → ‘(‘ E’)’
La table de transition de l'analyseur est
TA { } ; = + - * / ( ) idf cste
PR 1
LI 2 2
I 3 6
SI 3 4 4
E 7 7 7
F 11 11 11
SF 8 8 9 10 8 8 8
P 17 15 16
SP 12 12 12 12 13 14 12 12
Exécution

Avancement dans Phrase Unité lue us Sommet Règle Opérations Etat de


Pile application Effectuées la pile
{i=0 ; j=i+3} § § Empiler §PR
{i=0 ; j=i+3} § { PR 1 Dépiler, Empiler §}LI{
{ { Dépiler, Lire §}LI
{i=0 ; j=i+3} § i LI 2 Dépiler, Empiler §}SI I
i I 5 Dépiler, Empiler §}SI E=idf
i Idf Dépiler, Lire §}SI E
= = Dépiler, Lire §}SI E
0 E 7 Dépiler, Empiler §}SI SF F
0 F 8 Dépiler, Empiler §}SI SF SP P
0 P 16 Dépiler, Empiler §}SI SF SP cste
0 cste Dépiler, Lire §}SI SF SP
; SP Dépiler §}SI SF
; SP Dépiler §}SI
; SI 4 Dépiler, Empiler §}SI I
; I 5 Dépiler, Empiler §}SI ;
Dépiler, Lire §}SI
Dépiler, Empiler §}SI I
Dépiler, Empiler §}SI E=idf
Dépiler, Lire §}SI E=
Dépiler, Lire §}SI E
Dépiler, Empiler §}SI SF F

37
Dépiler, Empiler §}SI SF SP P
Dépiler, Empiler §}SI SF SP idf
Dépiler, Lire §}SI SF SP
Dépiler §}SI SF
Dépiler, Empiler §}SI SF F +
Dépiler, Lire §}SI SF F
Dépiler, Empiler §}SI SF SP P
Dépiler, Empiler §}SI SF SP cste
Dépiler, Lire §}SI SF SP
Dépiler §}SI SF
Dépiler §}SI
Dépiler §}
Dépiler, Lire §

III.3.3.Construction de l’arbre syntaxique


Comme pour la descente récursive, pour construire l’arbre syntaxique (ou abstrait), il faut
insérer des actions dans la grammaire LL(1) puis les définir. Au niveau de l’analyseur, ces actions
se traitent comme les non terminaux, c’est-à-dire, elles seront empilées dans la pile et dépilées.
Mais après avoir dépiler le nom d’une action, il faut l’appeler. Le nom d’une action n’intervient
pas dans la construction de la table du transition.
Exercice :

Ecrire les actions permettant de créer l’arbre abstrait pour la grammaire du paragraphe III.3.2 en
utilisant l’analyseur prédictif ?

38
Partie III
Sémantique, génération du code et
interprétation

39
Chapitre 1.
Analyse sémantique

I.I. Introduction
L'analyse sémantique peut être le processus de vérification de la cohérence sémantique d’une
chaine d’entrée donnée.
Cette analyse doit déterminer si la chaîne d’entrée est sémantiquement correcte, c’est-à-dire si
la phrase est grammaticalement correcte ou d’une autre manière si la phrase a un sens.
En réalité, ce ne sont pas les chaines d’un programme source qui constituent l’entrée de
l’analyseur sémantique, mais l’arbre syntaxique construit par l’analyseur syntaxique.
I.2. Contrôles sémantiques
I.2.1. Variable non déclarée
Soit le programme suivant
Début
Entier A, B ;
Lire(A,B) ;
C :=A+B ;
Fin
La variable C n’est pas déclarée ; on a donc deux possibilités :
 attribution d’un type à C par défaut
 message d’erreur : variable non déclarée.

Arbre ARB sous sa forme post fixée est

A B Lire C A B + := $

La table des symboles TDS


Idf Type Taille …
A Entier 2
B Entier 2
C Réel 4

Le contrôle sémantique ici, consiste à attribuer le type réel à la variable C.


I.2.2 Variable non affectée
Soit le programme suivant
Début
Entier A, B ;
A :=B+1 ;
Fin
La variable B n’est pas reçue de valeur ; on a donc deux possibilités :
 attribution d’une valeur à B par défaut
 message d’erreur : variable non affectée.
Arbre ARB sous sa forme post fixée est
A B 1 + := $

40
La table des symboles TDS
Idf Type Taille Valauer
A Entier 2 1
B Entier 2 0

Le contrôle sémantique ici, consiste à attribuer une valeur par défaut à la variable B. (B=0).
I.2.3 Conflits entre types
Soit le programme suivant
Début
Entier A, B ;
Réel C ;
Lire(A,B,C) ;
A :=A*(B+C) ;
Fin
La variable A n’aurait du être déclarée réelle et non pas entier, puisque la partie droite de
l’affectation est une expression réelle, on a donc deux possibilités :
 notre compilateur autorise les opérations entre variables de type différents et effectue les
conversions correspondantes,
 message d’erreur : conflit entre types.
Arbre ARB sous sa forme post fixée est
A B C Lire A A B C + * := $
La table des symboles TDS
Idf Type Taille …
A Entier 2
B Entier 2
C Reel 4
Le contrôle sémantique ici par exemple est l’affichage d’un message d’erreur : conflit entre
types.
I.3. Arbre sémantique: coloration de l'arbre syntaxique
Pratiquement, l’analyseur sémantique parcourt l’arbre syntaxique (abstrait) et suivant
l’opérateur rencontré, il génère un contrôle sémantiquement qui est :
 Soit une erreur sémantique,
 Soit des modifications apportées dans l’arbre syntaxique et/ou la table des synrboles :
coloration de l’arbre.
L'arbre syntaxique coloré s'appelle l’arbre sémantique.
I.3.1 Instruction affectation
<affectation>:: = <partie gauche> ←<partie droite>
Liens implicites
La forme poste-fixée généralisée (FPF) de l'affectation s’écrit :
<FPF Affect>::= <FPF PG> <FPF PD> ←
Le sous arbre associé:
FPF PG FPF PD ← $
Liens explicites

41
I.3.2 Instruction conditionnelle
<conditionnelle:> ::= si <condition> alors< listeinst>sinon< listeinst>f si
Liens implicites
La forme poste-fixée généralisée (FPF) de l'instruction conditionnelle s’écrit:
<FPF Conditionnel le>::=<FPF Condition> BF <FPF Listeinst> B <FPF Liste inst >
Le sous arbre associé :

Liens explicites

LI : Liste Inst

I.3.2 Instruction itérative


<conditionnelle:> ::= tantque <condition> répéter< listeinst>Fintanque
Liens implicites
La forme poste-fixée généralisée (FPF) de l'instruction itérative s’écrit:
<FPF itérative>::=<FPF Condition> BF <FPF Listeinst> B
Le sous arbre associé :

Liens explicites

42
LI : Liste Inst

43
Chapitre 2.
Interprétation

II.1. introduction

L'interpréteur est un programme qui reçoit En entrée:


 Le code intermédiaire sous forme d’un arbre: arbres syntaxique
 Lest ables des symboles créent par l'analyseur syntaxique et complétées par l'analyseur
sémantique:
- Tables des identificateurs
- Tables des constantes
- Tables des actions
En sortie:
Il utilise les périphériques de sortie d’une machine (l’écran, l’imprimante., . . .)
En Entrée/Sortie: utilise
 La mémoire centrale MC.
 Une pile d'évaluation PILE.

II.2. Algorithme générale d'un interpréteur


On suppose que l'on dispose des fonctions suivantes:
 Prendre (ELMT ) : prend un élément de l’arbre, |

44
 opérande(ELMT ) : teste si ELMT est une opérande . ( idf, constant, …)
 Traiter Opérande(ELMT): Traite l'opérande (conversion, recherche dans la TDS, Empiler, …)
 Traiter Opérateur(ElMT) : Traiter l'opérateur (Empiler, effectuer :+,-,*,/, :=, branchement,
arrêt($), …).
Action Interpréteur
Début
Prendre (ELM'l');
Tanque ELMT <> $ faire
Si Opérande (ELMT) alors
TraiterOpérande(ELMT);
Sinon
TraiterOpérateur(EI.MT);
Fsi
Prendre(ELMT);
Fin Tanque
Fin

II.3. Exemple
Soit un langage contenant des instructions d’affectations, des valeurs entières et réelles, les
tableaux d’entiers à une dimension et les constantes chaines.

NbElt : Égale au nombre d’éléments d’un tableau pour une variable tableau et égale I dans tous les
autres cas.
EmpRel : l’emplacement relatif d’un idf ( resp. d 'une constante) dans la mémoire centrale par rapport
à la base de zone des idf ( resp. des constantes) Debldf ( resp. DebCon):
EmpRef[i]=EmpRef[i-1]+NbEl t[i] * taille[i]
Codage:
 Des identificateurs : indice is dans TDS de 1 à 1000
 Des constantes : indice dans TDC, is +1000 : 1001 à 2000.
 Opérateurs :

Opérateur Code Description

45
+ 2001 Addition
- 2002 Soustraction
* 2003 Produit
/ 2004 Division
← 2005 Affectation
T 2006 Opérateur d’
$ 2007
Action interpréteur
Début
ia=1 ;
ip=deblp ;
ELMT=ARBRE[ia] ;
Tant que ELMT<> 2007 faire
Si ELMT ≤ 2000 alors
TraiterOpérande(ELMT) ;
Sinon
TraiterOpérateur(ELMT) ;
Fsi
ia :=ia+1 ;
ELMT= ARBRE[ia] ;
Fin Tanque
Fin
Action TraiterOpérande(ELMT)
Début
Si ELMT >1000 alors
ADR=DcbCon EmpRelC[ELMT-1000] ;
Sinon
ADR=DebIdf+EmpRell[ELMT] ;
Fsi
Empiler(PILE, ADR) ;
Fin
Action TraiterOpérateur(ELMT)
Debut
selon EMT faire
2005 : Dépiler(PILE, ADR2) ;
Dépiler(PILE, ADR1) ;
MC[ADR1]=MC[ADR2] ;
imc=imc+1 ;
2001-2004 : Depiler(PILE, ADR2) ;
Dépiler(PILE,ADR1) ;
Selon ELMT faire
2001 : OPDE=MC[ADR1]+MC[ADR2] ;
2002 : OPDE=MC[ADR1]-MC[ADR2] ;
2003 : OPDE=MC[ADR1]*MC[ADR2] ;
2004 : OPDE=MC[ADR1]/MC[ADR2] ;
fselon
MC[imc]=OPDE ;

46
Empiler(PILE,imc) ;
imc=imc+1 ;
2006 : Dépiler(PILE, ADRi) ;
Dépiler (PILE,ADRT) ;
ADRTi=ADRT+MC[ADRi] ;
Empiler(ADRTi) ;
Fselon
Fin

47
Chapitre 3.
Génération du code machine

III.1. Introduction
Après l’analyse sémantique de l'arbre abstrait, commence la phrase de traduction de ce dernier
en un autre langage généralement proche du langage machine :
 Si l'arbre est traduit directement dans le langage de la machine, on parle du code machine. Les
assembleurs 80286 et 68000 sont deux exemples du code machine.
 Si l'arbre est traduit en autre code, on parle du code intermédiaire. La construction de l'arbre
lui-même sous forme post-fixée est une génération du code intermédiaire (chapitres
précédents).
On remarque qu’on peut passer par plusieurs générations de codes intermédiaires successives
pour générer le code machine ou pour interpréter le dernier code intermédiaire généré. .
Dans ce chapitre on ne génère que du code machine. On suppose donc qu'on a une machine
qui possède une unité arithmétique et logique, une pile PILEV, une mémoire centrale MC, une
zone de calcul intermédiaire (pointé par imc) et au moins un registre accumulateur Acc:

III.2. Déclarations
Dans les chapitres précédents on a signalé que les déclarations:
. Indiquent la nature des objets utilisés dans un langage de programmation ce qui permet de détecter des
opérations interdites ou de signaler le besoin d'une conversion (Analyse sémantique),
. Réservent la place en mémoire pour chacun des objets (Génération de code).
Soit la grammaire engendrant te langage définit dans l'exemple de chapitre2 :
<PROG> ::= f0<ListeDec>
<ListeDec> ::= f0<Dec><SuiteDec>
<SuiteDec> ::= ^|<ListeDec>
<Dec> ::= entier f1 <Liste Var avec Nb Element> |
reel f2 <Liste Var avec Nb Element> |
chaine f3 <Liste Var avec longeur> ;
<Liste Var avec Nb Element> ::= f4 <Var avec Nb Element> <Suite Var avec Nb Element> ;
<Suite Var avec Nb Element> ::= ^|’ ;’ <Liste Var avec Nb Element>
<Var avec Nb Element> ::= idf f5 <Nb Element>
<Nb Element> ::= idf f6 ‘(‘ cent f6‘)’
<Liste Var avec longeur> ::= f4 <Var avec longueur> <Suite Var avec longueur> ;
<Suite Var avec longueur> ::= ^|’ ;’ <Liste Var longueur>

48
<Liste Var longueur> ::= idf f6 ‘(‘ cent f7‘)’

Écrivons-nous, un programme dans ce langage

Entier Xxy
Réel Moy
Entier Varia(100)
Chaine chp(10)
Réel Vach

Action f1
TypeIdf=’entier ‘
TailleIdf=2 ;
Fin
Action f2
TypeIdf=’réel ‘
TailleIdf=4 ;
Fin
Action f3
TypeIdf=chaine ‘
TailleIdf=1 ;
Fin
Action f4
id=id+1 ;
Type[id]= TypeIdf ;
Taille[id]= TailleIdf ;
NbElmt[id]=1;
EmplRel[id]=AdrRel

Fin
Action f5
Si identif( TDS, us) alors
Erreur(1) ;
id=id+1 ;
Sinon
idf[id]=us ;
Fsi
Fin
Action f6
NbElmt[id]=conversion(us) ;
AdrRel=AdrRel+Taille[id]*NbElmt[id] ;

49
Fin
Action f7
Taille[id]=conversion(us) ;
AdrRel=AdrRel+Taille[id] ;
Fin
Action f0
AdrRel=0 ;

Fin
Ces actions permettent de remplir la table des symboles TDS.

III.3. Algorithme générale d'un générateur de code machine


III.3.1 Méthode itérative: liens implicites
Action GénèreCode
ia=1 ;
ELMT=ARB[ia] ;
Tant que ELMT <> « $ » faire
« idf » : id = Chercher (TDS, ELMT)
ADR=DebIdf+TDS[id].EmpRel ;
Générer(Empiler(PILEV,ADR)) ;
« cste » : id = Chercher (TDS, ELMT)
ADR=Debcon+TDC[ic].EmpRel ;
Générer(Empiler(PILEV,ADR)) ;
« ←» : Générer(Dépiler(PILEV,ADR2)) ;
Générer(Dépiler(PILEV,ADR1)) ;
Générer(MC[ADR1]← MC[ADR2]) ;
«+», « -» : Générer(Dépiler(PILEV,ADR2)) ;
«*», «/» : Générer(Dépiler(PILEV,ADR1)) ;
Selon OP faire
‘+’ : générer(MC[imc]←MC[ADR1]+ MC[ADR2]) ;
‘-’ : générer(MC[imc]←MC[ADR1]-MC[ADR2]) ;
‘*’ : générer(MC[imc]←MC[ADR1]* MC[ADR2]) ;
‘/’ : générer(MC[imc]←MC[ADR1]/ MC[ADR2]) ;
fselon
générer(Empiler(PILEV, imc) ;
générer(imc=imc+1) ;
.
.
.
.
fselon
ia=ia+1 ;
ELMT=ARB[ia] ;
FinTanque
Fin
L'action générer consiste à traduire directement l'instruction entre parenthèse en code
machine.
III.3.2 Méthode itérative : liens implicites
Action GénérerCode( p :Pointeur sur la structure Nœud) :Pointeur
Selon ELMT faire
« idf » : id = Chercher (TDS, ELMT)
ADR=DebIdf+TDS[id].EmpRel ;
Retourne(ADR) ;

50
« cste » : id = Chercher (TDS, ELMT)
ADR=Debcon+TDC[ic].EmpRel ;
Retourne(ADR) ;
« ←» : ADR2=GénéreCode(p^.fd) ;
ADR1=GénéreCode(p^.fg) ;
Générer(MC[ADR1]← MC[ADR2]) ;
«+», « -» : ADR2=GénéreCode(p^.fd) ;
«*», «/» : ADR1=GénéreCode(p^.fg) ;
Selon OP faire
‘+’ : générer(MC[imc]←MC[ADR1]+ MC[ADR2]) ;
‘-’ : générer(MC[imc]←MC[ADR1]-MC[ADR2]) ;
‘*’ : générer(MC[imc]←MC[ADR1]* MC[ADR2]) ;
‘/’ : générer(MC[imc]←MC[ADR1]/ MC[ADR2]) ;
fselon
générer(Empiler(PILEV, imc) ;
générer(imc=imc+1) ;
Retourne(imc-1) ;
.
.
.
.
fin selon
Fin
L’action générer consiste à traduire directement l’instruction entre parenthèse en code machine.

III.4. Exemple: Instruction d'affectation


<affectation> ::=idf f1 ←<EA> f2

III.4.1 Expression sans parenthèses et sans priorité entre les opérateurs

Dans ce paragraphe, en suppose que l’expression arithmétique ne comporte aucun parenthèse et que
les opérateurs +, -,* et / ont la même priorité:
<EA> ::= <opérande>f3<Suite>
<suite> ::= ^|’+’f4<opérande>f5<suite>|’-’f4<opérande>f5<suite>
|’*’f4<opérande>f5<suite>|’/’f4<opérande>f5<suite>
Pour simplifier, on génère directement le code machine sans construire l’arbre syntaxique :
Action f1
id= Chercher(TDS, us) ;
AdrG=DebIdf+TDS[id].EmpRel ;
Fin
Action f2
Générer( MC[AdrG]←Acc)
Fin
Action f3
Si identif(us) alors
id= Chercher(TDS, us);
ADR=DabIdf+TDS[id].EmpRel;
Sinon
ic= Chercher(TDC, us);
ADR=DabIdf+TDC[ic].EmpRel;
Fsi
Val=MC[ADR] ;
Générer ( Acc ← Val);
Fin

51
Acton f4
OP=us ;
Fin
Action f5
Si identif(us) alors
id= Chercher(TDS, us);
ADR=DabIdf+TDS[id].EmpRel;
Sinon
ic= Chercher(TDC, us);
ADR=DabIdf+TDC[ic].EmpRel;
Fsi
Val=MC[ADR] ;
Selon OP faire
‘+’ : Generer ( Acc ←Acc+Val) ;
‘-’ : Generer ( Acc ←Acc-Val) ;
‘*’ : Generer ( Acc ←Acc*Val) ;
‘/’ : Generer ( Acc ←Acc/Val) ;
Fselon
Fin

52
Les langages à structures de blocs

IV.1. Introduction
Un langage à structure de blocs, est un langage ou trouve des parties (blocs) indépendantes de
programme c’est-à-dire qui comportent leurs propres déclarations et instructions. Un bloc peut
lui-même comporter d’autres blocs.
Si une variable n’est pas déclarée dans un bloc qui l'utilise, on se sert de la déclaration de la
même variable dans le bloc englobant (de niveau plus petit).
Exemple
Debut
Entier A,B ;
B=3 ;
A=2 ;
A=A+B+1 ;
Debut
Entier A,C ;
A=8 ;
C=2 ;
A=A*C ;
Ecrire(A,B,C) ;
Fin
Le résultat de ce programme est :
Bloc interne A=16, B=3 et C=2
Bloc externe A=4, B=3 et c n’est pas déclarée
IV.2. Structure de données
Pour pouvoir localiser chaque variable d’un bloc donné, il faut une structure stockant les
informations sur les blocs (niveau,...), en plus de la table des symboles classique déjà rencontrée
dans les chapitres précédents. D' où la structure suivante:

profendeur : c’est le niveau du bloc


Aine : c’est le numéro du bloc ( premier fils ou le fils le plus âgé), ça permet de
lier les père son fils ainé ( lien parent ainé)
Frère : Frère d’un bloc (lien entre les frères)

53
AdrAlloc : c’est l’adresse d’allocation d’un bloc dans la mémoire centrale
EntIdf : c’est l’entrée du bloc dans la TDS (début des idf du bloc dans la TDS)
FinIdf : c’est la fin du bloc dans la TDS (fin des idf du bloc dans la TDS)
IV.3. Grammaire
<prog> ::= <Bloc>
<Bloc> ::= debut f1 <Liste Dec> f3 <Liste Ins > fin f2
<Liste Ins > ::= <Inst><Suite Ins>
<Suite Ins> ::= ^|’ ;’<List Ins>
<Ins> ::= …..|<Bloc>
Action f0
Niveau=-1 ;
ibloc=0 ;
id=1 ;
AdrLibre=0 ;
Empiler(PileBloc, Niveau+1) ;
Fin
Action f1
Niveau=Niveau+1 ;
ibloc=ibloc+1 ;
Profendeur[ibloc]=Niveau ;
AdrAlloc[ibloc]=AdrLibre ;
EntIdf[ibloc]=id ;
Ainé[ibloc]=0 ;
Frère[ibloc]=0 ;
Si NonVide(PileBloc) alors
Dépiler(PileBloc,Parent) ;
Si Ainé[Parent]=0 alors
Ainé[Parent]=ibloc ;
Sinon
Premier=Ainé [Parent] ;
Tant que Frère [Premier]<>0 faire
Premier= Frere[Premier] ;
Fin Tanque
Frere[Premier]=ibloc ;
Fsi
Fin
Action f2
AdrLibre= AdrAlloc[Sommet(PileBloc)]=id ;
Niveau=Niveau-1 ;
Dépiler (PileBloc,Bidon) ;
Fin

Action f3
FinIdf[Sommet(PileBloc)]=id ;
Fin

54

Vous aimerez peut-être aussi