Professional Documents
Culture Documents
Sommaire ............................................................................................................................................... i
Chapitre 1.
Qu'est ce que la compilation ? ................................................................................ 1
1.
Pourquoi ce cours ? ................................................................................................................... 2
2.
Structure d'un compilateur ...................................................................................................... 2
3.
Phases d'analyses ...................................................................................................................... 3
3.1 Analyse lexicale (appele aussi Analyse linaire) .............................................................. 3
3.2 Analyse syntaxique (Analyse hirarchique ou grammaticale) ......................................... 3
3.3 Analyse smantique (analyse contextuelle) ......................................................................... 3
4.
Phases de production ................................................................................................................ 3
4.1 Gnration de code ................................................................................................................. 3
4.2 Optimisation de code .............................................................................................................. 4
5.
Phases parallles ....................................................................................................................... 4
5.1 Gestion de la table des symboles........................................................................................... 4
5.2 Gestion des erreurs.................................................................................................................. 4
6.
Conclusion .................................................................................................................................. 4
Chapitre 2.
Analyse lexicale ......................................................................................................... 7
1.
Units lexicales et lexmes ...................................................................................................... 7
2.
Spcification des units lexicales ........................................................................................... 7
3.
Attributs ...................................................................................................................................... 8
4.
Analyseur lexical ....................................................................................................................... 9
5.
Erreurs lexicales ....................................................................................................................... 11
Chapitre 3.
L'outil (f)lex ......................................................................................................... 12
1.
Structure du fichier de spcifications (f)lex .................................................................. 12
2.
Les expressions rgulires (f)lex ...................................................................................... 12
3.
Variables et fonctions prdfinies ....................................................................................... 14
4.
Exemples de fichier .l ............................................................................................................. 14
5.
Exercices .................................................................................................................................... 14
Chapitre 4.
Analyse syntaxique ................................................................................................. 16
1.
Grammaires et Arbres de drivation ................................................................................... 16
2.
Grammaires .............................................................................................................................. 16
3.
Arbre de drivation ................................................................................................................. 18
4.
Mise en oeuvre d'un analyseur syntaxique ........................................................................ 20
4.1 Analyse descendante ............................................................................................................ 20
4.1.1
Exemples.................................................................................................................................. 20
5.
Analyse ascendante................................................................................................................. 33
Cours de Compilation
6.
Erreurs syntaxiques................................................................................................................. 39
6.1 Rcupration en mode panique........................................................................................... 40
6.2 Rcupration au niveau du syntagme ................................................................................ 40
6.3 Productions d'erreur ............................................................................................................. 41
6.4 Correction globale ................................................................................................................. 41
Chapitre 5.
Traduction dirige par la syntaxe ......................................................................... 42
1.
Dfinition dirige par la syntaxe ......................................................................................... 42
2.
Arbre syntaxique dcor ........................................................................................................ 42
3.
Attributs synthtiss et hrits ............................................................................................. 43
3.1 Attributs synthtiss ............................................................................................................. 43
3.2 Attributs hrits ..................................................................................................................... 43
4.
Graphe de dpendances ......................................................................................................... 44
5.
Evaluation des attributs ......................................................................................................... 47
5.1 Aprs l'analyse syntaxique................................................................................................... 47
5.2 Pendant l'analyse syntaxique............................................................................................... 47
6.
Exercices .................................................................................................................................... 52
Chapitre 6.
L'outil yacc/bison ................................................................................................ 54
1.
Structure du fichier de spcifications bison..................................................................... 54
2.
Attributs .................................................................................................................................... 55
3.
Communication avec l'analyseur lexical : yylval ........................................................... 55
4.
Variables, fonctions et actions prdfinies ........................................................................ 56
5.
Conflits shift-reduce et reduce-reduce ................................................................................ 56
6.
Associativit et priorit des symboles terminaux ............................................................. 56
7.
Rcupration des erreurs ....................................................................................................... 57
8.
Options de compilations Bison ............................................................................................ 57
9.
Exemples de fichier .y............................................................................................................. 58
10. Exercices .................................................................................................................................... 59
Chapitre 7.
Analyse smantique ................................................................................................ 61
1.
Porte des identificateurs ...................................................................................................... 61
2.
Contrle de type ...................................................................................................................... 62
3.
Surcharge d'oprateurs et de fonctions ............................................................................... 64
4.
Fonctions polymorphes .......................................................................................................... 65
5.
Environnement d'excution .................................................................................................. 69
5.1 Organisation de la mmoire l'excution.......................................................................... 69
5.2 Allocation dynamique : gestion du tas ............................................................................... 70
6.
Gnration de code ................................................................................................................. 72
6.1 Code intermdiaire................................................................................................................ 72
6.2 Caractristiques communes aux machines cibles ............................................................. 72
6.3 Code 3 adresses simplifi .................................................................................................. 72
6.4 Production de code 3 adresses.......................................................................................... 74
6.4.1
6.4.2
7.
Cours de Compilation
ii
Tout programme crit dans un langage de haut niveau (dans lequel il est fait abstraction
(sauf pour quelques instructions) de la structure et des dtails du calculateur sur lequel les
programmes sont destins tre excuts) ne peut tre excut par un ordinateur que s'il est
traduit en instructions excutables par l'ordinateur (langage machine, instructions
lmentaires directement excutables par le processeur).
Lorsque le langage cible est aussi un langage de haut niveau, on parle plutt de traducteur.
Autre diffrence entre traducteur et compilateur : dans un traducteur, il n'y a pas de perte
d'informations (on grade les commentaires, par exemple), alors que dans un compilateur il y
a perte d'informations. Une autre phase importante qui intervient aprs la compilation pour
obtenir un excutable est la phase d'ditions de liens. Un diteur de liens rsout entre autres
les rfrences des appels de routines dont le code est conserv dans des librairies. En
gnral, un compilateur comprend une partie diteur de liens. Nous n'en parlerons pas ici.
En outre, sur les systmes modernes, l'dition des liens est faite l'excution du programme
! (Le programme est plus petit et les mises jour plus faciles). On ne parlera pas non plus de
la prcompilation (cf. prprocesseur C).
Attention, il ne faut pas confondre les compilateurs et les interprteurs ! Un compilateur est
Cours de Compilation
un programme (de traduction automatique d'un programme crit dans un langage source en
un programme crit dans un langage cible). Au lieu de produire un programme cible comme
dans le cas d'un compilateur, un interprte excute lui mme au fur et mesure les
oprations spcifies par le programme source. Il analyse une instruction aprs l'autre puis
l'excute immdiatement. A l'inverse d'un compilateur, il travaille simultanment sur le
programme et sur les donnes. Gnralement les interprteurs sont assez petits.
Il existe des langages qui sont mi-chemin de l'interprtation et de la compilation. Par
exemple Java, le source est compil pour obtenir un fichier (.class) ''byte code'' qui sera
interprt par une machine virtuelle.
En rgle gnrale, le programmeur dispose d'un calculateur concret (cartes quipes de
processeurs, puces de mmoire, ...). Le langage cible est dans ce cas dfini par le type de
processeur utilis. Mais si l'on crit un compilateur pour un processeur donn, il n'est alors
pas vident de porter ce compilateur (ce programme) sur une autre machine cible.
C'est pourquoi (entre autres raisons), on introduit des machines dites abstraites qui font
abstraction des architectures relles existantes. Ainsi, on s'attache plus aux principes de
traduction, aux concepts des langages, qu' l'architecture des machines. Cf. machine abstraite
Java.
1. Pourquoi ce cours ?
Il est vident qu'il n'est pas ncessaire de comprendre comment est crit un compilateur pour
savoir comment l'utiliser. De mme, un informaticien a peu de chances d'tre impliqu dans
la ralisation ou mme la maintenance d'un compilateur pour un langage de programmation
majeur. Alors pourquoi ce cours ?
Le but de ce cours est de prsenter les principes de base inhrents la ralisation de
compilateurs. Les ides et techniques dveloppes dans ce domaine sont si gnrales et
fondamentales qu'un informaticien (et mme un scientifique non informaticien) les utilisera
trs souvent au cours de sa carrire (traitement de donnes, moteurs de recherche, etc.).
Nous verrons les principes de base inhrents la ralisation de compilateurs : analyse
lexicale, analyse syntaxique, analyse smantique, gnration de code.
En outre, comprendre comment est crit un compilateur permet de mieux comprendre les
"contraintes" imposes par les diffrents langages lorsque l'on crit un programme dans un
langage de haut niveau.
Nous avons dj tudi les outils fondamentaux utiliss pour effectuer ces analyses :
fondements de base de la thorie des langages (grammaires, automates, ...), mthodes
algorithmiques d'analyse, ...
Cours de Compilation
une phase d'analyses, qui va reconnatre les variables, les instructions, les oprateurs et
laborer la structure syntaxique du programme ainsi que certaines proprits smantiques
une phase de synthse et de production qui devra produire le code cible.
, les mots clefs du langage, ... C'est cela que l'on appelle des units
2.1
lexicales.
Outils thoriques utiliss : expressions rgulires et automates tats finis
4. Phases de production
4.1 Gnration de code
Il s'agit de produire les instructions en langage cible. En gnral, on produira dans un
premier temps des instructions pour une machine abstraite (virtuelle). Puis ensuite on fera
la traduction de ces instructions en des instructions directement excutables par la machine
relle sur laquelle on veut que le compilateur s'excute. Ainsi, le portage du compilateur sera
facilit, car la traduction en code cible virtuel sera faite une fois pour toutes,
indpendamment de la machine cible relle. Il ne reste plus ensuite qu' tudier les
problmes spcifiques la machine cible, et non plus les problmes de reconnaissance du
programme (cf. Java).
Cours de Compilation
5. Phases parallles
5.1 Gestion de la table des symboles
La table des symboles est la structure de donnes utilise servant stocker les informations
qui concernent les identificateurs du programme source (par exemple leur type, leur
emplacement mmoire, leur porte, visibilit, nombre et type et mode de passage des
paramtres d'une fonction, ...). Le remplissage de cette table (la collecte des informations) a
lieu lors des phases d'analyse. Les informations contenues dans la table des symboles sont
ncessaires lors des analyses syntaxique et smantique, ainsi que lors de la gnration de
code.
6. Conclusion
Figure 2.1: Structure d'un compilateur
Cours de Compilation
Pendant toutes les annes 50, les compilateurs furent tenus pour des programmes difficiles
crire. Par exemple, la ralisation du premier compilateur Fortran ncessita 18 hommesannes de travail (1957). On a dcouvert depuis des techniques systmatiques pour traiter la
plupart des tches importantes qui sont effectues lors de la compilation.
Phases d'analyse
analyse lexicale
expressions rgulires
(scanner)
analyse syntaxique
grammaires
(parer)
automates pile
analyse smantique
optimisation de code
Gestions parallles
Contrairement une ide souvent rpandue, la plupart des compilateurs sont raliss (crits)
dans un langage de haut niveau, et non en assembleur. Les avantages sont multiples :
facilit de manipulation de concepts avancs
maintenabilit accrue du compilateur
portage sur d'autres machines plus ais
Par exemple, le compilateur C++ de Bjrne Stroustrup est crit en C, .... Il est mme possible
Cours de Compilation
d'crire un compilateur pour un langage L dans ce langage L (gcc est crit en C ...!)
(bootstrap).
Cours de Compilation
CHAPITRE 2.
ANALYSE LEXICALE
L'analyseur lexical constitue la premire tape d'un compilateur. Sa tche principale est de
lire les caractres d'entre et de produire comme rsultat une suite d'units lexicales que
l'analyseur syntaxique aura traiter. En plus, l'analyseur lexical ralise certaines tches
secondaires comme l'limination de caractres superflus (commentaires, tabulations, fin de
lignes, ...), et gre aussi les numros de ligne dans le programme source pour pouvoir
associer chaque erreur rencontre par la suite la ligne dans laquelle elle intervient.
Dans ce chapitre, nous abordons des techniques de spcifications et d'implantation
d'analyseurs lexicaux. Ces techniques peuvent tre appliques d'autres domaines. Le
problme qui nous intresse est la spcification et la conception de programmes qui
excutent des actions dclenches par des modles dans des chanes (traitement de
donnes, moteurs de recherche, ...).
Une unit lexicale est une suite de caractres qui a une signification
Exemples :
L'unit lexicale IDENT (identificateurs) en C a pour modle : toute suite non vide de
caractres compose de chiffres, lettres ou du symbole "_" et qui commencent par une lettre.
Des exemples de lexmes pour cette unit lexicale sont : truc, i,a1, ajouter_valeur ...
L'unit lexicale NOMBRE (entier sign) a pour modle : toute suite non vide de chiffres
prcde ventuellement d'un seul caractre parmi
. Lexmes possibles : -12, 83204,
+0 ...
L'unit lexicale REEL a pour modle : tout lexme correspondant l'unit lexicale
NOMBRE suivi ventuellement d'un point et d'une suite (vide ou non) de chiffres, le tout
suivi ventuellement du caractre E ou e et d'un lexme correspondant l'unit lexicale
NOMBRE. Cela peut galement tre un point suivi d'une suite de chiffres, et ventuellement
du caractre E ou e et d'un lexme correspondant l'unit lexicale NOMBRE. Exemples de
lexmes : 12.4, 0.5e3, 10., -4e-1, -.103e+2 ...
Pour dcrire le modle d'une unit lexicale, on utilisera des expressions rgulires.
Cours de Compilation
Nous disposons donc, avec les E.R., d'un mcanisme de base pour dcrire des units lexicales.
Par exemple, une ER dcrivant les identificateurs en C pourrait tre :
ident = (a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v |w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q
|R|S|T|U
|V|W|X|Y|Z) (a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r| s|t|u|v|w|x|y|z|A|B |C|D|E|F|G|H|I|J|K|L|M |N|O
|P|Q| R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|_)*
C'est un peu chiant et illisible ...
Alors on s'autorise des dfinitions rgulires et le symbole - sur des types ordonns (lettres,
chiffres, ...).
Une dfinition rgulire est une suite de dfinitions de la forme
, et chaque di est un
Remarque IMPORTANTE : toutes les units lexicales ne peuvent pas tre exprimes par
des dfinitions rgulires. Par exemple une unit lexicale correspondant au modle "toute
suite de a et de b de la forme anbn avec
" ne peut pas tre exprime par des dfinitions
rgulires, car ce n'est pas un langage rgulier (mais c'est un langage que l'on appelle hors
contexte, on verra plus tard qu'on peut s'en sortir avec les langages hors contexte et
heureusement).
Autre exemple : il n'est pas possible d'exprimer sous forme d'ER les systmes de parenthses
bien forms, ie les mots (), (())(), ()(()()(())) .... Ce n'est pas non plus un langage rgulier
(donc les commentaires la Pascal ne forment pas un langage rgulier).
3. Attributs
Exemples :
Pour ce qui est des symboles <=, >=, <, >, <>, l'analyseur syntaxique a juste besoin de
savoir que cela correspond l'unit lexicale OPREL (oprateur relationnel). C'est seulement
lors de la gnration de code que l'on aura besoin de distinguer < de >= (par exemple).
Pour ce qui est des identificateurs, l'analyseur syntaxique a juste besoin de savoir que c'est
l'unit lexicale IDENT. Mais le gnrateur de code, lui, aura besoin de l'adresse de la variable
correspondant cet identificateur. L'analyseur smantique aura aussi besoin du type de la
variable pour vrifier que les expressions sont smantiquement correctes.
Cette information supplmentaire, inutile pour l'analyseur syntaxique mais utile pour les
autres phases du compilateur, est appele attribut.
Cours de Compilation
4. Analyseur lexical
Rcapitulons : le rle d'un analyseur lexical est la reconnaissance des units lexicales. Une
unit lexicale peut (la plupart du temps) tre exprime sous forme de dfinitions rgulires.
Dans la thorie des langages, on dfinit des automates qui sont des machines thoriques
permettant la reconnaissance de mots (voir chapitre 8). Ces machines ne marchent que pour
certains types de langages. En particulier :
Thorme 3.9 Un langage rgulier est reconnu par un automate fini.
Contentons nous pour l'instant de donner un exemple d'automate :
Le langage rgulier dcrit par l'ER (abbc|bacc)+c(c|bb)* est reconnu par l'automate :
tat a b c
0
1 4
3
4
6
5
1 4 7
8 7
Bon, un langage rgulier (et donc une ER) peut tre reconnu par un automate. Donc pour
crire un analyseur lexical de notre programme source, il "suffit" (...) donc d'crire un
programme simulant l'automate reconnaissant les units lexicales.
Lorsqu'une unit lexicale est reconnue, elle envoye l'analyseur syntaxique, qui la traite,
puis repasse la main l'analyseur lexical qui lit l'unit lexicale suivante dans le programme
source. Et ainsi de suite, jusqu' tomber sur une erreur ou jusqu' ce que le programme source
soit trait en entier (et alors on est content).
Exemple de morceau d'analyseur lexical pour le langage Pascal (en C) :
c = getchar();
switch (c) {
case ':' : c=getchar();
if (c== '=')
{
unite_lex = AFFECTATION;
c= getchar();
}
Cours de Compilation
else
unite_lex = DEUX_POINTS;
break;
case '<' : unite_lex := OPREL;
c= getchar();
if (c=='=')
{
attribut = INFEG;
c = getchar();
}
else
attribut := INF
break;
case .....
ou encore, en copiant le travail de l'automate (mais cela donne un code moins rapide, car il
contient beaucoup d'appels de fonctions)
void Etat1()
{
char c;
c= getchar();
swicth(c) {
case ':' : Etat2();
break;
case '<' : Etat5();
break;
case 'a' : Etat57();
break;
...
default : ERREUR();
}
}
void Etat36524()
...
Il n'est pas vident du tout dans tout a de s'y retrouver si on veut modifier l'analyseur, le
complter, ...
Heureusement, il existe des outils pour crire des programmes simulant des automates partir
de simples dfinitions rgulires. Par exemple : flex
Exemple de programme en flex :
chiffre
lettre
entier
ident
%%
":="
"<="
[0-9]
[a-zA-Z]
[+-]?[1-9]{chiffre}*
{lettre}({lettre}|{chiffre})*
{ return AFF; }
{
attribut = INFEG;
return OPREL;
}
if|IF|If|iF {return MC_IF;}
{entier} {return NB;}
{ident}
{return ID;}
%%
Cours de Compilation
10
main() {
yylex();
}
5. Erreurs lexicales
Peu d'erreurs sont dtectables au seul niveau lexical, car un analyseur lexical a une vision trs
locale du programme source. Les erreurs se produisent lorsque l'analyseur est confront une
suite de caractres qui ne correspond aucun des modles d'unit lexicale qu'il a sa
disposition.
Par exemple, dans un programme C, si l'analyseur rencontre esle, s'agit t-il du mot clef else
mal orthographi ou d'un identificateur ? Dans ce cas prcis, comme la chane correspond au
modle de l'unit lexicale IDENT (identificateur), l'analyseur lexical ne dtectera pas d'erreur,
et transmettra l'analyseur syntaxique qu'il a reconnu un IDENT. S'il s'agit du mot clef mal
orthographi, c'est l'analyseur syntaxique qui dtectera alors une erreur.
Autre exemple : s'il rencontre 1i, il ne sait pas s'il s'agit du chiffre 1 suivi de l'identificateur i
ou d'une erreur de frappe (et dans ce cas l'utilisateur pensait-il i1, i, 1 ou tout autre chose
?). L'analyseur lexical peut juste signaler que la chane 1i ne correspond aucune unit
lexicale dfinie.
Lorsque l'analyseur est confront une suite de caractres qui ne correspond aucun de ses
modles, plusieurs stratgies sont possibles :
- mode panique : on ignore les caractres qui posent problme et on continue3.1
- transformations du texte source : insrer un caractre, remplacer, changer, ...
La correction d'erreur par transformations du texte source se fait en calculant le nombre
minimum de transformations apporter au mot qui pose problme pour en obtenir un qui ne
pose plus de problmes. On utilise des techniques de calcul de distance minimale entre des
mots. Cette technique de rcupration d'erreur est trs peu utilise en pratique car elle est trop
coteuse implanter. En outre, au niveau lexical, elle est peu efficace, car elle entrane
d'autres erreurs lors des phases d'analyse suivantes3.2.
La stratgie la plus simple est le mode panique. En fait, en gnral, cette technique se contente
de refiler le problme l'analyseur syntaxique. Mais c'est efficace puisque c'est lui, la plupart
du temps, le plus apte le rsoudre.
Cours de Compilation
11
CHAPITRE 3.
L'OUTIL (F)LEX
De nombreux outils ont t btis pour construire des analyseurs lexicaux partir de notations
spcifiques bass sur des expressions rgulires. lex est un utilitaire d'unix. Son grand frre
flex est un produit gnu. (f)lex accepte en entre des spcifications d'units lexicales sous
forme de dfinitions rgulires et produit un programme crit dans un langage de haut niveau
(ici le langage C) qui, une fois compil, reconnat ces units lexicales (ce programme est donc
un analyseur lexical).
> (f)lex fichier_de_specif.l
donne le fichier lex.yy.c qu'il faut compiler avec la bibliothque (f)lex :
> gcc lex.yy.c -l(f)l
Cours de Compilation
12
Les mta-caractres $,
Priorits :
truc|machin* est interprt comme (truc)|(machi(n*))
abc{1,3} est interprt avec lex comme (abc){1,3} et avec flex comme ab(c{1,3})
truc|machin est interprt avec lex comme ( truc)|machin et avec flex comme
(truc|machin)
Avec lex, une rfrence une dfinition rgulire n'est pas considre entre (), en flex si. Par
exemple
Cours de Compilation
13
id
[a-z][a-z0-9A-Z]
%%
truc{id}*
4. Exemples de fichier .l
Ce premier exemple compte le nombre de voyelles, consonnes et caractres de ponctuations
d'un texte entr au clavier.
%{
int nbVoyelles, nbConsonnes, nbPonct;
%}
consonne
[b-df-hj-np-xz]
ponctuation
[,;:?!\.]
%%
[aeiouy]
nbVoyelles++;
{consonne}
nbConsonnes++;
{ponctuation}
nbPonct++;
(.|\n)$
yyterminate();
%%
main(){
nbVoyelles = nbConsonnes = nbPonct = 0;
// printf("Chane analyser: %s", argv);
yylex();
printf("Il y a %d voyelles, %d consonnes et %d signes de
ponctuations.\n",
nbVoyelles, nbConsonnes, nbPonct);
}
5. Exercices
1. Ecrire un programme flex qui compte le nombre de mots d'un texte
2. (commande wc d'Unix) Ecrire un programme flex qui compte le nombre de caractres, de
mots et de lignes du texte source
Cours de Compilation
14
(a) en restreignant l'alphabet aux lettres minuscules et majuscules, aux chiffres et aux
caractres d'espacement.
(b) en tendant l'alphabet tous les symboles.
3. Ecrire un programme flex qui vrifie si une expression est correctement parenthse.
4. Ecrire un programme flex qui remplace dans le texte source toutes les occurrences de bon
par mauvais sauf lorsque c'est un sous mot d'un plus grand mot.
5. Ecrire un programme flex qui supprime dans le texte source tous les mots qui
commencent par une voyelle.
Cours de Compilation
15
CHAPITRE 4.
ANALYSE SYNTAXIQUE
Tout langage de programmation possde des rgles qui indiquent la structure syntaxique
d'un programme bien form. Par exemple, en Pascal, un programme bien form est compos
de blocs, un bloc est form d'instructions, une instruction de ...
La syntaxe d'un langage peut tre dcrite par une grammaire.
L'analyseur syntaxique reoit une suite d'units lexicales de la part de l'analyseur lexical et
doit vrifier que cette suite peut tre engendre par la grammaire du langage.
Le problme est donc
2. Grammaires
Exemples (dfinitions informelles de grammaires) :
dans le langage naturel, une phrase est compose d'un sujet suivi d'un verbe suivi d'un
complment (pour simplifier ...).
Par exemple :
L'tudiant subit un cours
On dira donc :
phrase = sujet verbe complment
Ensuite, il faut expliquer ce qu'est un sujet, un verbe, un complment. Par exemple :
sujet = article adjectif nom
| article nom adjectif
| article nom
article = le | la | un | des| l'
adjectif = malin | stupide | couleur
couleur = vert | rouge | jaune
Cours de Compilation
16
Il faut encore dfinir ce qu'est une expression et ce qu'est une instruction ...
On distingue les
symboles terminaux : les lettres du langage ( le, la, if ...dans les exemples)
symboles non-terminaux : les symboles qu'il faut encore dfinir (ceux en italique dans les
exemples
prcdents)
Dfinition 5.1 Une grammaire est la donne de G=(VT,VN,S,P) o
VT est un ensemble non vide de symboles terminaux (alphabet terminal)
VN est un ensemble de symboles non-terminaux, avec
S est un symbole initial
appel axiome
P est un ensemble de productions (rgles de rcritures)
Dfinition 5.2 Une rgle de production
(
).
Exemple 1 :
symboles terminaux (alphabet) :
symboles non-terminaux :
axiome : S
rgles de production :
qui se rsument en
PRONOM
il | elle
VERBE
VERBETAT | VERBACTION
VERBETAT
est | devient | reste
VERBACTION
parle | court
COMPLEMENT
sympa | vite
Conventions : l'emploi de lettres capitales est rserv pour dnoter des symboles nonterminaux. Les lettres minuscules du dbut de l'alphabet sont utilises pour reprsenter des
symboles terminaux. Les lettres minuscules de la fin de l'alphabet (t, ..., z) sont utilises pour
indiquer une chane de symboles terminaux. Les lettres grecques dnotent des chanes
composes de symboles terminaux et non terminaux.
3. Arbre de drivation
On appelle drivation l'application d'une ou plusieurs rgles partir d'un mot de
On notera
.
une drivation obtenue par application d'une seule rgle de production, et
une
Exemples :
sur la grammaire de l'exemple 1
Remarque : il est possible de gnrer des phrases syntaxiquement correctes mais qui n'ont pas
de sens. C'est l'analyse smantique qui permettra d'liminer ce problme.
Dfinition 5.3 Etant donne une grammaire G, on note L(G) le langage gnr par G et
dfini par
(c'est dire tous les mots composs uniquement de symboles terminaux (de lettres de
l'alphabet) que l'on peut former partir de S).
L'exemple 1 nous donne
Dfinition 5.4 On appelle arbre de drivation (ou arbre syntaxique) tout arbre tel que
- la racine est l'axiome
- les feuilles sont des units lexicales ou
Cours de Compilation
18
sont
si et seulement si
est une
(drivations gauches)
ou
(drivations
droites)
Ces deux suites diffrentes de drivations donnent le mme arbre de drivation.
Dfinition 5.5 Une grammaire est dite ambigu s'il existe un mot de L(G) ayant plusieurs
arbres syntaxiques.
Remarque : la grammaire prcdente n'est pas ambigu.
Exemple de grammaire ambigu :
Cours de Compilation
19
if (x>10)
if (y<0)
a=1
// finsi
else
a=0
// finsi
Exemple 1 :
Cours de Compilation
20
Cours de Compilation
21
Pour savoir quelle rgle utiliser, il faut cette fois-ci connatre aussi la dernire lettre du mot.
Exemple 4 : grammaire des expressions arithmtiques
Exemple :
, donc
PREMIER(S)
, donc
PREMIER(S)
, donc
PREMIER(S)
, donc
PREMIER(S)
Il n'y a pas de drivation
Donc PREMIER(S)
22
1. Si X est un non-terminal et
est une production de la grammaire (avec Yi
symbole terminal ou non-terminal) alors
ajouter les lments de PREMIER(Y1) sauf dans PREMIER(X)
s'il existe j (
) tel que pour tout
on a
alors ajouter les lments de PREMIER(Yj) sauf dans PREMIER(X)
si pour tout
2. Si X est un non-terminal et
PREMIER(Yi),
Exemple 2 :
Cours de Compilation
23
Exemple :
et
...
Exemple 1 :
Exemple 2 :
S -> aSb | cd | SAe
A ->aAdB |
B -> bb
PREMIER SUIVANT
S ac
$bae
Cours de Compilation
24
A a
ed
B B
ed
Une table d'analyse est un tableau M deux dimensions qui indique pour chaque symbole
non-terminal A et chaque symbole terminal a ou symbole $ la rgle de production appliquer.
faire
), rajouter la production
dans la case
dans M[A,b]
E
E'
T
T'
F
nb
Maintenant qu'on a la table, comment l'utiliser pour dterminer si un mot m donn est tel que
?
On utilise une pile.
Algorithme :
donnes : mot m, table d'analyse M
initialisation de la pile :
S
$
Cours de Compilation
25
alors
enlever X de la pile
mettre Yn puis Yn-1 puis ...puis Y1 dans la pile
Entre Sortie
$E
3+4*5$
$ E'T
3+4*5$
$ E'T'F
3+4*5$
$ E'T'3
3+4*5$
$ E'T'
+4*5$
$ E'
+4*5$
$ E'T+
+4*5$
$ E'T
4*5$
$ E'T'F
4*5$
Cours de Compilation
nb
nb
26
$ E'T'4
4*5$
$ E'T'
*5$
$ E'T'F*
*5$
$ E'T'F
5$
$ E'T'5
5$
$ E'T'
$ E'
nb
Entre Sortie
$E
(7+3)5$
$ E'T
(7+3)5$
$ E'T'F
(7+3)5$
$ E'T') E (
(7+3)5$
$ E'T') E
7+3)5$
$ E'T')E'T
7+3)5$
$ E'T')E'T'F 7+3)5$
Cours de Compilation
27
$ E'T')E'T'7
7+3)5$
$ E'T')E'T'
+3)5$
$ E'T')E'
+3)5$
$ E'T')E'T+
+3)5$
$ E'T')E'T
3)5$
$ E'T')E'T'F
3)5$
$ E'T')E'T'3
3)5$
$ E'T')E'T'
)5$
$ E'T')E'
)5$
$ E'T')
)5$
$ E'T'
* T E'
5$ ERREUR !!
, PREMIER
, SUIVANT
et
b d $
S
A
Cours de Compilation
28
Il y a deux rductions pour la case M[A,c], donc ce n'est pas une grammaire LL(1). On ne peut
pas utiliser cette mthode d'analyse. En effet, on l'a dja remarqu, pour pouvoir choisir entre
la production
et la production
, il faut lire la lettre qui suit celle que l'on
pointe (donc deux symboles de pr-vision sont ncessaires)5.1.
Autre exemple :
Dfinition 5.8 Une grammaire est immdiatement rcursive gauche si elle contient un
non-terminal A tel qu'il existe une production
o est une chane quelconque.
Exemple :
Thorme 5.9 La grammaire ainsi obtenue reconnat le mme langage que la grammaire
initiale.
Sur l'exemple, on obtient :
Cours de Compilation
29
.
Remarque : ici on peut se passer de A'.
C'est dire en fait que
est quivalent
(qui, elle, n'est pas
immdiatement rcursive gauche, mais droite, ce qui n'a rien voir).
Dfinition 5.10 Une grammaire est rcursive gauche si elle contient un non-terminal A tel
qu'il existe une drivation
Exemple :
Le non-terminal S est rcursif gauche car
rcursif gauche).
par
fin pour
liminer les rcursivits gauche immdiates des productions Ai
fin pour
Thorme 5.11 La grammaire ainsi obtenue reconnat le mme langage que la grammaire
initiale.
Sur l'exemple :
On ordonne S,A
i=1 pas de rcursivit immdiate dans
i=2 et j=1 on obtient
on limine la rec. immdiate :
Cours de Compilation
30
Autre exemple :
S->Sa | TSc | d
T->TbT|
On obtient la grammaire :
S -> TScS' | dS'
S'->aS'|
T->T'
T'->bTT'|
Or on a S -> TScS' -> T'ScS' -> ScS' damned une rcursivit gauche !!!! Eh oui, l'algo ne
marche pas toujours lorsque la grammaire possde une rgle A->
4.3.2 Grammaire propre
Dfinition 5.12 Une grammaire est dite propre si elle ne contient aucune production A->
Comment rendre une grammaire propre ? En rajoutant une production dans laquelle le A est
remplac par , ceci pour chaque A apparaissant en partie droite d'une production, et pour
chaque A d'un A ->
Exemple :
S -> a Tb|aU
T -> bTaTA|
U -> a U | b
devient
S -> a Tb|ab|aU
T -> bTaTA|baTA|bTaA|baA
U -> a U | b
4.3.3 Factorisation gauche
L'ide de base est que pour dvelopper un non-terminal A quand il n'est pas vident de choisir
l'alternative utiliser (ie quelle production prendre), on doit rcrire les productions de A de
faon diffrer la dcision jusqu' ce que suffisamment de texte ait t lu pour faire le bon
choix.
Exemple :
Au dpart, pour savoir s'il faut choisir
ou
, il faut avoir lu la
5ime lettre du mot (un a ou un c). On ne peut donc pas ds le dpart savoir quelle production
prendre. Ce qui est incompatible avec une grammaire LL(1). (Remarque : mais pas avec une
grammaire LL(5), mais ce n'est pas notre problme.)
Factorisation gauche :
Pour chaque non-terminal A
trouver le plus long prfixe
Cours de Compilation
31
Si
, remplacer
pas par ) par les deux rgles
(o les
ne commencent
finpour
Recommencer jusqu' ne plus en trouver.
Exemple :
Si notre grammaire est LL(1), l'analyse syntaxique peut se faire par l'analyse descendante vue
ci-dessus. Mais comment savoir que notre grammaire est LL(1) ? Etant donne une
grammaire
Cours de Compilation
32
n'est pas LL(1). Or elle n'est pas rcursive gauche, elle est factorise
gauche et elle n'est pas ambige !
5. Analyse ascendante
Principe : construire un arbre de drivation du bas (les feuilles, ie les units lexicales) vers le
haut (la racine, ie l'axiome de dpart).
Le modle gnral utilis est le modle par dcallages-rductions. C'est dire que l'on ne
s'autorise que deux oprations :
Exemple :
...
on peut utiliser
on ne peut rien rduire, donc on dcale
on ne peut rien rduire, donc on dcale
On peut utiliser
On peut utiliser
Cours de Compilation
33
dcalage
dcalage
rduction par
rduction par
rduction par
dcalage
dcalage
rduction par
rduction par
dcalage
dcalage
dcalage
rduction par
dcalage
dcalage
rduction par
rduction par
rduction par
termin !!!! On a gagn, le mot est bien dans le langage.
En mme temps, on a construit l'arbre (figure 5.2).
Figure 5.2: Analyse ascendante
Cours de Compilation
34
Conclusion : a serait bien d'avoir une table qui nous dit si on dcale ou si on rduit, et par
quoi, lorsque le pointeur est sur une lettre donne.
Table d'analyse LR
(on l'appelle comme a parce que, de mme que la mthode descendante vue prcdemment
ne permettait d'analyser que les grammaires LL(1), cette mthode va permettre d'analyser les
grammaires dites LR).
En fait, ce n'est pas vraiment un tableau, c'est plutt une sorte d'automate, qu'on appelle
automate pile. Cette table va nous dire ce qu'il faut faire quand on lit une lettre a et qu'on est
dans un tat i
- soit on dcale Dans ce cas, on empile la lettre lue et on va dans un autre tat j. Ce qui sera
not dj
- soit on rduit par la rgle de production numro p, c'est dire qu'on remplace la chane en
sommet de pile (qui correspond la partie droite de la rgle numro p) par le non-terminal de
la partie gauche de la rgle de production, et on va dans l'tat j qui dpend du non-terminal en
question. On note a rp
- soit on accepte le mot. Ce qui sera not ACC
- soit c'est une erreur. Case vide
Construction de la table d'analyse : utilise aussi les ensembles SUIVANT ( et donc
PREMIER), plus ce qu'on appelle des fermetures de 0-items. Un 0-item (ou plus simplement
item) est une production de la grammaire avec un "." quelque part dans la partie droite. Par
exemple (sur la gram ETF) : E -> E . + T ou encore T-> F. ou encore F -> . ( E )
Fermeture d'un ensemble d'items I :
1- Mettre chaque item de I dans Fermeture(I)
2- Pour chaque item i de Fermeture(I) de la forme
.B
Cours de Compilation
35
X.
.X
.nb, F
est dans I)
.(E)} on aura
.(E)}
d5
d4
1 2 3
d6
r2 d7
r2
r2
r4 r4
r4
r4
d5
E T F
ACC
d4
Cours de Compilation
8 2 3
36
r6 r6
r6
r6
d5
d4
9 3
d5
d4
10
d6
d11
r1 d7
r1
r1
10
r3 r3
r3
r3
11
r5 r5
r5
r5
Analyseur syntaxique SLR : On part dans l'tat 0, et on empile et dpile non seulement les
symboles (comme lors de l'analyseur LL) mais aussi les tats successifs.
Exemple : l'analyse du mot
est donne figure 5.3. La figure 5.4 donne l'analyse
du mot 3+4*2$. On peut aussi dessiner l'arbre obtenu (figure 5.5)
Cours de Compilation
37
Remarques :
cette mthode permet d'analyser plus de grammaires que la mthode descendante (car
il y a plus de grammaires SLR que LL)
Cours de Compilation
38
en TP on va utiliser un outil (bison) qui construit tout seul une table d'analyse LR
(LALR en fait, mais c'est presque pareil) partir d'une grammaire donne
dans cette mthode d'analyse, a n'a strictement aucune importance que la grammaire
soit rcursive gauche, mme au contraire, on prfre.
Les grammaires ambiges provoquent des conflits
o conflit dcalage/rduction : on ne peut pas dcider la lecture du terminal a s'il
faut rduire une production
ou dcaler le terminal
o conflit rduction/rduction : on ne peut pas dcider la lecture du terminal a
s'il faut rduire une production
ou une production
On doit alors rsoudre les conflits en donnant des priorits aux actions (dcaler ou
rduire) et aux productions.
Par exemple, soit la grammaire
Soit analyser 3+4+5. Lorsqu'on lit le 2ime + on a le choix entre
o
6. Erreurs syntaxiques
Beaucoup d'erreurs sont par nature syntaxiques (ou rvles lorsque les units lexicales
provenant de l'analyseur lexical contredisent les rgles grammaticales). Le gestionnaire
d'erreur doit
- indiquer la prsence de l'erreur de faon claire et prcise
- traiter l'erreur rapidement pour continuer l'analyse
- traiter l'erreur le plus efficacement possible de manire ne pas en crer de nouvelles.
Heureusement, les erreurs communes (confusion entre deux sparateurs (par exemple entre ;
et ,), oubli de ;, ...) sont simples et un mcanisme simple de traitement suffit en gnral.
Cependant, une erreur peut se produire longtemps avant d'tre dtecte (par exemple l'oubli
d'un { ou } dans un programme C). La nature de l'erreur est alors trs difficile dduire. La
plupart du temps, le gestionnaire d'erreurs doit deviner ce que le programmeur avait en tte.
Lorsque le nombre d'erreur devient trop important, il est plus raisonnable de stopper
l'analyse5.4.
Il existe plusieurs stratgies de rcupration sur erreur : mode panique, au niveau du
syntagme5.5, productions d'erreur, correction globale. Une rcupration inadquate peut
provoquer une avalanche nfastes d'erreurs illgitimes, c'est dire d'erreurs qui n'ont pas t
faites par le programmeur mais sont la consquence du changement d'tat de l'analyseur lors
de la rcupration sur erreur. Ces erreurs illgitimes peuvent tre syntaxiques mais galement
Cours de Compilation
39
smantiques. Par exemple, pour se rcuprer d'une erreur, l'analyseur syntaxique peut sauter
la dclaration d'une variable. Lors de l'utilisation de cette variable, l'analyseur smantique
indiquera qu'elle n'a pas t dclare.
6.1 Rcupration en mode panique
C'est la mthode la plus simple implanter. Quand il dcouvre une erreur, l'analyseur
syntaxique limine les symboles d'entre les uns aprs les autres jusqu' en rencontrer un qui
appartienne un ensemble d'units lexicales de synchronisation, c'est dire (par exemple) les
dlimiteurs (;, end ou }), dont le rle dans un programme source est clair.
Bien que cette mthode saute en gnral une partie considrable du texte source sans en
vrifier la validit, elle a l'avantage de la simplicit et ne peut pas entrer dans une boucle
infinie.
6.2 Rcupration au niveau du syntagme
Quand une erreur est dcouverte, l'analyseur syntaxique peut effectuer des corrections locales.
Par exemple, remplacer une , par un ;, un wihle par un while, insrer un ; ou une (, ...Le
choix de la modification faire n'est pas vident du tout du tout en gnral. En outre, il faut
faire attention ne pas faire de modifications qui entranerait une boucle infinie (par exemple
dcider d'insrer systmatiquement un symbole juste avant le symbole courant).
L'inconvnient majeure de cette mthode est qu'il est pratiquement impossible de grer les
situations dans lesquelles l'erreur relle s'est produite bien avant le point de dtection.
On implante cette rcupration sur erreur en remplissant les cases vides des tables d'analyse
par des pointeurs vers des routines d'erreur. Ces routines remplacent, insrent ou suppriment
des symboles d'entre et mettent les messages appropris.
Exemple : grammaire des expressions arithmtiques
e1
d3 e1 e1 d2 e2
e3 d4 d5 e3 e2 ACC
d3 e1 e1 d2 e2
e1
r4 r4 r4 r4 r4
r4
d3 e1 e1 d2 e2
e1
d3 e1 e1 d2 e2
e1
e3 d4 d5 e3 d9
e4
r1 r1 d5 r1 r1
r1
r2 r2 r2 r2 r2
r2
r3 r3 r3 r3 r3
r3
Cours de Compilation
40
Cours de Compilation
41
CHAPITRE 5.
Un schma de traduction dirige par la syntaxe (TDS) est un formalisme permettant d'associer
des actions une production d'une rgle de grammaire.
Une rgle smantique est une suite d'instructions algorithmiques : elle peut contenir des
affectations, des si-sinon, des instructions d'affichage, ...
Dfinition 6.1 On appelle dfinition dirige par la syntaxe (DDS), la donne d'une
grammaire et de son ensemble de rgles smantiques.
On notera X.a l'attribut a du symbole X. S'il y a plusieurs symboles X dans une production, on
, et X(0) s'il est en partie gauche.
les notera
Exemple : Grammaire
attributs : nba (calcul du nombre de a), nbc (du nombre de c)
une DDS permettant de calculer ces attributs est alors :
production
Rgle smantique
S(0).nba:=S(1).nba+1
S(0).nbc:=S(1).nbc
S(0).nba:=S(1).nba+1
S(0).nbc:=S(1).nbc
S(0).nba:=S(1).nba+S(2).nba+1
S(0).nbc:=S(1).nbc+S(2).nbc+2
S(0).nba:=0
S(0).nbc:=0
Cours de Compilation
42
Exemple : la figure 6.1 donne l'arbre syntaxique dcor pour le mot acaacabb et la DDS
dfinie prcdemment.
Figure 6.1: Calcul du nombre de a et c pour
acaacabb
Cours de Compilation
43
Exemple : calcul du niveau d'imbrication des ) dans un systme de parenthses bien form.
Grammaire :
DDS :
production action smantique
S.nb:=0
S(1).nb:=S(0).nb+1
S(2).nb:=S(0).nb
crire S.nb
La figure 6.2 donne l'arbre dcor pour le mot (())(()())()
4. Graphe de dpendances
Une DDS peut utiliser la fois des attributs synthtiss et des attributs hrits. Il n'est alors
pas forcmment vident de s'y retrouver pour calculer ces attributs. L'attribut machin dpend
de l'attribut bidule qui lui-mme dpend de l'attribut truc ...Il faut tablir un ordre
d'valuation des rgles smantiques. On construira ce qu'on appelle un graphe de
dpendances.
Exemple : Soit la DDS suivante (totalement stupide) qui fait intervenir deux attributs hrits
h et r et deux attributs synthtiss s et t.
production
action smantique
Cours de Compilation
44
E.h:=2*E.s+1
S.r:=E.t
E(0).s:=E(1).s+E(2).s
E(0).t:=3*E(1).t-E(2).t
E(1).h:=E(0).h
E(2).h:=E(0).h+4
nombre E.s:= nombre
E.t:=E.h +1
La figure 6.3 donne un exemple d'arbre dcor.
Figure 6.3: Attributs hrits et synthtiss
Cours de Compilation
45
:
Sur l'exemple : La figure 6.4 donne le graphe de dpendances pour chaque rgle de
production, et la figure 6.5 le graphe de dpendance de l'arbre syntaxique.
Remarque : si le graphe de dpendance contient un cycle, l'valuation des attributs est alors
impossible.
Exemple : Soit la DDS suivante (a hrit et b synthtis)
production action smantique
S.a:=S.b
S(0).b:=S(1).b+S(2).b+T.b
S(1).a:=S(0).a
S(2).a:=S(0).a+1
T.a:=S(0).a+S(0).b+S(1).a
T(0).b:=P.a+P.b
P.a:=T(0).a+3
T(1).a:=...
Le graphe de dpendance associ peut contenir un cycle (voir figure 6.6), le calcul est donc
impossible.
Figure 6.6: Cycle dans le graphe de
dpendance
Cours de Compilation
46
production
action smantique
Cours de Compilation
47
F.val:=nb
empiler(nb)
L'analyse syntaxique s'effectue partir de la table d'analyse LR donne. Par exemple, pour le
mot 2*(10+3) on obtiendra :
Pile
entre action
2*(10+3)$ d5
$
$
*(10+3)$ r6 :
*(10+3)$ r4 :
*(10+3)$ d7
(10+3)$ d4
10+3)$ d5
10
+3)$ r6 :
+3)$ r4 :
2 10
+3)$ r2 :
2 10
+3)$ d6
2 10
3)$ d5
2 10
)$ r6 :
)$ r4 :
2 10 3
)$ r1 :
2 13
)$ d11
2 13
$
$
nb
nb
nb
r5 :
2 10
2 10 3
2 13
$ r3 :
26
$ r2 :
26
$ ACCEPT !!!
26
Cours de Compilation
48
Exemple avec un attribut hrit : reprenons l'attribut hrit qui calcule le niveau
d'imbrication des ) dans un systme de parenthses bien form.
S.nb:=0
empiler(0)
empiler(sommet()+1)
crire S.nb
Ecrire depiler()
, SUIVANT
et SUIVANT
S'
S
Analysons le mot (()(()))()
Pile
entre action
$ S'
(()(()))()$
$S
(()(()))()$
01
$ S)S(
(()(()))()$
01
$ S)S
()(()))()$
012
$ S)S)S(
()(()))()$
012
$ S)S)S
)(()))()$
01
$ S)S)
)(()))()$
01
$ S)S
(()))()$
012
$ S)S)S(
(()))()$
012
$ S)S)S
()))()$
0123
$ S)S)S)S(
()))()$
0123
$ S)S)S)S
)))()$
012
$ S)S)S)
)))()$
012
Cours de Compilation
49
$ S)S)S
))()$
01
$ S)S)
))()$
01
$ S)S
)()$
$ S)
)()$
$S
()$
01
$ S)S(
()$
01
$ S)S
)$
$ S)
)$
$S
FINI
Mais, vu que les attributs synthtiss s'valuent avec une analyse ascendante et les attributs
hrits avec une analyse descendante, comment on fait quand on a la fois des attributs
synthtiss et hrits ? Hein ?
Souvent, on peut utiliser des attributs synthtiss qui font la mme chose que les hrits
que l'on voulait.
Exemple : compter le nombre de bit 1 dans un mot binaire.
Grammaire :
DDS avec attribut hrit :
production action smantique
B.nb:=0
B(1).nb:=B(0).nb
B(1).nb:=B(0).nb+1
crire B.nb
DDS avec attribut synthtis :
production action smantique
crire B.nb
B(0).nb:=B(1).nb
Cours de Compilation
50
B(0).nb:=B(1).nb+1
B.nb:=0
La figure 6.7 donne les arbres dcors pour le mot 10011.
Figure 6.7: Arbre dcor pour 10011 avec des
attributs (a)hrits (b) synthtiss
Parfois, il est ncessaire de modifier la grammaire. Par exemple, considrons une DDS de
typage de variables dans une dclaration Pascal de variables (en Pascal, une dclaration de
variable consiste en une liste d'identificateurs spars par des virgules, suivie du caractre ':',
suivie du type. Par exemple : a,b : integer)
production
:T
Id
action smantique
L.type:=T.type
mettre dans la table des symboles le type L.type pour Id
, Id
L(1).type:=L(0).type
mettre dans la table des symboles le type L(0).type pour Id
integer T.type:=entier
char
T.type:=caractere
C'est un attribut hrit. Pas possible de trouver un attribut synthtis faisant la mme chose
avec cette grammaire. Changeons la grammaire !
production
action smantique
Id L
, Id L
L(0).type:=L(1).type
mettre dans la table des symboles le type L(1).type pour Id
:T
L.type:=T.type
Cours de Compilation
51
integer T.type:=entier
char
T.type:=caractere
Et voil !
Mais bon, ce n'est pas toujours vident de concevoir une DDS n'utilisant que des attributs
hrits ou de concevoir une autre grammaire permettant de n'avoir que des synthtiss.
Heureusement, il existe des mthodes automatiques qui transforment une DDS avec des
attributs hrits et synthtiss en une DDS quivalente n'ayant que des attributs synthtiss.
Mais nous n'en parlerons pas ici, cf bouquins.
Une autre ide peut tre de faire plusieurs passes d'analyses, suivant le graphe de
dpendances de la DDS. Par exemple (DDS donne prcdemment) : une passe ascendante
permettant d'valuer un certain attribut synthtis s, puis une passe descendante pour
valuer des attributs hrits h et r, puis enfin un passe ascendante pour valuer un attribut
synthtis t.
6. Exercices
1.
(a)
Ecrire une DDS n'utilisant que des attributs synthtiss qui calcule le nombre de a
contenus dans un mot de (a|b)*. Donner un arbre dcor pour le mot bbabaab
(b)
Mme question avec des attributs hrits.
2.
Considrons la grammaire suivante qui gnre des expressions arithmtiques formes
de constantes entires et relles et de l'oprateur +
Lorsque l'on additionne 2 entiers, le rsultat est un entier, sinon c'est un rel
(a)
Ecrire une DDS donnant le type de l'expression
(b)
Donner un arbre dcor pour le mot 5+3.05+10
3.
On considre un robot qui peut tre command pour se dplacer d'un pas vers l'est,
l'ouest, le nord ou le sud partir de sa position courante.
Une squence de tels dplacements peut tre engendre par la grammaire
debut L fin
est | sud | nord | ouest
La position du robot est donne dans le plan (le nord tant en haut). (0,0) est la
position initiale du robot.
(a)
Ecrire une DDS affichant les positions successives (x,y) du robot.
(b)
Cours de Compilation
52
Donner un arbre dcor pour le mot debut est est nord ouest sud sud
nord ouest fin
4.
(a)
Ecrire une DDS donnant le nombre maximum de a conscutifs dans un mot de (a|b)*
(exemple : pour aababaaaba il faut donner 3)
(b)
Donner un arbre dcor pour le mot aababaaaba
5.
Ecrire une DDS permettant de traduire un entier sous forme binaire en sa valeur
dcimale. Donner un exemple d'arbre dcor.
6.
Mme exercice que prcdemment avec des rels.
7.
Considrons la grammaire suivante qui gnre des expressions arithmtiques formes
des oprateurs + et *, et de la variable x et de constantes :
E
E+T|T
T
T*F|F
F
nb | x
(a)
Ecrire une DDS qui calcule la drive d'une telle expression
(b)
Donner un arbre syntaxique dcor pour la chane 2*x*x+5*x+3
Cours de Compilation
53
CHAPITRE 6.
L'OUTIL YACC/BISON
De nombreux outils ont t btis pour construire des analyseurs syntaxiques partir de
grammaires. C'est dire des outils qui construisent automatiquement une table d'analyse
partir d'une grammaire donne. YACC est un utilitaire d'unix, bison est un produit gnu.
YACC/bison accepte en entre la description d'un langage sous forme de rgles de
productions et produit un programme crit dans un langage de haut niveau (ici, le langage
C) qui, une fois compil, reconnat des phrases de ce langage (ce programme est donc un
analyseur syntaxique).
YACC signifie Yet Another Compiler Compiler, c'est dire encore un compilateur de compilateur.
Cela n'est pas tout fait exact, yacc/bison tout seul ne permet pas d'crire un compilateur,
il faut rajouter une analyse lexicale ( l'aide de (f)lex par exemple) ainsi que des actions
smantiques pour l'analyse smantique et la gnration de code.
YACC/bison construit une table d'analyse LALR qui permet de faire une analyse
ascendante. Il utilise donc le modle dcallages-rductions.
prod1
prod2
...
prodn
Les actions smantiques sont des instructions en C insres dans les rgles de production.
Elles sont excutes chaque fois qu'il y a rduction par la production associe.
Cours de Compilation
54
Exemples :
G : S B 'X' {printf("mot reconnu");}
;
S : A {print("reduction par A");} T {printf("reduction par T");} 'a'
;
La section du bloc principal doit contenir une fonction yylex() effectuant l'analyse
lexicale du texte, car l'analyseur syntaxique l'appelle chaque fois qu'il a besoin du terminal
suivant. On peut
- soit crire cette fonction
- soit utiliser la fonction produite par un compilateur (f)lex appliqu un fichier de
spcifications nom.l. Dans ce cas, il suffira d'inclure le fichier lex.yy.c produit par
(f)lex et de rajouter la bibliothque (f)lex lors de la compilation C du fichier nom.tab.c
(avec cc nom.tab.c -ly -l(f)l).
2. Attributs
A chaque symbole (terminal ou non) est associ une valeur (de type entier par dfaut). Cette
valeur peut tre utilise dans les actions smantiques (comme un attribut synthtis). Le
symbole $$ rfrence la valeur de l'attribut associ au non-terminal de la partie gauche, tandis
que $i rfrence la valeur associe au i-me symbole (terminal ou non-terminal) ou action
smantique de la partie droite.
Exemple :
expr : expr '+' expr { tmp=$1+$3;} '+' expr { $$=tmp+$6;};
Par dfaut, lorsqu'aucune action n'est indique, yacc/bison gnre l'action {$$=$1;}
Par exemple
%union {
int entier;
double reel;
char * chaine;
}
Cours de Compilation
55
permet de stocker dans yylval la fois des int, des double et des char *.
L'analyseur lexical pourra par exemple contenir
{nombre}
{
yylval.entier=atoi(yytext);
return NOMBRE;
}
Le type des lexmes doit alors tre prcis en utilisant les noms des champs de l'union
%token <entier> NOMBRE
%token <chaine> IDENT CHAINE COMMENT
On peut galement typer des non-terminaux (pour pouvoir associer une valeur de type autre
que int un non-terminal) par
%type <entier> S
%type <chaine> expr
Cours de Compilation
56
une production
error
Dans ce cas, une production d'erreur sera traite comme une production classique. On pourra
donc lui associer une action smantique contenant un message d'erreur.
Ds qu'une erreur est rencontre, tous les caractres sont avals jusqu' rencontrer le caractre
correspondant . Exemple : La production
instr error ;
indique l'analyseur qu' la vue d'une erreur, il doit sauter jusqu'au del du prochain ";" et
supposer qu'une instr vient d'tre reconnue.
La routine yyerrok replace l'analyseur dans le mode normal de fonctionnement c'est dire
que l'analyse syntaxique n'est pas interrompue.
57
9. Exemples de fichier .y
Cet exemple reconnat les mots qui ont un nombre pair de a et impair de b. Le mot doit se
terminer par le symbole $. Cet exemple ne fait pas appel la fonction yylex gnre par le
compilateur (f)lex.
%%
mot : PI '$'
;
{printf("mot accepte\n");YYACCEPT;}
PP : 'a' IP
| 'b' PI
| /* vide */
;
IP : 'a' PP
| 'b' II
| 'a'
;
PI : 'a' II
| 'b' PP
| 'b'
;
II : 'a' PI
| 'b' IP
| 'a' 'b'
| 'b' 'a'
;
%%
int yylex() {
char car=getchar();
if (car=='a' || car=='b' || car=='$') return(car);
else printf("ERREUR : caractere non reconnu : %c ",car);
}
Ce deuxime exemple lit des listes d'entiers prcdes soit du mot somme, soit du mot
produit. Une liste d'entiers est compose d'entiers spars par des virgules et se termine par
un point. Lorsque la liste dbute par somme, l'excutable doit afficher la somme des entiers,
lorsqu'elle dbute par produit, il doit en afficher le produit. Le fichier doit se terminer par $.
Cet exemple utilise la fonction yylex gnre par le compilateur flex partir du fichier de
spcification exemple2.l suivant
%{
#include <stdlib.h>
int nbligne=0;
%}
chiffre
[0-9]
entier
{chiffre}+
espace
[ \t]
%%
somme
return(SOMME);
produit
return(PRODUIT);
Cours de Compilation
58
\n
[.,]
{entier}
nbligne++;
return(yytext[0]);
{
yylval=atoi(yytext);
return(NOMBRE);
}
{espace}+
/* rien */;
"$"
return(FIN);
.
printf("ERREUR ligne %d : %c
inconnu\n",nbligne,yytext[0]);
%%
Le fichier de spcifications yacc est alors le suivant
%token SOMME
%token PRODUIT
%token NOMBRE
%token FIN
%%
texte : liste texte
| FIN {printf("Merci et a bientot\n");YYACCEPT;}
;
liste : SOMME sentiers '.' {printf("la somme est %d\n",$2);}
| PRODUIT pentiers '.' {printf("le produit est %d\n",$2);}
;
sentiers : sentiers ',' NOMBRE {$$=$1+$3;}
| NOMBRE {$$=$1;}
;
pentiers : pentiers ',' NOMBRE {$$=$1*$3;}
| NOMBRE {$$=$1;}
;
%%
#include "lex.yy.c"
10. Exercices
1.
Ecrire un fichier de spcification Bison permettant de reconnaitre si une expression est
correctement parenthse ou non.
2.
Modifier l'exercice prcdent pour afficher les niveaux d'imbrication.
Exemple : l'expression (()()(()))() doit donner 1 2 2 2 3 1.
Cours de Compilation
59
3.
Modifier l'exercice 1 en considrant les mots begin et end au lieu des parenthses.
4.
Mini-calculateur de bureau. On considre la grammaire des expressions
arithmtiques suivante :
NOMBRE
(o NOMBRE est un nombre entier)
(a)
crire un programme qui indique si une expression arithmtique donne est correcte
ou non.
(b)
Complter les fichiers de spcifications prcdents pour
(c)
Complter les fichiers de spcifications prcdents pour que le programme retourne la
valeur de chaque expression arithmtique rentre, en considrant qu'on ne traite que
des entiers.
(d)
Mme question en considrant galement des nombres rels.
5.
Mme exercice que prcdemment en considrant cette fois la grammaire ambigu
suivante :
NOMBRE
Cours de Compilation
60
CHAPITRE 7.
ANALYSE SEMANTIQUE
Cours de Compilation
61
structure d'une table des symboles peut aller du plus simple au plus compliqu (simple tableau
tri ou non tri, liste linaire, forme arborescente, table d'adressage dispers (avec donc des
fonctions de hachage), ...), cela dpend de la complexit du langage compiler, de la rapidit
que l'on souhaite pour le compilateur, de l'humeur du concepteur du compilateur, ....
Dans certains langages, on peut autoriser qu'un identificateur ne soit pas dclar dans son bloc
B condition qu'il ait t dclar dans un bloc d'un niveau infrieur qui contient B. Il faut
galement grer le cas o un identificateur est dclar dans au moins deux blocs diffrents B
contenu dans B' (dans ce cas, l'identificateur de plus haut niveau cache les autres).
On peut alors utiliser une pile pour empiler la table des symboles et grer ainsi la porte des
identificateurs. Mais il y a d'autres mthodes, comme par exemple coder la porte dans la
table des symboles, pour chaque symbole.
Figure 9.1: Porte des identificateurs : empilement de la table des symboles
2. Contrle de type
Lorsque le langage source est un langage typ, il faut vrifier la pertinence des types des
objets manipuls dans les phrases du langage.
Exemples : en C, on ne peut pas additionner un double et un char *, multiplier un int et
un struct, indicer un tableau avec un float, affecter un struct * un char ... Certaines
oprations sont par contre possibles : affecter un int un double, un char un int, ...
(conversions implicites).
Dfinition 8.1 On appelle statique un contrle de type effectu lors de la compilation, et
dynamique un contrle de type effectu lors de l'excution du programme cible.
Cours de Compilation
62
Le contrle dynamique est viter car il est alors trs difficile pour le programmeur de voir
d'o vient l'erreur (quand il y en a une). En pratique cependant, certains contrles ne peuvent
tre fait que dynamiquement. Par exemple, si l'on a
int tab[10];
int i;
...
... tab[i] ...
Recherche_Table(Id)
reel et E(1).type
reel et E(2).type
entier)
entier) alors
erreur_type_incompatible(ligne,E(1).type,E(2).type)
sinon
reel
E(0).type := si E(1).type=entier et E(2).type=entier alors entier
sinon erreur_type(ligne,...)
Cours de Compilation
63
Ces constructeurs tant unaires, les expressions de type forms en les appliquant des types
de base ont une structure trs uniforme. Par exemple
caractre
fretourne(caratre)
tableau(caractre)
fretourne(pointeur(entier))
tableau(pointeur(fretourne(entier)))
Chaque constructeur peut tre reprsente par une squence de bits selon un procd
d'encodage simple, de mme que chaque type de base. Par exemple
constructeur codage
pointeur
01
tableau
10
fretourne
11
0000
boolen
0001
caractre
0010
rl
0011
Les expressions de type peuvent maintenant tre encode comme des squences de bit
expression de type
codage
Cours de Compilation
64
multiplication standard qui retourne un entier ou celle qui retourne un complexe ? Pour
rpondre cette question, il faut regarder comment est utilise l'expression. Si c'est dans
2+(3*5) c'est un entier, si c'est dans z*(3*5) o z est un complexe, alors c'est un
complexe. Bref, au lieu de remonter un seul type dans la TDS, il faudra remonter un ensemble
de types possibles, jusqu' ce que l'on puisse conclure. Et je ne parle pas de la gnration de
code ...
4. Fonctions polymorphes
Une fonction (ou un oprateur) est dite polymorphe lorsque l'on peut l'appliquer des
arguments de types variables (et non fixs l'avance). Par exemple, l'oprateur & en C est
polymorphe puisqu'il s'applique aussi bien un entier qu' un caractre, une structure
dfinie par le programmeur, .... On peut galement, dans certains langages, se dfinir une
fonction calculant la longueur d'une liste d'lments, quelque soit le type de ces lments.
Cette fois-ci, il ne s'agit plus seulement de vrifier l'quivalence de deux types, mais il faut
les unifier.
Un des problmes qui se pose alors est l'infrence de type, c'est dire la dtermination du
type d'une construction du langage partir de la faon dont elle est utilise. Ces problmes
ne sont pas simples et sont tudis dans le cadre des recherches en logique combinatoire et
en lambda-calcul. Le lambda-calcul permet de dfinir et d'appliquer les fonctions sans
s'occuper des types.
Cours de Compilation
65
Indice
s
Buckets
size
2
3
temp
4
5
6
Exemple de TDS de taille 7. Nous avons insr les identificateurs (i, j, size, temp) et size et j
ont la mme valeur de hachage. Sur la figure size prcde j, ceci dpend de la mthode
dinsertion des items dans une liste. Communment linsertion se fait en tte, puisque un
identificateur nouvellement dclar a plus de chance dtre utilis immdiatement.
Le nombre dentre de la TDS est connu pendant la construction du compilateur. Cette taille
est gnralement un nombre premier (permet de dfinir des meilleurs fonctions de hachage).
Par exemple il est prfrable de choisir 211 au lieu de 200.
5.1.4 Les fonctions de hachage
Convertir une chane de caractres (nom de lidentificateur) en un entier de lintervalle *0..
size-1] o size est la taille de la table. Premirement convertir chaque caractre de la chane
en un entier, en pascal on utilisera la fonction ord qui retourne la valeur ASCII dun
caractre, en C cest automatique si on utilise un caractre dans une expression arithmtique.
Ensuite combiner ces codes (en les additionnant) pour obtenir un entier unique. Enfin le
Cours de Compilation
66
n n i
h ci mod size
i 1
#define
SIZE
#define
SHIFT
Cours de Compilation
67
par des expressions de type telle que integer ou des expressions structures comme array
[1..100] of real.
Cours de Compilation
68
7. Environnement d'excution
Avant d'attaquer le problme de la gnration du code cible, il faut se poser des questions sur
l'organisation de la mmoire :
- gestion du flot de contrle (appels et retour de fonctions, instructions de saut (du type break
en C))
- stockage des variables
- allocation dynamique de mmoire
7.1 Organisation de la mmoire l'excution
Questions lies au langage source :
- les fonctions peuvent-elles tre rcursives ?
- une fonction peut-elle faire rfrence des noms non locaux ?
- comment sont passs les paramtres lors d'un appel de fonction ?
- peut-on allouer dynamiquement de la mmoire ?
- doit-on librer explicitement l'espace mmoire allou dynamiquement ?
- que deviennent les variables locales une fonction lorsque l'on sort de la fonction ?
- ...
Questions lies la machine cible :
- quelle est la longueur d'un mot ou d'une adresse ?
- quelles sont les entits directement adressables de la machine ?
- existe-t-il des instructions pour accder directement et efficacement des parties d'entits
directement adressables ?
- est-ce possible de rassembler plusieurs "petits" objets (boolen, caractre, ...) dans des units
plus
grandes ?
- y-a-t-il des restrictions d'adressage (conditions d'alignements) ?
- ...
Les rponses ces questions influent sur la faon d'organiser la mmoire.
(modle gnral)
On divise le bloc de mmoire alloues l'excution par le systme d'exploitation cible en 4
zones :
code cible
donnes statiques
Pile de contrle
tas
donnes statiques : la taille de certaines donnes est connue ds la compilation. On les place
alors dans une zone dtermine statiquement. L'avantage c'est que l'on peut alors compiler
directement leur adresse dans le code cible.
Cours de Compilation
69
pile de contrle : elle permet de grer les appels de fonctions. On y stocke les
enregistrements d'activation, c'est dire toutes les informations ncessaires lors de l'activation
et du retour d'une fonction (adresse de retour, valeur de certains registres, ...). Lors d'un appel
de fonction, un protocole d'appel alloue un enregistrement d'activation, remplit ses champs et
l'empile. Lors du retour, la squence de retour dpile et restaure l'tat de la machine afin de
permettre la fonction appelante de continuer l'excution.
tas : contient tout le reste, c'est dire les variables alloues dynamiquement au cours de
l'excution du programme.
7.2 Allocation dynamique : gestion du tas
Les allocations peuvent tre explicites (instructions new en Pascal, malloc en C, ...) ou
implicites (utilisation de cons en Lisp, appels implicites aux constructeurs en C++, ...). Les
donnes alloues sont conserves jusqu leur libration explicite (instructions free en C,
dispose en Pascal, ...) ou implicite (appels implicites des destructeurs en C++, garbage
collector en Lisp, ...).
Libration implicite
Intrt : vite les rebuts (ou miettes), c'est dire les zones mmoires alloues mais
devenues inaccessibles, et les rfrences fantmes, c'est dire une rfrence une
zone qui a t libre.
Modle de format de bloc :
taille du bloc
compteur de rfrence et/ou marquage
pointeurs vers des blocs
informations utilisateurs
Il y a deux mthodes pour grer la libration implicite :
Compteur de rfrences : On suppose qu'un bloc est utilis s'il est possible au
programme utilisateur de faire rfrence l'info contenue dedans (par un
pointeur ou en suivant une liste de pointeurs).
Cours de Compilation
70
Cours de Compilation
71
8. Gnration de code
Il s'agit prsent de produire le code cible.
Bien qu'un texte source puisse tre traduit directement en langage cible, on utilise en gnral
une forme intermdiaire indpendante de la machine cible. On produira donc dans un premier
temps du code intermdiaire, que l'on pourra ensuite optimiser, puis enfin partir du code
intermdiaire on produira le code cible.
8.1 Code intermdiaire
Le langage intermdiaire doit tre facile produire et facile traduire dans le langage cible
(quelqu'il soit).
Pourquoi passer par un langage intermdiaire ?
le "reciblage" est facilit. En effet, le langage cible dpend normemment de la machine sur
laquelle
il va tourner. Utiliser un langage intermdiaire permet de porter plus rapidement le
compilateur,
puisque les 9/10 du boulot sont dja fait lorsqu'on est arriv produire du code intermdiaire.
l'optimisation du code est facilit. Il existe de nombreux algorithmes et mthodes
d'optimisation de
code pour les codes intermdiaires les plus utiliss.
8.2 Caractristiques communes aux machines cibles
(cf cours Archi/Systme ...)
Cours de Compilation
72
Un programme crit en code 3 adresses est une squences d'instructions numrotes, ces
instructions pouvant tre
affectation binaire
(num) x := y op z
affectation unaire
(num) x := opu z
affectation indice
(num) y[i]:= z
copie
(num) x:=y
lecture
(num) lire x
ecriture
(num) ecrire y
avec
op
opu
oprel
...
, NON, ...
oprateur relationnel (
10.1
adresse
y, z
de variable ou registre
et c'est tout.
le nombre de registres disponibles est illimit
Exemple 1 : le fragment de programme C a=b*c+b*(b-c)*(-c) devient
(1)
(2)
(3)
(4)
t1
t2
t3
t4
:=
:=
:=
:=
b*c
b-c
b*t2
-c
(5) t5 := t3*t4
(6) t6 := t1+t5
(7) a := t6
t1
t2
t2
t3
:=
:=
:=
:=
b*c
b-c
b*t2
-c
(5) t2 := t3*t4
(6) t1 := t1+t5
(7) a := t1
Cours de Compilation
73
z=z+2*x;
h=5;
}
else
h=-5;
y+=h;
} while (x<=50 && z<=5*y);
(11)
(12)
(13)
(14)
(15)
(16)
(17)
(18)
(19)
(20)
aller a (14)
t6 := -5
h := t6
t7 := y+h
y := t7
si x>50 aller a (20)
t8 := 5*y
si z>t8 aller a (20)
aller a (3)
...
Soit la grammaire
La TDS utilise deux attributs : code et place (pour le nom du registre qui contient la valeur du
non-terminal)
Production
Rgle smantique
I.code=
E.code
Id.place ":=" E.place
E.place=
Id.place
E.code=
""
E(0).place= E(1).place
E(0).code= E(1).code
E(0).place= NouvRegistre()
E(0).code= E(1).code
E(2).code
E(0).place ":="E(1).place "+" E(2).place
etc.
Cours de Compilation
74
Il faudrait aussi penser aux numros des instructions, ce n'est pas fait dans cet exemple. Mais
en fait, les seuls numros importants sont lorsqu'il y a des branchements, le reste du temps on
s'en foue compltement.
8.4.2 Expressions boolennes
Dfinition 10.1 On appelle valuation paresseuse l'valuation de tous les termes qui
composent une expression boolenne
Dfinition 10.2 On appelle valuation court-circuit l'valuation d'une expression
boolenne qui n'value que les termes ncessaires
Soit la grammaire
Pour une instruction, on considre un attribut suivant qui contient l'tiquette de l'instruction
suivante effectuer (ce n'est pas toujours celle qui suit dans le code car il peut y avoir des
branchements).
La TDS qui produit le code paresseux est
Production
Rgle smantique
I(1).suivant= I(0).suivant
I(2).suivant= I(0).suivant
Vrai=NouvEtiquette()
Faux=NouvEtiquette()
I(0).code =
L.code
"si" L.place"=vrai aller a" Vrai
"aller a" Faux
"(" Vrai ")" I(1).code
"aller a" I(1).suivant
"(" Faux ")" I(2).code
L(0).place = NouvRegistre()
L(0).code = L(1).code
L(2).code
L(0).place ":=" L(1).place "ou" L(2).place
L.place = NouvRegistre()
Cours de Compilation
75
Rgle smantique
L.code=
L.vrai = NouvEtiquette()
L.faux = NouvEtiquette()
I(1).suivant= I(0).suivant
I(2).suivant= I(0).suivant
I(0).code =
L.code
"(" L.vrai ")"
I(1).code
"aller a" I.suivant
I(2).code
L(1).vrai = L(0).vrai
L(1).faux = NouvEtiquette()
Cours de Compilation
76
L(2).vrai = L(0).vrai
L(2).faux = L(0).faux
L(0).code =
L(1).code
"(" L(1).faux ")"
L(2).code
L(1).vrai = NouvEtiquette()
L(1).faux = L(0).faux
L(2).vrai = L(0).vrai
L(2).faux = L(0).faux
L(0).code =
L(1).code
"(" L(1).vrai ")"
L(2).code
L(1).vrai = L(0).faux
L(1).faux = L(0).vrai
L(0).code = L(1).code
L.code = "aller a" L.vrai
L.code = "aller a" L.faux
Avec cette TDS, l'expression si a<b ou c<d et e<f alors ... sera traduite par
(L1)
(l2)
(Lvrai)
(Lfaux)
(Lsuiv)
On remarque que le code n'est pas optimal, la seconde instruction est inutile ici. Pour corriger
cela, il faudra optimiser le code produit.
9. Optimisation de code
Le terme "optimisation" est abusif parce qu'il est rarement garanti que le code rsultat soit le
meilleur possible.
On obtient une amlioration maximale pour un effort minimal si on peut identifier les parties
du programme les plus frquemment excutes (on laisse alors tomber les autres parties). En
effet, en gnral, dans la plupart des programmes, 90% du temps d'excution est pass dans
seulement 10% du code. Mais, of course, ce n'est pas vident de deviner quels sont les "points
chauds" du programme10.2.
Cours de Compilation
77