Vous êtes sur la page 1sur 12

République Algérienne Démocratique et Populaire

Ministère de l’Enseignement Supérieur et de la Recherche


Scientifique

2ème année Cycle Supérieur (2CS)


Option : Systèmes Informatiques et Logiciels (SIL1)

Thème :
PROJET DE COMPILATION
Rapport finale

Réalisé par :
-ARAB Bilal
-BELHADEF Anis
-BOUKHIMA Lilia
-BENABBES Ilyes

Demandé par : M.ABDMEZIEM Riyadh

Promotion 2023-2024
Table des matières

1 Introduction 2

2 Analyse lexicale 2
2.1 Flex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Table de symboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

3 Analyse syntaxique 5
3.1 BISON (YACC) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.2 Méthode de travail sur BISON . . . . . . . . . . . . . . . . . . . . . 5

4 Analyse sémantique 7
4.1 Quadruplets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.2 Routine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.3 Les instructions réalisés . . . . . . . . . . . . . . . . . . . . . . . . . 8

5 Conclusion 11

1
1 Introduction

Aujourd’hui, plusieurs langages de programmation coexistent, chacun présen-


tant des variations dans la manière dont le code est rédigé. Néanmoins, tous ces
langages reposent sur trois étapes fondamentales : l’analyse lexicale, l’analyse syn-
taxique et l’analyse sémantique. Ces étapes sont suivies de la génération du code
intermédiaire, puis de la production du code objet. On appelle l’ensemble de ces
étapes le processus de compilation.
Ainsi, la compilation consiste à traduire un langage écrit par l’humain vers un
langage que la machine comprend. C’est précisément l’objet de notre projet, qui
implique la réalisation des trois premières étapes :
• L’analyse lexicale
• L’analyse syntaxique
• L’analyse sémantique

2 Analyse lexicale

Est-ce une chaîne de caractères, un nombre, un opérateur, un mot-


clé... ? C’est à cette question que répond l’analyseur lexical.
Le lexeur, ou analyseur lexical, ou encore tokenizer, est la partie d’un langage
qui convertit l’entrée, c’est-à-dire le code que vous souhaitez exécuter, en jetons
(Tokens) que le parseur (à voir par la suite) peut comprendre. Si vous envisagez
votre programme comme une série de phrases, les jetons seraient les mots de ces
phrases. La tâche du lexeur est d’extraire ces mots (jetons) et de les étiqueter avec
un type. Pour effectuer l’analyse lexicale, on utilise ici les expressions régulières.

2.1 Flex

Il existe plusieurs outils tels que Flex qui facilitent l’analyse lexicale. La struc-
ture de Flex est la suivante : du côté gauche, une expression régulière définit
comment le jeton est apparié ; du côté droit, l’action à effectuer. Les expressions
régulières du côté gauche sont appliquées de manière répétée, dans l’ordre, à la
portion suivante de la chaîne de code en entrée. Lorsqu’une correspondance est
trouvée, l’action du côté droit est exécutée. La valeur du jeton est stockée dans
yylval et le type de jeton est renvoyé (String, Identificateur, Mot clé )
yyval : yyval est une structure utilisée pour stocker la valeur associée à un
token. Par exemple, yyval.int_val pour un entier, yyval.real_val pour un
nombre à virgule flottante, et yyval.string_val pour une chaîne de caractères.

2
strcpy() : La fonction strcpy est utilisée pour copier une chaîne de caractères
dans une autre. Utilisez pour copier le texte du token (yytext) dans la structure
yyval.string.
atoi() : La fonction atoi convertit une chaîne de caractères représentant un
entier en une valeur entière.
atof() : La fonction atof convertit une chaîne de caractères représentant un
nombre à virgule flottante en une valeur réelle.
yytext : yytext est une variable dans Flex qui contient le texte correspondant
au dernier token trouvé.
yyleng : Variable qui contient la longueur du dernier token trouvé. Utiliser
pour mettre à jour la position de la colonne.
yywrap() : Fonction appelée lorsqu’il n’y a plus d’entrée à traiter. (Renvoyer
1, indiquant la fin de l’analyse lexicale).

2.2 Table de symboles

La table des symboles est utilisée par le compilateur pour enregistrer les iden-
tificateurs et leurs attributs, ces attributs peuvent être des informations d’empla-
cement, de type de valeur ..., Notre table de symbole est mise sous forme d’un
tableau dont la structure est ainsi :

typedef struct Token {


char Nom [256];
char type [256];
char valeur [256];
int numeroLigne ;
int numeroColonne ;
struct Token * suiv ;
} Token ;
La table des symboles sera alimentée au fur et à mesure dans les différentes
étapes de notre analyse. Les manipulations que nous effectuerons sur cette table
sont définies par l’insertion d’un Token avec ses différentes informations avec la
fonction insererToken. De plus, la fonction getTokenInfo sera utilisée pour
récupérer les informations associées à un nom de token donné.

3
Table 1 – Exemples de tokens reconnus par Flex

Token Description
INTEGER Nombre entier
FLOAT Nombre flottant
IDENTIFIER Identificateur
KEYWORD_IF Mot-clé if
OPERATOR_PLUS Opérateur d’addition
SEMICOLON Point-virgule
STRING Chaîne de caractères

Extrait de fichier fl.y

Figure 1 – Extrait de fichier fl.y -1

Figure 2 – Extrait de fichier fl.y -2

4
3 Analyse syntaxique

L’analyse syntaxique a pour objectif de structurer la séquence d’unités lexicales


obtenues lors de l’analyse lexicale, vérifiant l’ordre dans lequel ces unités sont
données et s’assurant qu’elles respectent la syntaxe définie par le langage. Cette
syntaxe est généralement formulée à l’aide d’une grammaire non contextuelle
de type (2). Pour accomplir cette mission, on fait appel à des générateurs de
parseurs, qui transforment la grammaire du langage en un analyseur syntaxique. Ce
dernier joue un rôle crucial dans la création d’un arbre syntaxique représentant
la structure du programme source. Contrairement à l’analyse lexicale, qui traite des
séquences de caractères, l’analyse syntaxique se penche sur des motifs spécifiques
de jetons.

3.1 BISON (YACC)

Bison est une version moderne de Yacc, le parseur le plus largement utilisé.
Yacc signifie Yet Another Compiler Compiler, car il compile la grammaire vers un
compilateur de tokens.il est le plus souvent utilisé avec Lex, il a été porté vers plu-
sieurs langages cibles. Comme Lex, Yacc compile une grammaire en un analyseur
syntaxique. Voici comment une règle de grammaire Yacc est définie : À gauche,
on définit comment la règle peut être appariée en utilisant des types de tokens
et d’autres règles. À droite, entre crochets, se trouve l’action à exécuter lorsque
la règle est appariée. Dans ce bloc, nous pouvons faire référence aux valeurs de
token appariées en utilisant 1,2, etc. Enfin, nous stockons le résultat dans

3.2 Méthode de travail sur BISON

• On a inclus le fichier Flex (fl.l) dans notre fichier Bison (fl.y). De plus,
dans Flex, nous avons inclus le fichier généré après la compilation de notre
fichier fl.y (fl.tab.h), ainsi que la table des symboles (symtable.c).
• Autres fonctions prédéfinies dans Bison nous aidant dans cette analyse in-
cluent la fonction yyerror, utilisée pour traiter les erreurs de syntaxe.

• Nous devons indiquer au parseur quels tokens s’attendre. Ainsi, chaque type
de token produit par notre analyseur lexical doit être déclaré ici.

5
• Nous avons défini les différentes priorités des opérateurs, cela indique au par-
seur dans quel ordre analyser les expressions contenant des opérateurs.

• Déclaration de l’union (%union) pour spécifier les différents types de données


que l’analyseur syntaxique utilisera pour stocker les valeurs associées aux to-
kens.

• Déclaration de la structure de la table qui va contenir nos quadruplets (à voir


plus dans la 3ème étape).

• On a déclaré la grammaire syntaxique de notre langage.

6
Figure 3 – Extrait de fichier Bison

4 Analyse sémantique

L’analyse sémantique vise à attribuer un sens aux structures grammaticales du


programme source et à vérifier leur cohérence sémantique. Cette étape prend en
compte des aspects tels que :

• Recueille les informations sur les types des identificateurs et met à jour la
table des symboles.

• Assure le contrôle des types.

• Vérifie la conformité aux règles sémantiques, telles que l’utilisation correcte


des identificateurs, les règles de portée, etc.

• Gère les erreurs sémantiques et génère des messages d’erreur appropriés en


cas de non-conformité.

4.1 Quadruplets

Les quadruplets sont utilisés comme une forme intermédiaire entre l’analyse syn-
taxique et la génération de code final. Ils permettent de simplifier la représentation
du code tout en conservant l’essence des opérations à effectuer.
Un quadruplet est composé de quatre éléments :

• Opération : Il s’agit de l’opération effectuée, telle qu’une opération arithmé-


tique, logique, ou toute autre instruction.

• Opérande 1 : C’est la première opérande de l’opération.

• Opérande 2 : C’est la deuxième opérande de l’opération.

• Résultat : C’est l’endroit où le résultat de l’opération sera stocké


Voici donc la forme utiliser :

7
Figure 4 – Quadruplet

4.2 Routine

Ce sont des actions injectées dans la grammaire syntaxique et qui s’exécutent


chaque fois qu’un contrôle sémantique est nécessaire.
Dans ce projet, nous avons enrichi le fichier Bison fl.y intégrant des quadru-
plets, permettant ainsi la création d’une représentation intermédiaire. De plus,
nous avons incorporé des routines aux endroits où un contrôle sémantique est
requis. Il s’agit d’une traduction dirigée par la syntaxe.

4.3 Les instructions réalisés

1. Les déclarations (Integer, String et Double)


2. Vérification de types
3. La boucle while
4. Les opérations arithmétiques et de comparaison
5. L’affectation
6. Output
7. Input

Decalaration Lors des déclarations, nous vérifions d’abord si l’identifiant


utilisé pour la déclaration existe déjà dans la table de symboles. Si tel est le
cas, c’est une erreur sémantique. Sinon, nous l’insérons avec son type(Integer,
String ou Double). (Note : Il n’est pas possible de faire une affectation en
même temps qu’une déclaration.)

8
Figure 5 – Routine declaration

Affectation
Dans la routine d’affectation, nous commençons par vérifier si le type de la
valeur associée à l’identifiant auquel nous affectons correspond bien à son
type déclaré que nous avons déjà stocké dans la table de symboles lors de la
déclaration. Si c’est le cas, nous insérons la valeur affectée. Sinon, une erreur
sémantique.

Figure 6 – Routine affectation

Les opérations arithmétiques PLUS Voici donc aussi un exemple de l’opé-


ration arithmétique PLUS (+) où l’on effectue la vérification sur les différents
types de l’expression que l’on peut avoir, afin d’associer le bon type au membre
gauche de la production et donc le champ résultat de notre quadruplet.

Figure 7 – Routine arithmetique -PLUS

DIVIDE
Ici aussi, on vérifie les différents cas possibles que les expressions peut avoir
sur le type. De plus, nous gérons le cas où l’utilisateur essaie de diviser par
zéro, ce qui est également considéré comme une erreur

9
Figure 8 – Routine arithmetique -DIVIDE

Extrait sur les opérateurs de comparaison

Figure 9 – Rouine comparaison

Note : Pour pouvoir effectuer l’opération MINUS sur un identifiant, il faut


le mettre entre parenthèses
Boucle While Dans notre boucle while, à chaque itération, nous vérifions la
condition. Si elle est fausse, nous effectuons un branchement vers l’instruction
qui suit la fin du while. Sinon, nous continuons l’exécution, et à la fin du while,
nous effectuons un branchement inconditionnel vers la condition du while pour
une nouvelle vérification.

Figure 10 – Routine Boucle while

Input - Output

10
Figure 11 – Routine Input Output

5 Conclusion

Ce projet nous a permis de bien comprendre les différentes notions vues en


cours sur les différentes phases de compilation, notamment l’analyse lexicale,
l’analyse syntaxique, et l’analyse sémantique. Il nous a offert un aperçu plus
approfondi au-delà d’un simple langage de programmation, nous permettant
d’explorer en détail le fonctionnement d’un langage de programmation, y com-
pris des aspects moins évidents et moins étudiés dans la théorie. Certes, ce
travail ne présente pas une compilation complète, car il reste encore du trai-
tement sur les différentes grammaires de notre langage, ainsi que la nécessité
d’anticiper toutes les entrées que l’utilisateur peut introduire et saisir, étant
donné que nous sommes aveugles sur ces aspects. Ceci est suivi d’une phase
d’optimisation du code et ensuite de la génération du code objet.

11

Vous aimerez peut-être aussi