Conception et implémentation d'un nouveau langage de programmation
1. Remerciements
En préambule à ce projet nous remerciant ALLAH qui nous aide et nous donne la patience
et le courage durant ces langues années d’étude.
Nous souhaitant adresser nos remerciements les plus sincères aux personnes qui nous ont
apporté leur aide et qui ont contribué à l’élaboration de ce mémoire ainsi qu’à la réussite de
cette formidable année universitaire.
Ces remerciements vont tout d’abord au corps professoral et administratif de l’institut
supérieure d’informatique et mathématique Monastir pour la richesse et la qualité de leur
enseignement et qui déploient de grands efforts pour assurer à leurs étudiants une formation
actualisée.
Nous tenant à remercier sincèrement Monsieur, Mohamed Graeit, qui, en tant que enca-
dreur de notre projet de fin d’études, il s’est toujours montré à l’écoute et très disponible
tout au long de la réalisation de ce projet, ainsi pour l’inspiration, l’aide et le temps qu’ils
ont bien voulu nous consacrer et sans lui ce projet n’aurait jamais vu le jour.
On n’oublie pas nos parents pour leur contribution, leur soutien et leur patience.
Enfin, nous adressons nos plus sincères remerciements à tous nos proches et amies, qui
nous ont toujours soutenue et encouragée au cours de la réalisation de ce projet. Merci à tous
et à toutes.
i
2. Dédicaces
Je dédie ce travail
À MES CHERS PARENTS
Aucune dédicace ne saurait exprimer mon respect, mon amour éternel et ma considéra-
tion pour les sacrifices que vous avez consenti pour mon instruction et mon bien être.
Je vous remercie pour tout le soutien et l’amour que vous me portez depuis mon enfance
et j’espère que votre bénédiction m’accompagne toujours.
Puisse Dieu, le Très Haut, vous accorder santé, bonheur et longue vie et faire en sorte
que jamais je ne vous déçoive.
À MES DEUX CHERS ET ADORABLES
SOEURS
En témoignage de mon affection fraternelle, de ma profonde tendresse et reconnaissance,
je vous souhaite une vie pleine de bonheur et de succès et que Dieu, le tout puissant, vous
protége et vous garde.
À MES AMIS
En souvenir de notre sincère et profonde amitié et des moments agréables que nous avons
passés ensemble.
Veuillez trouver dans ce travail l’expression de mon respect le plus profond et mon affec-
tion la plus sincère.
À TOUTES LES PERSONNES QUI ONT
PARTICIPÉ A L’ÉLABORATION DE CE
TRAVAIL, À TOUS CEUX QUE J’AI OMIS
DE CITER
Ben Belgacem Yahya
ii
3. Dédicaces
Je dédis ce modeste travail comme un témoignage d’affectation, de respect d’admiration
à :
À Ma MÉRE
Tu m’as donné la vie, la tendresse et le courage pour réussir. Tout ce que je peux t’offrir
ne pourra exprimer l’amour et la reconnaissance que je te porte. En témoignage, je t’offre ce
modeste travail pour te remercier pour tes sacrifices et pour l’affection dont tu m’as toujours
entouré.
À Mon PERE
L’épaule solide, l’oeil attentif compréhensif et la personne la plus digne de mon estime et
de mon respect. Aucune dédicace ne saurait exprimer mes sentiments, que Dieu te préserve
et te procure santé et longue vie.
À MES FRERES ET MES SOEURS
À MES CHERS AMIES
À CELLE QUI M’INSPIRE TOUJOURS
Bouein Aymen
iii
10. Introduction
La programmation, d’après le Nouveau Petit Robert 1
, recouvre « l’élaboration et la codi-
fication de la suite d’opérations formant un programme », un programme, aussi appelé
un logiciel, étant « l’ensemble des instructions rédigé dans un langage de programmation
permettant à un système informatique d’exécuter une tâche donnée ».
Cette définition souligne le rôle essentiel du langage de programmation comme vecteur de
communication entre le programmeur et l’ordinateur. Cependant, elle ne fait pas apparaître la
grande diversité des nombreux langages de programmation existants, un seul de ces langages
est directement exécutable par les circuits de l’ordinateur : il s’agit du langage machine
lorsqu’il se présente sous forme de nombres (les «0 et 1» bien connus du grand public), ou
encore du langage assembleur lorsqu’il se présente sous forme textuelle.
Ces langages machine ou assembleur sont dits «de bas niveau», car les ordres exprimés
dans ces langages manipulent directement les composants de l’ordinateur que sont les re-
gistres, la mémoire et les périphériques.
Quel que soit le langage dans lequel un programmeur écrit ses logiciels, il faut savoir passer
de ce langage de plus ou moins haut niveau au langage de la machine. La compilation est le
processus qui traduit un langage de programmation vers le langage machine. Cette traduction
est effectuée automatiquement par un programme appelé compilateur, qui produit du code
machine, les grandes différences d’expressivité entre un langage de programmation de haut
niveau et un langage machine ou assembleur rendent très difficile la transformation directe
entre les deux langages.
Nous allons explorer à quel point un compilateur est conçu et organisé. Nous allons voir
que ses principales composantes impliquent une sorte de système expert, à l’aide des règles
de production. Le principal défi dans un compilateur donner un sens à une longue séquence
de caractères dans un fichier source. C’est un problème difficile en informatique, en fait, c’est
une forme d’intelligence artificielle : comment faire correspondre quelque chose qui est claire
et sensée à un être humain dans quelque chose qui est clair et sensible à une machine comme
l’indique la figure 1.
1. Le Nouveau Petit Robert est un dictionnaire de langue française, publié par les dictionnaires le Robert,
Reconnais par sont richesse et possibilité de rechercher à travers le web.
x
11. Introduction générale
compilateurE E
c
erreur
input file output File
Figure 1 – Principe de compilateur
Un langage de programmation de haut niveau définie un abstraction : le programmeur
exprime un algorithme en utilisant le langage et le compilateur doit traduire ce programme
vers le langage cible. De façon général, il est plus facile de programmer en utilisant les
langages de plus haut niveau, mais on obtient des programmes cibles qui s’exécutent plus
lentement.
Le programmeur qui utilise un langage de plus bas niveau a plus de contrôle pour les
calculs et qui peut ensuite écrire des codes plus efficace. Malheureusement, les programmes
de bas niveau sont plus difficiles à écrire et surtout moins portable et plus difficiles à faire
évoluer. De nos jour, on remarque l’apparition de plusieurs langages de programmation dans
plusieurs domaines informatique. Mais la question que se pose " Qu’elle est le meilleur lan-
gage ? " bien sûr la réponse de cette question dépend principalement à cette de question "
Qu’elle est le domaine d’application du langage ? ".
Toutes les fois ou un langage de programmation est apparaitre, elle supplante d’autre en
terme de popularité, l’évolution s’est faite vers des niveaux d’abstraction supérieures, C fut
le langage prédominant pour la programmation système dans les années 1980, pour les projet
démarrés dans les années 1990, le choix se porta souvent vers C++, JAVA, introduit en 1995
gagna rapidement en popularité a la fin de la décennie [1] .
Le meilleure bénéfice de programmeation haut niveau est l’apparition des nouveaux outils
ou bien l’amélioration des anciens, qui sont bien équipés d’un syntaxe qui est de plus en plus
proche d’eux.
L’évolution de syntaxe de programmation est ascendant au niveau de simplicité et d’effica-
cité, on peut distinger cette évolution, lorsque on fait un étude comparatif entre C++,Java[3]
et Python[2] induquée dans les listings 1,2 et 3 .
1 #include <iostream.h>
2 int main ()
3 {
4 cout << "Hello World" ;
5 return 0
6 }
Listing 1 – Hello World avec C++
1 #include <iostream.h>
2 public class Hello
3 {
4 public static void main (String args)
5 {
6 System.out.println("Hello World") ;
7 } }
Listing 2 – Hello World avec Java
xi
12. Introduction générale
1 print("hello World")
Listing 3 – Hello World avec Python
La contrainte du temps et l’acheminement de l’apparition des langages de programmation
n’est pas une raison de jugement entre les langages de programmation et de distinguer les
fonctionnalités. Cependant, que java qui apparaitre après c++ ne supporte pas la notion de
pointeur et de multihéritage qui sont fournies avec leur prédécesseur.
xii
14. CHAPITRE 1. CONTEXTE ET ÉTAT DE L’ART
N ous avons jusqu’à ici considéré un compilateur comme une boite noire qui transforme
un programme source en un programme cible sémantiquement équivalent. Ouvrons un
peu cette boite nous distinguons deux parties dans cette transformation :
Phase d’analyse
La phase d’analyse permet principalement de décomposer le programme source en des
constituants, tout en associant à chacun une structure grammaticale, elle utilise ensuite cette
structure pour détecter si le programme source est syntaxiquement mal formé ou sémanti-
quement incorrecte. Elle accumule également diverses informations qu’elles conservent dans
une structure de données nommée table de symbole, elle est constituée principalement de
trois analyseur
Analyseur lexicale :
Le mot «lexicale» dans le sens traditionnel du terme signifie «se rapportant à des paroles»,
sur le plan des langages de programmation, les mots sont des objets comme noms de variables,
les numéros, les mots clés, etc. Ces mots sont traditionnellement appelés jetons.
Un analyseur lexical aura en entrée une chaîne des caractères de l’individu, sont principale
rôle et de diviser cette chaîne en unité lexicale. En outre, il va filtrer tout ce que sépare les
jetons (ce qu’on appelle caractère superflus).
L’objectif principal de l’analyse lexicale est de rendre la vie plus facile par la suite à
l’analyseur syntaxique. En théorie, le travail qui est effectué lors de l’analyse lexicale peut
faire partie intégrante de l’analyse syntaxique.
Analyseur syntaxique :
L’analyseur syntaxique est à peu près l’équivalent de vérifier du texte ordinaire écrit dans
un langage naturel est grammaticalement correct (sans se soucier de sens).
Le but de l’analyse de syntaxe est de vérifier que nous avons une séquence valide de jetons
envoyée par l’analyseur lexical, notez que cette séquence n’a pas besoin d’avoir un sens.
La correspondance entre le programme et les règles syntaxique est généralement hiérar-
chique, et peut être spécifiée par un arbre de dérivation pour le programme pour y utilisé
dans la phase de génération de code.
Analyseur sémantique :
L’analyse syntaxique vérife seulement que le programme se compose des jetons disposés
dans une combinaison valide de syntaxe. Maintenant, nous allons passer à l’analyse séman-
tique, où nous plongeons profondément afin de vérifier si elles forment un ensemble logique
d’instructions.
Pour qu’un programme puisse être sémantiquement valide, toutes les variables, fonctions,
classes, etc, doivent être correctement définis. Les expressions et les variables doivent être
utilisées de manière que le respect du système de type, de contrôle d’accès doivent être
respectés, et ainsi de suite.
L’analyse sémantique est l’avant-dernière phase avant la fin de la dernière chance du
compilateur pour éliminer les programmes incorrectes. Nous avons besoin d’être assurer que
le programme est suffisamment solide pour continuer à la génération de code.
2
15. CHAPITRE 1. CONTEXTE ET ÉTAT DE L’ART
Phase de synthése :
La partie de synthèse (back-end) construit le programme cible souhaité à partir de la
représentation intermédiaire et les informations dans la table des symboles.
Production de code intermediaries :
Au cours de la traduction d’un programme source en code machine pour une machine
cible, un compilateur peut générer un code de langue de niveau moyen, il est connu sous le
nom de code intermédiaire et texte intermédiaire. La complexité de ce code se trouve entre
le code de la langue source et le code objet.
Le code intermédiaire peut être représenté sous la forme d’un arbre de syntaxe à trois
adresses de code, cette représentation doit avoir deux propriétés importantes : elle doit être
facile à produire à partir de l’arbre syntaxique et facile à traduire en langage cible.
Optimisation de code :
La phase d’optimisation de code indépendante de la machine tente d’améliorer le code in-
termédiaire afin d’avoir un meilleur code cible. En général, meilleur signifie plus rapide. Mais
on peut viser d’autres objectifs, comme un code final plus court ou consommant peu d’éner-
gie. Quelques optimisations simples peuvent améliorer efficacement le temps d’exécution du
programme cible sans retarder trop la compilation.
Production de code :
Le générateur du code prend en entrée une représentation intermédiaire du programme
source et la réécrit dans un langage cible. Si le langage cible est un code machine, à chaque
variable de programme on fait correspondre un registre ou une adresse de mémoire. Ensuite
les instructions intermédiaires sont traduites en séquence d’instruction machine qui effectuent
la même tache.
3
16. CHAPITRE 1. CONTEXTE ET ÉTAT DE L’ART
Phases logiques de la compilation d’une instruction
c
Analyseur lexical
c
Position = Initiale + Vitesse * 60
<id,1> <=> <id,2> <+> <id,3> <*> <60>
Analyseur syntaxique
c
=
<id,1>
¨¨ €€
+
<id,2>
¨¨ €€
*
<id,3>
¨¨ €€
60
Analyseur sémantique
c
=
<id,1>
¨¨ €€
+
<id,2>
¨¨ €€
*
<id,3>
¨¨ €€
intToFloat
60
Génération de code intermédiaire
c
t1 = intToFloat(60)
t2 = id,3 * t1
t3 = id2 + t2
id1 = t3
Optimiseur de code
c
t1 = id,3 * 60.0
id1 = id2 + t1
Générateur de code
c
LDF R2, id,3
MULF R2, R2, 60.0
LDF R1, id2
ADDF R1, R1, R2
STF id1, R1
Table de symbole
Position1 ...
Initiale2 ...
Vitesse3 ...
Figure 1.1 – Exemple de compilation d’une instruction
4
17. CHAPITRE 1. CONTEXTE ET ÉTAT DE L’ART
Couple Flex Bison
Lex 1
et YACC 2
sont des outils Linux qui engendrent des programmes d’analyse du texte.
Les programmes générés offrent des fonctionnalités de reconnaissance, de structuration, de
traduction d’un texte écrit dans un langage donné.
Flex et Bison nouveauté de lex et yacc sont des outils de construction de programme qui
gèrent d’entrées structurées, ils ont l’origine des outils de construction des compilateur.
Flex
Flex (abréviation de Fast Lexical Analyzer) est un outil conçu à l’aide du développement
du compilateur. En particulier, flex est un outil qui s’occupe principalement à l’étape première
de la réalisation de compilateur. Comme étant un analyseur lexical, flex est un programme
qui reconnaît les modèles lexicales du texte fournie en entrée, ou son entrée standard est le
nom de fichier donné pour une description d’un scanner à générer sous la forme d’extension
".l", la description est à la forme de paires d’expressions régulières et de code C appelée règles.
Flex génère en sortie un fichier source C "lex.yy.c" par défaut ,ce fichier après avoir compiler
et générer leur exécutable analyse son entrée pour les occurrences de modèle lexicale, chaque
fois qu’elle en trouve un, il exécute le code C correspondant [4].
Bison
Bison est un outil qui s’occupe principalement à la deuxième étape de réalisation de
compilateur, comme étant un analyseur syntaxique, bison est un programme qui vérifier
le rangement des modèles lexicales envoyé par flex. Cependant flex et bison travaillent la
main dans main puisque bison appelle flex à chaque fois qu’il veut un nouveau modèles
lexicale. Bison doit être décrit par une grammaire, cela signifie que vous spécifiez un ou
plusieurs regroupements syntaxiques et donner des règles pour les construires à partir de ces
regroupements. Il prend comme entrée un fichier d’extension ".y" contenant ces regroupement
grammaticales et fournit en sortie deux fichiers portant leur nom : la première contient le
code C de l’analyseur (analyseur.tab.c) et la deuxième contient la définition des codes des
modèles lexicales (analyseur.tab.h), afin qu’elle puissent être partagée par les deux premiers
analyseur comme l’induque la figure 1.2 [5].
compilateur C
FLEXE
E
c
input File
EEa.exe
Bison.tab.c + Bison.tab.h
Flex.l
Lex.yy.c
BISONE
Bison.y
E
Figure 1.2 – Couple Flex Bison
1. Lexical parser
2. Yet Another Compiler Compiler
5
19. CHAPITRE 2. ANALYSE LEXICALE
Le rôle de l’analyseur lexical
L’analyse lexicale est la première phase d’un compilateur, la principale tâche accomplie
par cette phase consiste à identifier l’ensemble des mots d’un langage valide qui se pro-
duit dans un flux d’entrée 1
. Cependant, l’analyseur lexical agit comme une interface entre
le programme source à compiler et la suite du compilateur, il lit les caractères constituent le
programme source et les regroupés en lexèmes. L’analyseur lexical convertit ces lexèmes en
un flux des mots valides de la langue, mieux connues sous les noms d’unités lexicales (Tokens).
L’analyseur syntaxique et l’analyseur lexical travail main dans la main, dans le sens que
à chaque fois l’analyseur syntaxique a besoin de plus jeton, il demande à l’analyseur lexical
qui est à son tour numériser le flux d’entrée restantes et retourne le jeton produisant ensuite.
L’idée est expliquée dans la figure 2.1.
analyseur lexicale Suite de complilateur
Table de symbole
E
E
'
Jeton
demande de jeton
Source
¨
¨¨¨
¨¨¨¨
¨¨¨B
r
rrr
rrr
rrrr‰r
rrr
rrr
rrrrj
¨
¨¨¨
¨¨¨
¨¨¨¨%
Figure 2.1 – Principe de compilateur
En dehors de cela, l’analyseur lexical participe également à la création et à la maintenance
de la Table Symbole. C’est parce que, il est le premier module d’identifier l’apparition d’un
symbole, s’il est défini pour la première fois, il doit l’installer dans la table de symbole.
L’analyseur lexical est également responsable pour enlever les cosmétiques du programme
comme des espaces blancs supplémentaires, commentaires, etc.
Unités lexicales
Une unité lexicale est un couple constitué d’un nom lexicale et d’une valeur d’attribut
optionnelle. Le nom d’unité lexicale est un symbole abstrait représentant leur type lexicale.
Les noms d’unité lexicale sont les symboles d’entrée que traite l’analyseur syntaxique. Nous
écrivons généralement les noms d’unités lexicales en caractère gras et nous les désignerons
souvent à partir de leur noms.
1. Le flux d’entrée est l’ensemble des caractères de programme source.
7
20. CHAPITRE 2. ANALYSE LEXICALE
Motif
Un motif est une description de la forme que les lexèmes d’une unité lexicale donnée
peuvent prendre. Dans le cas d’une unité lexicale de type mot clé, le motif est simplement la
séquence de caractère qui forme le mot clé. Pour les identificateurs et d’autres types d’unités
lexicales, le motif est une structure plus complexe qui reconnait de nombreuses chaines des
caractères.
Lexème
Un lexème est une séquence de caractère dans le programme source qui est reconnue par
les motifs d’une unité lexicale, il est identifié par l’analyseur lexical comme une instance de
cette unité lexicale.
Erreur lexical
Il est difficile pour l’analyseur lexicale de dire sans l’aide des autres composants, qu’il
y a une erreur dans le code source. Par exemple si la chaine "Foor" est reconnait pour la
première fois dans un programme C dans le contexte décrit dans la Listing 2.1.
1 foor (int i =0;i<5;i++)
2 cout << i ;
Listing 2.1 – Faux boucle for
Un analyseur lexicale ne peut pas dire si foor est une orthographe erronée du mot clé for ou
un identificateur de fonction non déclaré.
Puisque foor est un lexème pour l’unité lexicale id. L’analyseur lexicale doit retourner
l’unité lexicale id à l’analyseur syntaxique et laisse une autre phase de compilateur gérer une
erreur due à la permutation des deux lettres.
Spécification des unités lexicales
Expression régulières
Les expression régulières sont les ensembles de toutes les constantes entières ou l’ensemble
de tous les noms de variables ou les ensembles des chaînes, où les lettres individuelles sont
prises à partir d’un alphabet.
Un tel ensemble de chaîne est appelée un langage. Pour les entiers, l’alphabet se compose
de chiffres de 0-9, les noms de variables de l’alphabet contient à la fois des lettres et des
chiffres (et peut-être quelques-uns d’autres caractères, tels que tiret bas).
Étant donné un alphabet, nous allons décrire des ensembles de chaînes de caractères par
des expressions régulières. Une notation algébrique qui est compact et facile pour les humains
à utiliser et à comprendre [6].
8
21. CHAPITRE 2. ANALYSE LEXICALE
Expression régulières reconnue par Flex
Les expressions régulières acceptées par Flex paraissent souvent comme les autres expres-
sions régulières on peut distinguer quelques différences au niveau de représentation et des
caractères spéciaux indique dans la figure 2.2.
Caractère Signification
+ Un ou plusieurs fois
* 0 ou plusieurs fois
? 0 ou un fois
- Intervalle d’ensemble
Caractère Signification
| Union
" Valeur littérale des carac.
. Tout car sauf fin de ligne
Complément d’ensemble
Figure 2.2 – Caractère spéciaux de Flex
Mémorisation du texte d’entrée (couple de tampons)
Il existe des cas ou l’analyseur lexical doit lire en avance quelque caractère. Une partie im-
portante du temps est consacré au déplacement des caractères. Il existe plusieurs techniques
spéciales de mémorisation permettant de réduire le coût lié au traitement des caractères
du texte d’entrée. Cependant, les techniques sont dépendantes des paramètres du systèmes.
Nous présentons dans ce qui suit les principes reliés à la reconnaissance des unités lexicales
comme l’indique le fique 2.3.
x = a 1 + 3 EOF
T
c
Avant
Head
Figure 2.3 – Un tampon d’entrée en deux moitiés
L’idée est d’utiliser un tampon divisé en deux moitiés de N caractère. N peut être le
nombre de caractère dans un bloc de disque 2
, s’il reste moins que N caractères en entrée, un
caractère spécial EOF (End Of File) est placé dans le tampon après les caractères d’entrée.
Par exemple le développeur doit gérer deux pointeurs vers le tampon d’entrée. L’ensemble
de caractères situés entre les deux pointeurs constituent le lexème en cours.
2. Le bloc de disque (en anglais cluster) est la plus petite unité de stockage d’un système de fichiers.
9
22. CHAPITRE 2. ANALYSE LEXICALE
Au départ les deux pointeurs Head et Avant désignent le premier caractère du prochain
lexème à trouver :
L’un appelé le pointeur Avant, lit en avant jusqu’à trouver un modèle, une fois que le
prochain lexème est reconnu, le pointeur Avant est positionné sur le caractère à sa droite,
après le traitement de ce lexème, les deux pointeurs sont positionnés sur le caractère qui suit
immédiatement le lexème.
Si le pointeur Avant est sur le point de dépasser la marque de moitié, la moitié droite est
remplie avec N nouveaux caractères d’entrée, si le pointeur Avant est sur le point de dépas-
ser l’extrémité droite du tampon, la partie gauche est remplie avec N nouveaux caractères
d’entrées, et le pointeur Avant poursuit circulairement au début du tampon.
Diagramme de transition
Les diagrammes de transition sont une étape préparatoire pour la réalisation d’un analy-
seur lexical. Cette étape consiste principalement à convertir les motifs exprimés sous forme
d’une expression régulière en organigramme stylisé (diagramme de transition).
Les diagrammes de transition comprennent un ensemble des nœuds ou des cercles appelés
états, chaque état représente une condition qui peut être réalisée pendant le parcours de
l’entrée à la recherche d’un lexème qui soit reconnu par un ou plusieurs motifs. Des arcs
relient un état du diagramme de transition à un autre, chaque arc est étiqueté par un symbole
ou un ensemble de symbole, si l’on est dans un état ’0’ et si le symbole d’entrée suivant est
’a’ on cherche un arc qui part de ’0’ et qui est étiqueté par ’a’, le passage d’un état à un état
et totalement relatif à l’incrémentation du pointeur Avant lors de lecture de caractère.
0start 1
a
Figure 2.4 – Exemple de diagramme de transition
On distingue deux types d’états permettant de référencer la progression de l’étape de
reconnaissance des unités lexicales :
- Un état désigné comme étant l’état de départ ou état initial : il est indiqué par un arc,
étiqueté "start" qui ne vient d’aucun arc, le diagramme de transition part de l’état initial,
avant toute lecture de symbole d’entrées.
- Certains états sont dits d’acceptation ou finaux, ces indiquent d’un lexème à été trouvé.
On indique toujours un état final par double cercle et si une action doit être effectué (typi-
quement, transmettre une unité lexicale et une valeur d’attribut à l’analyseur syntaxique)
on attache cette action à l’état final.
Parfois, nous ne somme pas obligé de revenir en arrière en décrémentant le pointeur Avant
par un pas en cas où le lexème n’inclut pas le symbole qui nous a amenés à l’état final, alors
10
23. CHAPITRE 2. ANALYSE LEXICALE
on ajoute le symbole "*" à l’état final.
La figure 2.5 montre le diagramme traduise la reconnaissance des unités lexicales INFEG,
DIFF, INF, EGAL, SUPEG, SUP et IDENTIF respectivement définie par les expressions
régulières <=, <>, <, =, >=, >.
0start 1 2
3
4
5
6 7
8
<
=
>
=
>
autre
autre
= Return(SUPEGAL)
Return(SUP)
Return(INF)
Return(DIFF)
Return(INFEGAL)
Return(EGAL)
*
*
Figure 2.5 – Diagramme de transition des opérateur
Les automates à état finie
Les expressions régulières sont pratique pour spécifier des jetons lexicaux, mais nous
avons besoin d’un formalisme qui peut être réalisé par un ordinateur comme un programme
informatique. Pour cela, nous pouvons utiliser des automates finis.
Un automate finie est un ensemble finie d’états et des arcs conduisant d’un état à un
autre. Comme étant une spécification de diagramme des transition. Les automates à état
finie sont répartie en des états(terminaux et non-terminaux), chaque état est marqué par un
symbole qui représente l’état courant [6].
Les automates à état finie non déterministe
Un automate finie non déterministe (AFN) est celui qui a un choix d’arêtes étiquetées
avec le même symbole sortir de l’état ou il peut avoir des bords spéciaux marqués avec la
lettre grecque epsilon qui peut être suivie sans passer de n’importe quel symbole de l’entrée
[6] .
Les automate à état finie déterministe
Les Automates non déterministes sont comme mentionnés plus haut, pas tout à fait proche
de "la machine" que nous le souhaiterions. Par conséquent, nous introduisons maintenant
11
24. CHAPITRE 2. ANALYSE LEXICALE
une forme plus restreinte des automates finie : L’automate finie déterministe, ou (AFD) est
un automate à état finie déterministe, mais qui obéissent à un certain nombre de restrictions
supplémentaires :
— Il n’y à pas epsilon-transitions.
— Deux transitions ne peuvent pas être étiquetées par le même état.
Cela signifie que nous n’avons jamais le choix entre plusieurs cotés d’états : le symbole
d’entrée suivant déterminer de façon unique la transition. C’est pourquoi ces automates sont
appelés déterministe.
Conception et Implémentation d’analyseur lexicales
Conception d’analyseur lexical
L’étape de conception consiste à définir les expressions régulière pour les différentes unités
lexicales, cette définition suivie d’une automate déterministe représentée sous forme d’un
diagramme de transition pour mieux comprendre l’étape de récupération de ces unités et
leur méthode de reconnaissance.
Les identificateurs :
Un identificateur est un suite de caractère et de nombre qui ne doit pas commencer par
un nombre ou caractère réservé (les opérateurs . . . ) et qui peut contenir un caractère de
séparation ’_’ (tiré bas).
Le diagramme de transition représenté dans la figure 2.6, décrit la manipulation des
identificateurs au niveau de l’analyseur lexicale.
0start 1
a-z A-Z
_
a-z A-Z
0-9
_
Figure 2.6 – Diagramme de transition des identificateur
L’expression régulière permet de reconnaître un identificateur est la suivante :
ident [a-zA-Z_] ([0-9a-zA-Z_])*
Expression régulière flex
Tout en respectant les contraintes au niveau de l’identificateur, On doit l’associer un nom
significatif pour qu’on puisse référencer ultérieurement dans l’analyseur syntaxique, dans
notre cas ident désigne un occurrence d’un lexème au niveau de code source. La deuxième
partie de l’expression flex désigne l’expression régulière habituelle qu’on trouve répartie lo-
giquement en deux parties :
12
25. CHAPITRE 2. ANALYSE LEXICALE
— La première décrit l’obligation de commencement par une lettre ou tiré bas.
— La deuxième représente la libération de choix aux développeurs.
Les chaines des caractères :
Une chaine des caractères est une suite de caractères qui sont éliminées par deux ca-
ractères spéciaux réservés pour leur récupération(""). Le problème majeur est qu’on peut
trouver un mot clé réservé au fonctionnement de compilateur (if ) dans la suite de caractère
de chaine, donc il est souvent important de déclarer l’analyseur lexicale du l’importance de
sauter à la position de deuxième séparateur sans tenir compte à l’intérieur de chaine.
Le diagramme de transition représenté ce dessus, décrit la manipulation des chaines des
caractères aux niveau de l’analyseur lexicale.
0start 1 3
"
a-z A-Z
0-9
"
Figure 2.7 – Diagramme de transition des chaines des caractères
L’expression régulière permet de reconnaître une chaine des caractères est la suivante :
"""(.)+""" { return(CHAINE) ; }
Expression régulière flex
Nous référençons ce séquencement limité des caractères par le nom d’unité lexicale chaine.
La première partie désigne tous les caractères sauf le retour à la ligne (au niveau de dia-
gramme de transition ci-dessus, a-z, A-Z désigne toutes les occurances des caractére puisque
nous pouvons pas traité tous les cas ).
Le premier guillemet élimine tous les chances pour renvoyer une autre unité à l’intérieur
de chaîne puisque le pointeur avant est initialisé et qui bascule directemant à la position de
deuxiéme guillemet.
Les caractères :
Les caractères sont des cas particuliers de chaine de caractère. Ils sont tout simplement
une chaine de longueur un. Mais la question qui se pose "est ce que l’analyseur lexicale va
envoyer une unité lexical qui référence une chaine de caractère ou bien un caractère ", deux
réponses possible pour cette question :
— La solution JAVA consiste à les distingués par les séparateur c’est à dire pour les
chaines de caractères on doit l’éliminer par les "" et pour les caractères par ”.
13
26. CHAPITRE 2. ANALYSE LEXICALE
— L’autre solution est de renvoyer deux unités lexicales distinctes référencées les chaines
des caractères et les caractère et laisser la distinction s’appuie à l’analyseur séman-
tique pour décrire le comportement de l’unité lexical, puisque une chaine de caractère
peut contenir un caractère et l’inverse est faux.
Le diagramme de transition représenté dans la figure 2.9, décrit la manipulation des caractères
au niveau de l’analyseur lexicale.
0start 1 3 4
"
a-z A-Z
0-9 "
Figure 2.8 – Diagramme de transition des caractères
L’expression régulière permet de reconnaître un caractère est la suivante :
""".""" { return(CARACTERE) ; }
Expression reguliere flex
L’unité lexicale CARACTERE se réfère à l’existence d’un caractère, leur expression ré-
gulière paraît comme celle de chaine de caractères (a-z A-Z et 0-9 désigne tous caractéres)
mais sans avoir la possibilité d’écrire plusieurs caractères.
Les entiers et réels :
Les entiers sont le séquencement des chiffres sans aucun séparateur entre eux. Les réels
sont deux entiers séparés par un point. D’après les règles arithmétiques, un réel peut contenir
un entier ce qui cause un problème au niveau de l’analyseur lexicale. Aux niveau de cette
phase en va les distingue par leur unité lexicale. La vérification de sémantique de l’apparition
de ces deux unités ce fait au niveau de l’analyseur sémantique.
Le diagramme de transition représenté ce dessus, décrit la manipulation des entiers et les
réels aux niveau de l’analyseur lexicale.
0start 1 3 4
0-9
0-9
.
0-9
0-9
Figure 2.9 – Diagramme de transition des entier et réel
L’expression régulière permet de reconnaître un entier ou réel est la suivante :
14
27. CHAPITRE 2. ANALYSE LEXICALE
nbr [0-9]+
entier nbr
reel entier.nbr
Expression reguliere flex
Les deux unités lexicales entier et reel réfèrents successivement les entiers et les réels.
Les opérateurs arithmétique et opérateurs logique
Les opérateurs arithmétiques restent inchangables au niveau des instructions arithmé-
tique. Le traitement des opérateurs logique se diffère d’un compilateur à un autre.
Pour garantir la bonne fonctionnement de compilateur multi-langages on doit associer les
différentes représentations de ces opérateur l’un à l’autre.
L’analyseur lexicale doit renvoyer la même unité lexicale, lorsqu’il rencontre la même
signification des lexèmes, par exemple le lexème OR aux niveau de VB.NET est équivalant
à celui aux niveau de C++, cet exemple est pratique à la totalité des opérateurs logique.
Les mots clés :
les mots clés sont les mots réservés à la fonctionnement de compilateur. Ils sont considéré
par l’analyseur syntaxique comme étant des identificateurs (ils aient l’air d’identificateur )
car ils ont le même motif d’expression régulière.
La reconnaissance de mot clé et des identificateurs représentent un problème, est il y’a
deux façons de traiter les mots réservé qui ont l’air d’identificateur :
— Charger les mots réservés dans une table de symbole dès le départ, un champs d’en-
trées de la table de symbole indique que les chaines ne sont jamais des identificateurs
ordinaires et indique qu’elle unité lexicale le représente.
— Créer des diagrammes de transition représentant graphiquement les différences entre
les identificateurs et les mots réservées.
Le diagramme de transition représenté dans la figure 2.10, décrit un exemple de manipu-
lation des identificateurs par rapport aux mots réservées au niveau de l’analyseur lexicale.
15
28. CHAPITRE 2. ANALYSE LEXICALE
0start
2
4 IF
5INT
1 6
7
8
9
17
13
14
STEP
Identificateur
15
16
12 STRING
3 10
Entier
11 REEL
i
0-9
s
a-z
sauf i, s
t a-z sauf t
a-z
Return
f a-z sauf f,nn
t
a-zsauft
e
r
a-z
saufr,e
n
g
a-zsaufg
p
a-zreturn
i
a-zsaufi
i
Return
a-z
Return
0-9 .
Return
0-9
0-9
return
a-z
Return
a-z
Return
Figure 2.10 – Différence entre les mots clé et les identificateurs
16
29. CHAPITRE 2. ANALYSE LEXICALE
Implémentation d’analyseur lexical
Puisque l’analyseur lexicale a le contrôle sur le flux d’entrée, il doit être attaché à l’amélio-
ration qui nécessite de compilateur. L’analyseur lexicale doit se référer à la ligne courant pour
bien identifier les erreurs lexicales ou bien syntaxiques, le listing 2.2 représente un example
non-complet d’implémentation d’analyseur lexicale.
1 %{
2 #include <stdlib.h>
3 #include "compilateur.h"
4 int line =1;
5 %}
6
7 %x IN_COMMENT
8
9 blanc [ t]+
10 nbr [0 -9]+
11 entier {nbr}
12 reel {entier }.{ nbr}
13 ident [a-zA -Z_]([0 -9a-zA -Z_])*
14
15 %%
16
17 """(.)+""" { strcpy(yylval.Tstr ,yytext);
18 return(CHAINE); }
19
20 "//"(.)+"" { strcpy(yylval.Tstr ,yytext);
21 return(COMM); }
22
23 <INITIAL > {
24 "/*" BEGIN(IN_COMMENT);
25 return(COMMPL);
26 }
27 <IN_COMMENT >{
28 "*/" BEGIN(INITIAL);
29 n line ++;
30 }
31
32 {a}{d}{d} return(ADD);
33 {v}{o}{i}{d} return(VOID);
34 {m}{a}{i}{n} return(MAIN);
35
36 {i}{n}{t}{e}{g}{e}{r} return(DEC_ENTIER);
37 {i}{n}{t} return(DEC_ENTIER);
38 {r}{e}{a}{l} return(DEC_REEL);
39 {f}{l}{o}{a}{t} return(DEC_REEL);
40 {c}{h}{a}{r} return(DEC_CARA);
41 {s}{t}{r}{i}{n}{g} return(DEC_CHAINE);
42
43 {w}{r}{i}{t}{e} return(WRITE);
44 {r}{e}{a}{d} return(READ);
45
46 {t}{h}{e}{n} return(THEN);
47 {e}{n}{d}{i}{f} return(ENDIF);
48 {e}{l}{s}{e} return(ELSE);
49 {i}{f} return(IF);
50
51 . return(POINT);
17
30. CHAPITRE 2. ANALYSE LEXICALE
52 ":" return(DEUXPOINT) ;
53
54 n {line ++;}
55
56 "=" return(AFFECT);
57
58 "(" return(PARG);
59 ")" return(PARD);
60
61 ">" return(OP);
62 "<" return(OP);
63
64 " <=" return(OP);
65 " >=" return(OP);
66
67 "&&" return(SEP);
68 "||" return(SEP);
69
70 "+" return(PLUS);
71 "-" return(MOIN);
72 "*" return(MULT);
73 %%
Listing 2.2 – Implémentation Flex
Conclusion
Nous avons vu que l’analyse lexicale est le première contact de code source avec le com-
pilateur, puisque il lit l’entrée caractère par caractère et produit en sortie un flux d’unités
lexicales en tenir compte des expressions régulière de ces unités.
18
32. CHAPITRE 3. ANALYSE SYNTAXIQUE
rôle analyseur syntaxique
A près que l’analyseur lexicale divise l’entrée en jetons, le but de l’analyse syntaxique
(également connu sous le nom Parsing ) est de recombiner ces jetons et de découvrir
la structure de texte à utilisé pour déterminer si oui ou non un texte conforme à un format
attendu.
Il s’agit de créer une représentation intermédiaire arborescente qui dépeint la structure
grammaticale du flot d’unité. Cette transformation est envoyée à la suite de processus de
compilation pour traiter ultérieurement (vérification sémantiquement). En plus de trouver
la structure du texte d’entrée l’analyse syntaxique doit également rejeter les textes invalides
par rapport aux erreurs de syntaxe.
Gestion des erreurs de syntaxe
La gestion des erreurs est déléguée au concepteur du compilateur qui prévoit la gestion des
erreurs dés le départ pourront à la fois de simplifier la structure du compilateur et améliorer sa
gestion des erreur. Les erreurs de programmation habituelle peuvent se présenter à différents
niveaux :
— Les erreurs lexicales comprennent les fautes d’orthographe dans les identificateurs, les
guillemets manquants autour d’elles destiné à constituer une chaine de caractère.
— Les erreurs syntaxiques : elles comprennent les fautes de syntaxe telles que les point
virgules mal placés ou les accolades manquantes.
— Les erreurs sémantiques : elles comprennent les erreurs sémantique telles que les in-
cohérences de type entre opérateur et opérande.
— Les erreurs logiques peuvent être de toute nature commençant par les fautes de rai-
sonnement de la part du programmeur jusqu’à l’utilisation de l’opérateur d’affectation
à la place de l’opérateur de comparaison == dans un code C. Le programme conte-
nant = peut être bien formé ; cependant, il pourrait bien ne pas refléter l’intention du
programmeur.
Une autre raison de mettre l’accent sur le rattrapage d’erreur pendant l’analyse syntaxique
est que de nombreuses erreurs paraissent sous la forme d’erreurs de syntaxe, quelles que soient
leurs causes, ils sont donc détecte lorsque l’analyseur syntaxique ne peut se poursuivre.
Les analyseurs syntaxiques modernes doivent équiper d’un gestionnaire d’erreur qui est
fondé pour l’échappement des erreurs énoncer précédemment : - signaler la présence d’un
erreur de façon claire et précise. - Se rattraper après chaque erreur suffisamment vite pour
détecter des erreurs ultérieures.
Stratégie de rattrapage des erreurs
La stratégie de rattrapage d’erreur décrit la réaction de compilateur après la détection
d’une erreur :
20
33. CHAPITRE 3. ANALYSE SYNTAXIQUE
Rattrapage en mode panique
Avec cette méthode, quand l’analyseur syntaxique découvre une erreur, il élimine les sym-
boles d’entrée les un après les autre jusqu’à on récent un qui appartient à un ensemble des
unités lexicales de synchronisation. Habituellement, les unités lexicales de synchronisation
sont des éliminateurs, tels que le point virgule.
Le concepteur du compilateur doit sélectionner les unités lexicales de synchronisation
appropriées au langage source, bien que le rattrapage au mode panique dépasse en générale
une partie considérable du texte source sans y chercher d’autre erreur[1] .
Rattrapage au niveau du syntagme
Lorsque l’analyseur syntaxique découvre une erreur, il peut effectuer des corrections lo-
cale sur le reste de l’entré, c’est-à-dire remplacer un préfixe du texte source restant par une
chaine qu’il lui permet de continuer.
Le rattrapage au niveau de syntagme a été utilisé dans différent compilateur à rattrapage
d’erreurs, car il peut corriger n’importe quelle chaine d’entrée. Son majeur défaut est qu’il a
du mal à faire face à des situation dans les quelles la véritable erreur eu lieu avant le point
de détection [1].
Grammaire non contextuelle
Une grammaire est définie par un ensemble de symbole terminal (les entrées). Un ensemble
de non-terminal (les symbole représentant des construction syntaxique) et un ensemble de
production, chacun indiquant un moyen selon lequel des chaînes représentées par un non-
terminal peuvent être construite à partir de symboles terminaux.
Définition formelle des grammaire non contextuelle
Nous avons vu qu’une grammaire non contextuelle est constituée de terminaux, de non-
terminaux, d’un symbole de départ et de productions :
- Les terminaux sont les symboles de base dont sont formées les chaînes. Le terme nom
unité lexicale est un synonyme de terminal. Nous utilisons fréquemment le raccourci unité
lexicale à la place de terminal lorsqu’il sera clair que nous ne parlons que de nom d’unité
lexicale.
- Les non-terminaux sont des variables syntaxiques qui dénotent des ensembles de chaîne.
Les chaines dénotées par les non-terminaux aident à définir le langage défini par la grammaire.
Les non-terminaux imposent une structure hiérarchique au langage, qui est la clé de l’analyse
syntaxique.
- Les productions d’une grammaire définissent la façon dont terminaux et non-terminaux
peuvent être combinés pour former des chaînes. Chaque production consiste en premier : Un
non-terminal appelé la tête ou la partie gauche de la production. Le symbole, Parfois : :=
a été utilisé plutôt que la flèche. Un corps ou partie droite est constitué d’un ou plusieurs
terminal ou non-terminal. Les composantes de la partie droite décrivent une des manières
dont le chaines du non-terminal de partie gauche peuvent être construites.
21
34. CHAPITRE 3. ANALYSE SYNTAXIQUE
Convention de notation
Pour éviter d’avoir toujours préciser que " ces symboles sont les terminaux ", " ces sym-
boles sont les non-terminaux ", les convention de notation suivante à propret des grammaire
seront désormais utilisées dans ce rapport, si une modification aura lieu, un message d’aver-
tissement sera écrit avant la section.
Les symboles suivants sont des terminaux :
— Les lettres minuscules.
— Les symboles d’opérations tels que +, - etc.
— Les signes de ponctuations tels que point, point-virgule etc.
— Les chiffres.
Les symboles suivants sont des non-terminaux :
— Les lettres majuscule.
— La lettre S, qui, quand elle est utilisé,est généralement le symbole de départ.
— Les mot en minuscule et italique.
— Les lettres grecques.
Dérivation
L’idée de base de dérivation est de considérer que les productions des règles de réécriture :
Chaque fois que nous avons un non-terminal, on peut remplacer cela par le côté droit de toute
production dans lequel le non-terminal apparaît sur le côté gauche. Nous pouvons le faire
n’importe où dans une séquence de symboles terminaux et non-terminaux et répéter jusqu’à
ce faisant.
La séquence résultant des terminaux est une chaîne de caractères dans le langage défini
par la grammaire. Formellement, nous définissons la relation de dérivation =>.
Arbre d’analyse et dérivation
Un arbre d’analyse est la représentation graphique d’une dérivation qui ne garde pas trace
de l’ordre dans lequel les productions ont été appliquées pour remplacer les non-terminaux.
Chaque nœud d’un arbre d’analyse représente l’application d’une production. Le nœud
est étiqueté par le non-terminal de la partie gauche de la production, les fils du nœud sont
étiqueté, de gauche à droite, par les symboles de la partie de la production par laquelle le
non-terminal a été remplacé pendant la dérivation.
Analyse syntaxique descendante
Un analyseur descendant commence en construisant le nœud racine de l’arbre, qu’il sait
devoir étiqueter avec le symbole de départ. Il construit ensuite les nœuds de l’arbre syntaxique
en pré-ordre. Ce qui signifie que la racine d’un sous-arbre est constitué avant tous ses nœuds
descendantes [9].
22
35. CHAPITRE 3. ANALYSE SYNTAXIQUE
De façon équivalente, l’analyseur syntaxique descendant peut être vue comme la recherche
d’une dérivation gauche pour une chaine d’entrée.
Soit la grammaire :
E → T E’
E’ → + T E’ |
T → F T’
T’ → * F T’ |
F → ( E ) | id
E
dd
T
F
id
T’
E’ ˆˆˆˆˆˆ
+
dd
T T’
F
id
dd
T’
dd
* F T’
id
Figure 3.1 – Analyse syntaxique descendante de id + id * id
A chaque étape d’une analyse descendante, le problème clé est celui du choix de la pro-
duction à appliquer pour un certain non-terminal.
Une fois qu’une production choisie, le reste du processus d’analyse syntaxique consiste à re-
connaître dans la chaîne d’entrée les symboles terminaux de la partie droite de la production.
Analyse syntaxique par descente récursive
Une descente récursive, dans le cas général peut nécessiter des retours en arrière, c’est-à-
dire qu’elle peut avoir à parcourir plusieurs fois certaines portions de l’entrée. Cependant, les
retours en arrière sont rarement nécessaire pour effectuer l’analyse syntaxique de construction
de langage de programmation.
Grammaire LL(1)
Les analyseurs syntaxiques par descente récursive qui ne nécessitent par le retour en
arrière, peuvent être construit pour une classe de grammaire que l’on a appelé LL(1). Le
premier L dans LL(1) provient du sens de parcours à droite (en anglais Left). Le second
L vient de fait que l’on construite une dérivation gauche (en anglais Leftmost dérivation).
Le 1 indique que l’on n’utilise qu’un seul symbole de pré-vision à chaque étape de l’analyse
syntaxique pour prendre les décisions sur les actions à effectuer [1].
Analyse syntaxique ascendante
Une analyse syntaxique ascendante ( en anglais, bottom-up) correspond à la construction
d’un arbre d’analyse pour une chaîne d’entrée en partant des feuilles et en remontant jusqu’à
la racine (le haut ).
23
36. CHAPITRE 3. ANALYSE SYNTAXIQUE
Il est utile de décrire l’analyse syntaxique comme étant le processus de construction des
arbres d’analyse, bien qu’une partie frontale puisse en réalité effectuer une traduction directe,
sans construction explicite de l’arbre [1].
Soit la grammaire :
E → E + T | T
T → T * F | F
F → ( E ) | id
Analyse ascendant de l’instruction id * id :
id * id F * id
id
T * id
F
id
T * F
F
id
id
T
dd
T * F
F
id
id
E
T
dd
T * F
F
id
id
Figure 3.2 – Analyse syntaxique ascendante de id * id
Les chaines de cette suite sont formées à partir des racines de tous les sous-arbres. La suite
commence par la chaîne d’entrée id * id. La première réduction produit F * id en réduisant
le id le plus à gauche en F, à l’aide de la production F → id. La seconde réduction produit
T * id en réduisant F en T.
Nous avons maintenant le choix entre réduire la chaîne T, qui est la partie droite de E →
T,et réduire la chaine composée du second id, qui est la partie droite de F → id. Plutôt que
de réduire T en E, le second id est réduit en F, ce qui donne la chaîne T * F. Cette chaîne
se réduit alors en T. L’analyse syntaxique se termine par la réduction de T en E, le symbole
de départ.
Analyse syntaxique par décalage-réduction
On peut voir que l’analyse ascendante comme étant un processus de réduction d’un flux
d’unité lexicale vers le symbole de départ de la grammaire. A chaque étape de réduction,
une sous-chaîne particulière, reconnue par la partie droite d’une production, est remplacée
le non-terminal de partie gauche de cette production.
L’analyse syntaxique par décalage-réduction est une forme d’analyse syntaxique ascendante
ou une pile stocke des symboles grammaticaux et ou un tompon d’entrée stocke la partie
de la chaîne restant. Le symbole $ indique le fond de la pile ainsi que l’extrémité droite de
l’entrée.
Bien que les opérations de base soient le décalage et la réduction, un analyseur syntaxique
par décalage- réduction peut effectuer en réalité quatre action : (1) décaler, (2) réduire, (3)
accepter, (4) erreur.
1. Décaler le symbole d’entrée suivant depuis l’entrée vers le sommet de la pile.
2. Réduire, l’extrémité droite de la chaine à réduire doit être au sommet de la pile. Repérer
l’extrémité gauche de la chaine à réduire au sein de la pile et décider du non-terminal qui
24
37. CHAPITRE 3. ANALYSE SYNTAXIQUE
doit remplacer cette chaîne.
3. Accepter, annoncer le succès de l’analyse syntaxique.
4. Erreur, Si une erreur de syntaxe est trouvée [1].
La figure décrite ce dessus, montre le principe de fonctionnement de l’analyseur syntaxique
par décalage-réduction en manipulant l’instruction id1 * id2 :
Pile Entrée Action
$ id1 * id2 $ décaler
$ id1 * id2 $ réduire par F → id
$ F * id2 $ réduire par T → F
$ T * id2 $ décaler
$ T * id2 $ décaler
$ T * id2 $ réduire par F → id
$ T * F $ réduire par T → T * F
$ $ réduire par E → T
$ $ accepter
Figure 3.3 – Analyse syntaxique par décalage-réduction sur l’entrée id1 * id2
L’analyseur syntaxique charge l’instruction id1 * id2 dans la chaine d’entrée tout en
initialisant la pile à vide et appliqué l’action décaler pour envoyer le lexème id1 à la pile,
le choix de l’action ce fait par rapport à l’entête de pile, dans la deuxième instruction la
réduction de non-terminal id1 par la partie gauche de dérivation, et ainsi de suite jusqu’à
traité l’entrée et vidage de la pile ou bien trouvé un lexème qui ne correspond à aucune
dérivation.
Conception et implémentation d’analyseur syntaxique
Conception d’analyseur syntaxique
table de symbole
Une table de symbole ( ou liste de noms) peut être vu comme un tableau extensible d’ar-
ticle, qui peut être indexé par une chaîne au lieu d’un entier. La chaîne est l’identificateur,
et l’article associer contient les informations rassemblées sur cet identificateur plus précis les
article sont les propriétés de l’identificateur comme leur type, nature . . .
Lorsqu’ on appelle le table de symbole avec le nom de l’identificateur, il renvoie un article
de type information, et une référence sur les autres identificateurs. Le concepteur du compi-
lateur choisit le type de l’information relatif à l’identificateur de manière à pouvoir y utiliser
tout-au-long de processus de compilation.
25
40. CHAPITRE 3. ANALYSE SYNTAXIQUE
Conception d’un compilateur multi-langage
Le rôle principale de multi-compilateur est de faire un mélange de syntaxe des compila-
teur les plus populaires, c’est à dire de suivre une dérivation selon le choix de développer, de
plus de migrer leur puissant fonctionnalité l’une à l’autre.
L’idée de ce mélange peut être traitée lors de séparation de grammaire de langage des
compilateur, et laisser l’analyseur syntaxique vise le ligne de code souhaité traiter par rap-
port leur propre grammaire.
C++ et java1
Déclaration de donnée :
1 DECLARATION -> DECLARATION_TYPE IDENTIFICATEURS INITIALISATION ;
2 -> DECLARATION_TYPE [entier] IDENTIFICATEUR INITIALISATION ;
3
4 IDENTIFICATEURS -> identificateur
5 -> identificateur , IDENTIFICATEURS
6
7 INITIALISATION -> epsilon
8 -> = Identificateur
9 -> = entier
10 -> = reel
11 -> = chaine
12 -> = caractere
13
14 DECLARATION_TYPE -> int "type = entier"
15 -> real "type = reel "
16 -> char "type = caractere"
Listing 3.1 – Grammaire déclaration de donnée
Les mots écrivent en minuscule lors de la dérivation de non-terminal INITIALISATION
représente les lexèmes envoyer par l’analyseur lexicale.
La séparation entre DECLARATION et DECLARATION_TYPE est à pour utilité de
diminué la dérivation de grammaire et de facilité la récupération de type de donnée, l’ajout
aux niveau de table de symbole nécessite un couple de nom de donné et leur type ainsi que
leur nature.
La nature de donnée est récupérer à travers la dérivation c’est-à-dire le ligne de code nous
donne une impression sur la nature de donnée, le nom d’identificateur ou bien dans notre cas
la possibilité de déclarer plusieurs identificateurs en un seul ligne de grammaire, le type de
ou des identificateurs doit précéder leur nom.
L’étape d’ajout ce fait réellement lors de dérivation de non-terminal IDENTIFICATEUR.
28
41. CHAPITRE 3. ANALYSE SYNTAXIQUE
Boucle conditionnelle :
1 IF_INST -> if ( CONDITION ) { COMMANDE_SEQ } ;
2 -> if ( CONDITION ) { COMMANDE_SEQ } else IF_INST
3 -> if ( CONDITION ) { COMMANDE_SEQ } else { COMMANDE_SEQ } ;
4
5 CONDITION -> identificateur OP COND_DROITE SEP CONDITION
6 -> identificateur OP COND_DROITE
7 -> ( identificateur OP COND_DROITE ) SEP CONDITION
8 -> ( identificateur OP COND_DROITE )
9
10 COND_DROITE -> identificateur
11 -> entier
12 -> reel
13 -> chaine
14 -> caractere
15
16 OP -> <
17 -> <=
18 -> >
19 -> >=
20 -> ==
21 -> !=
22
23 SEP -> &&
24 -> ||
Listing 3.2 – Grammaire boucle conditionnelle C++ et Java
Les opérateurs (OP) et séparateurs (SEP) non-terminaux représentent deux regroupe-
ment des lexèmes passées par le même valeur lors de phase d’analyse lexicale.
Jusqu’à ici le valeur des opérateurs nous intéressons pas, ce qui nous intéressons pour
le moment est que ces non-terminal forment bien notre description textuelle de l’analyseur
syntaxique (grammaire).
Instruction Switch :
1 SWITCH_INST -> switch ( VALEUR_DE_TEST ) { CASES DEFAULTS }
2
3 VALEUR_DE_TEST -> identificateur
4 -> EXPRESSION
5 CASES -> epsilon
6 -> case VALEUR : COMMANDE_SEQ break ; CASES
7 -> default : COMMANDE_SEQ break ;
8
9 VALEUR -> entier
10 -> reel
11 -> caractere
12 -> chaine de caractere
Listing 3.3 – Instruction Switch C++ et Java
Les parenthèses qui suivent le mot clé "switch" indiquent une expression dont la valeur
est testée successivement par chacun des case.
29
42. CHAPITRE 3. ANALYSE SYNTAXIQUE
Lorsque l’expression testée est égale à une des valeurs suivant un case, la liste d’ins-
tructions qui suit celui-ci est exécutée. Le mot clé break indique la sortie de la structure
conditionnelle. Le mot clé default précède la liste d’instructions qui sera exécutée si l’expres-
sion n’est jamais égale à une des valeurs.
Boucle For :
1 FOR_INST -> for ( DECLARATION CONDITION ; AFFECT ) { COMMANDE_SEQ } ;
Listing 3.4 – Grammaire Boucle for C++ et Java
Les paramètres de boucle indiquée dans la grammaire ci-dessus parées différentes à celle de
boucle répétitif (for) habituelle puisque le point-vergues ne figure pas entre les non-terminaux
DECLARATION et CONDITION, mais lorsque en examine attentivement le non-terminal
DECLARATION en peut distingué l’existence des points lors de l’analyse.
Boucle do-while :
1 DO_INST -> do { COMMANDE_SEQ } while ( CONDITION ) ;
Listing 3.5 – Grammaire Boucle while C++ et Java
Boucle while :
1 WHILE_INST -> while ( CONDITION ) { COMMANDE_SEQ } ;
Listing 3.6 – Grammaire Boucle Do-while C++ et Java
Grammaire VB.NET2
Déclaration de donnée :
1 DECLARATION -> dim IDENTIFICATEURS as DECLARATION_TYPE INITIALISATION
2
3 IDENTIFICATEURS -> identificateur
4 -> identificateur , IDENTIFICATEUR
5
6 DECLARATION_TYPE -> integer "type = entier"
7 -> real "type = reel"
8 -> caractere "type = caractere"
9 -> string "type = chaine de caractere"
Listing 3.7 – Grammaire déclarative de donné VB.NET
Comme l’indique la grammaire décrite ci-dessus il est impossible d’ajouter l’identificateur aux
niveau de table de symbole lors de dérivation de non-terminal IDENTIFICATEUR puisque
l’analyseur syntaxique n’a pas collecté les informations nécessaires pour l’insertion aux niveau
de table de symbole.
L’analyseur syntaxique doit rapporté l’ajout après la récupération de type de l’identifi-
cateur.
30
43. CHAPITRE 3. ANALYSE SYNTAXIQUE
Boucle conditionnelle :
1 IF_INST -> CONDITION_VB then COMMANDE_SEQ else COMMANDE_SEQ endif
2 -> CONDITION_VB then COMMANDE_SEQ end if
3
4 CONDITION_VB -> identificateur OP COND_DROITE SEP CONDITION_VB
5 -> identificateur OP COND_DROITE
6
7 SEP -> and
8 -> or
9
10 OP -> <
11 -> <=
12 -> >
13 -> >=
14 -> ==
15 -> !=
Listing 3.8 – Grammaire boucle conditionnelle VB.NET
Boucle For :
1 FOR_INST -> for identificateur INITIALISATION to DONNEE step DONNEE
COMMANDE_SEQ next
2
3 DONNEE -> identificateur
4 -> entier
5 -> reel
6 -> chaine
7 -> caractere
Listing 3.9 – Grammaire boucle For VB.NET
Boucle do-while :
1 DO_INST -> do COMMANDE_SEQ loop TYPE_LOOP CONDITION COMMANDE_SEQ
2
3 TYPE_LOOP -> while
4 -> until
Listing 3.10 – Grammaire boucle Do-While VB.NET
Le boucle répétitif do-while représente deux possibilités de continuations de l’exécutions
while et until, while représente la continuité si la condition est vrai et l’autre invoque la sortie
de boucle si la condition est faut.
Boucle while :
1 WHILE_INST -> while CONDITION_VB COMMANDE_SEQ end while
2
3 CONDITION_VB -> identificateur OP COND_DROITE SEP CONDITION_VB
4 -> identificateur OP COND_DROITE
Listing 3.11 – Grammaire boucle While VB.NET
31
44. CHAPITRE 3. ANALYSE SYNTAXIQUE
Choix de language : Bibliothèque
Comme tout les langages de programmation moderne chaque appelle à une fonctionnalité
prédéfinie nécessite l’importation de son propre bibliothèque, de ce principe l’utilisation d’un
tels langages est accordée à l’importation de leur syntaxe.
1 IMPORT -> epsilon
2 -> add " identificateur " IMPORT
Listing 3.12 – Importation de langage
l’identificateur délimité par les deux " représente le nom de langage voulue importé.
La distinction de syntaxe selon le choix prédéfinie représente un avantage pour les langages
de programmation, pour nous la bibliothèque ne stocke pas les fonctionnalités prédéfinie mais
elle exprime le comportement de l’analyseur lors la phase de l’analyse pour gagner le temps
lors de choix de l’ensemble de grammaire avant de commencer.
Grammaire de notre compilateu3
Déclaration de donnée :
Cette section représente la façon ou le développeur peut insérer ces porteurs de donnée en
indiquent de deux façons la nature de ces données, la déclaration en C++ et Java représente
un repère de choix des instructions de déclaration, donc nous gardons leur grammaire en
éliminant seulement les point-virgule.
Le principe de compilateur multi-langage peut augmenté le bénéfice de développer, en
peut remarquer ce gain lorsque en met l’accent à la déclaration et la manipulation de chaine
de caractère qui est déclaré comme étant un tableur de caractères qu’elle est costumé en java
et VB.NET avec leur propre type "String".
De plus la possibilité de déclaration normale de donnée, nous fournissons un mécanisme qui
permet de déclarer un ou plusieurs identificateur à partir d’un autre qui précède en hiérarchie
de code source ou bien en passant un valeur et laisser l’analyseur syntaxique conclut leur type.
1 DECLARATION -> DECLARATION_TYPE IDENTIFICATEUR INITIALISATION
2 -> type IDENTIFICATEURS comme DONNEE
3
4 DONNEE -> identificateur
5 -> entier
6 -> reel
7 -> chaine
8 ->caractere
9
10 DECLARATION_TYPE -> int
11 -> real
12 -> char
13 -> string
14
15 IDENTIFICATEURS -> identificateur
16 -> identificateur , IDENTIFICATEURS
17
32
45. CHAPITRE 3. ANALYSE SYNTAXIQUE
18 INITIALISATION -> epsilon
19 -> = Identificateur
20 -> = entier
21 -> = reel
22 -> = chaine
23 -> = caractere
Listing 3.13 – déclaration de donné
Les trois lignes de code représentées ci-dessus donnent une vision de l’avantage de choix :
int a
type i comme a => type de i est un entier
type j comme 1.2 => type de j est un réel
Lecture des variables :
Cette partie représente l’ensemble des outils qui permet aux développeurs de gérer les
données récupérer à partie de clavier lors de l’étape de l’exécution elle est attachée égale-
ment à la phase de génération de code donc elle doit être décrite d’une façon qui facilite leur
manipulation lors des autres phases de compilation, de plus, elle doit être facile et compré-
hensible dans le choix de leur grammaire, l’utilisation de mot clé significative pour rendre
compte leur fonctionnalité pour cela en à choisir le mot clé «read».
La grammaire d’écrite dans le listing 3.14 représente une proposition de lecture de va-
riable :
1 LECTURE_VARIABLE -> read identificateur
2 -> read identificateur , LECTURE_VARIABLE
Listing 3.14 – description préalable de grammaire de lecture de variable
Cette représentation nécessite un complémentaire de façon qu’elle peut distingué l’allo-
cation mémoire de deux identificateurs passait en même temps.
Dans l’habitude la lecture d’une variable est précèder par un message d’affirmation, c’est
pour cela en à améliorer la grammaire décrite dans le listing 3.14 pour qu’elle garantie aux
développer d’affermer l’utilisateur en un seul ligne de code :
1 LECTURE -> read LECTURE_VARIABLE
2
3 LECTURE_VARIABLE -> AFFERMATION identificateur
4 -> AFFERMATION identificateur , LECTURE_VARIABLE
5
6 AFFERMATION -> epsilon
7 -> chaine :
Listing 3.15 – grammaire de lecture de variable
Les deux lignes de codes représentées ci-dessous donnent une vision de l’avantage de
choix :
read "Entrer a" : a
read "Entrer a" : a , b
33
46. CHAPITRE 3. ANALYSE SYNTAXIQUE
Affichage de donné :
Cette partie représente l’ensemble d’outils qui permet aux développeurs de gérer l’affi-
chage des données et de renvoyer les résultat, elle doit être capable de renvoyer les données
récupérés lors de l’écriture de données , les types de données ainsi que les instructions ma-
thématiques.
Elle doit être attacher à un mot clé significatif pour décrire son fonctionnalité, le mot
choisie est «white», Le retour à la ligne est décrit par le mot clé « NL».
La grammaire d’écrite das la listing 3.16 représente une proposition d’affichage des ins-
truction :
1 AFFICHAGE -> write INSTRUCTIONS
2
3 INSTRUCTIONS -> INSTRUCTION
4 -> INSTRUCTION , INSTRUCTIONS
5
6 INSTRUCTION -> nl
7 -> identificateur
8 -> entier
9 -> reel
10 -> chaine
11 -> caractere
12 -> EXPRESSION
Listing 3.16 – Grammaire d’affichage de donnée
Les ligne de code représentées ci-dessus donnent une vision de l’avantage de choix :
write nl
write " a = " ,a
write nl , " b = " , b
write nl , " a + b = " , a + b
Affectation :
Cette partie représente une description de changement de contenue des variables, comme
nous avons parlées aux niveaux de table de symbole qui permet de stocker les informations
relative aux comportement des identificateurs et non pas à stoker leur contenue cependant
le contenue est stoké aux niveau de mémoire dans des registres de donnée, donc on doit
préciser à l’analyseur syntaxique le séquencement des identificateur pour nous fournir la ré-
partition ou bien le s’enfuit des erreurs après la conversion du code source à langage machine.
Le listing 3.17 décrit la grammaire d’aafectation.
1 AFFECTATION -> identificateur = STR_EXP
2 -> identificateur = EXP
3
4 STR_EXP -> chaine
5 -> caractere
6
7 EXP ->identificateur
8 ->entier
34
47. CHAPITRE 3. ANALYSE SYNTAXIQUE
9 ->reel
10 ->EXP OP_AR EXP
11 -> - EXP
12 -> ( EXP )
13
14 OP_AR -> +
15 -> -
16 -> ^
17 -> *
18 -> /
Listing 3.17 – Grammaire d’affectation
Les lignes des codes représentées ci-dessous donnent une vision de l’avantage de choix :
a = "****"
a = -1
a = a +( b - (c - a))
string a
a = a + "****" => cette fausse instruction est traitée syntaxiquement
int a
a = "****" => cette fausse instruction est traitée sémantiquement
Les priorités des opérateurs ainsi que les parenthèses doit être prennent en considération
lors de l’analyse syntaxique.
Boucle For :
Cette partie représente une description de proposition de boucle itératif "for", cette repré-
sentation est basé sur le syntaxe des langages choisie comme référence et qui sont décrivent
dans la section précédente (C++,Java,VB.net).
La première idée est d’éliminer les parenthèses ainsi que les points virgule de boucle for
aux niveau de C++ est Java, la deuxième est de rattacher les phases de l’initialisation, condi-
tion et incrimination l’une à l’autre avec l’ajout des éliminateurs pour faciliter la distinction
entre le compteur et leur paramètre.
Le listing 3.18 décrit la grammaire de notre proposition de boucle For.
1 INST_FOR -> for DECLARATIONS :: INSTRUCTION : CONDITONS : INSTRUCTION {
COMM_SEQ }
2
3 DECLARATIONS -> identificateur
4 -> DECLARATION
5
6 INSTRUCTION -> identificateur
7 -> entier
8 -> reel
9 -> chaine
10 -> caractere
11 -> EXPRESSION
12
35
48. CHAPITRE 3. ANALYSE SYNTAXIQUE
13 CONDITION -> INSTRUCTION
14 -> CONDITION
Listing 3.18 – Grammaire boucle for
Les lignes de codes représentées ci-dessous donnent une vision sur l’avantage de choix :
for int i : : 0 : i > 5 : 1 {}
for i : : i = 0 : 5 : i = i +1 {}
for i : : 0 : i > 3 and test = true : 1
{
for int j : : j = i : j > 100 : 2
{
Write nl ," j = ", j
}
}
Les vérifications de compatibilités de type ainsi que les conditions ne font pas partie à la
fonctionnalité de l’analyseur syntaxique, les erreurs de mal placement des instructions interne
dans le boucle "for" sont gérer dans le chapitre président " l’analyseur sémantique ".
Instruction select :
Cette partie représente une description de proposition de l’instruction select (Swith en
C++ et Java), elle est basé essentiellement à l’instruction select de VB.net avec quelques
modifications au niveau des mots clé reversées aux distinction entre les sous partie (cases).
Le listing 3.19 décrit la grammaire de notre proposition de boucle Select.
1 SELECT_INST -> select VALEUR { CASES }
2
3 VALEUR -> identificateur
4 -> EXPRESSION
5
6 CASES -> :: INSTRUCTION : COMMAND_SEQ
7 -> :: INSTRUCTION to INSTRUCTION : COMMAND_SEQ
8 -> :: INSTRUCTION : COMMAND_SEQ CASES
9
10 INSTRUCTION -> identificateur
11 -> entier
12 -> reel
13 -> chaine
14 -> caractere
15 -> EXPRESSION
Listing 3.19 – Grammaire instruction select
Les lignes des codes représentées ci-dessous donnent une vision sur l’avantage de choix :
select a
{
: : 1 : write "a = 1"
36
49. CHAPITRE 3. ANALYSE SYNTAXIQUE
: : 5 : write "a = 5"
: : 2 to 3 : write "Valeur entre 1 et 3"
}
Implémentation d’analyseur syntaxique
table de symbole
Le code C représenté dans le listing 3.20 représente une implémentation de principe
de table de symbole décrit dans la section conception, et comme on à dit que le table de
symbole doit gérer les données et leur présence dans les méthodes c’est pour ce la les deux
structure sym_node abréviation de Symbole_node et sym_node_methode abréviation de
Symbole_node_methode sont invoquées.
1 typedef struct sym_node
2 {
3 char name [50];
4 int type;
5 struct sym_node *next;
6
7 }sym_node;
8
9 typedef struct sym_node_methode
10 {
11 char name [50];
12 int type;
13 struct sym_node *liste_var;
14 struct sym_node_methode *next;
15
16 } sym_node_methode ;
Listing 3.20 – Implémentation table de symbole
La manipulation de table de symbole nécessite des sous programmes pour gérer leur
entrées ainsi que leur sortie, le code C décrit dans le listing 3.21 illustre les entêtes des sous
programme de manipulation.
1 sym_node_methode *put_meth(char *meth_name , int meth_type)
2 sym_node_methode *get_meth(char *meth_name)
3 int get_meth_type(char *meth_name)
4
5 sym_node *put_sym_donn(char *nom_meth ,char *sym_name , int sym_type)
6 sym_node *get_sym_donn(char *nom_meth ,char *sym_name)
7 int get_sym_donn_type (char *nom_meth ,char *sym_name)
8 int get_sym_donn_nature (char *nom_meth ,char *sym_name)
Listing 3.21 – Entête des sous programme ralative à table de symbole
Les sous programmes responsable à la manipulation des méthodes :
— put_meth : Elle représente le sous programme responsable de l’ajout d’une mé-
thode au niveau de table de symbole, leur type de retour est un pointeur de type
sym_node_methode qui référence aux nouveau table de symbole.
37
50. CHAPITRE 3. ANALYSE SYNTAXIQUE
— get_meth : Elle représente le sous programme responsable à l’envoie de méthode par
rapport à son nom.
— get_meth_type : Elle représente le sous programme responsable à l’envoie de type
de méthode par rapport à leur nom, le type est référencé par un indice unique tels
que 0 pour les sous programmes qui ne renvois pas de résultat ou bien 1,2,3,4 pour
les entier, les réels,les caractères et chaines de caractères dans l’ordre.
Les sous programmes responsable à la manipulation des données :
— put_sym_donn : Elle représente le sous programme responsable à l’ajout de donnée
au niveau d’une méthode passé en paramètre l’eur type de retour est un pointeur de
type sym_node qui référence à la liste de donnée de cette méthode.
— get_sym_donn : Elle représente le sous programme responsable à l’envoi de liste de
variable à partir de nom de méthode.
— get_sym_donn_type : Elle représente le sous programme responsable à l’envoie de
type de donnée au niveau de méthode passée en paramètre qui à le même principe de
retour de méthode.
— get_sym_donn_nature : Elle représente le sous programme responsable à l’envoie
de nature de donnée au niveau de méthode passé en paramètre qui renvoie l’entier 5
lorsque le type est un variable, 6 si le type de retour est un constante.
Bibiliothéque
Le code C d’écrit dans le listing 3.22 représente une impression de principe de fonction-
nement de bibliothèque
1 typedef struct bib_node
2 {
3 char name [50];
4 struct bib_node *next;
5 }bib_node;
Listing 3.22 – Implémentation Bibliothèque
La manipulation de bibliothèque nécessite des sous programmes pour gérer leur entrées
ainsi que leur sortie, le code C décrit dans le listing .23 illustre les entêtes des sous programme
de manipulation.
1 bib_node *put_bib(char *bib_name)
2 bib_node *get_bib(char *bib_name)
Listing 3.23 – Entête des sous programme ralative aux Bibliothèque
Ces sous programmes représente une description de dérivation de analyseur syntaxique
contrairement aux table de symbole qui représente une vision sémantique de notre compila-
teur :
— put_bib : elle représente un sous programme qui gérer l’entrée à la premier référence
de bibliothèque leur occurrences en générale au premier de dérivation de syntaxe pour
38
51. CHAPITRE 3. ANALYSE SYNTAXIQUE
bien garantir leur meilleur complexité.
— get_bib ; elle représente un sous programme qui gérer la partie de test de validation de
syntaxe c’est-à-dire de vérifier la possibilité d’utilisation de syntaxe par le développer.
Bison(implémentation de grammaire )
Après avoir étudier et comprendre le principe de table de symbole et la bibliothèque notre
analyseur syntaxique doit avoir un lien dynamique entre ces deux composants, et de bien
contrôler leur instances.
structure bison Code C générer
% {
Pré code C
% }
Définitions et options
% %
Règles de production
% %
Post code C
Déclarations
Copie du post code C
int yyparse(void)
{ Copie du code C }
Tables d’analyse
Règles de production
Copie du pré code C
Figure 3.6 – Un tampon d’entrée en deux moitiés
L’analyse syntaxique se compose par deux principaux phases, La première représente un
étude préalable et l’établissement de lien avec l’analyseur lexicale et le deuxième représente
la phase de dérivation de grammaire décrite lors de l’implémentation .
• étude préalable
Il représente les outils de travail ainsi quelque condition que l’analyseur syntaxique
doit le prenne en considération.
• Instanciation de table de symbole et bibliothèque :
L’analyseur syntaxique doit conserver d’une et une seul instance de table de symbole
et de bibliothèque, pour ne pas tombé dans le cas ou plusieurs variable portent le
même nom, ainsi de pouvoir récupérer les problème lors de l’analyse.
Les erreurs traitées par l’analyseur syntaxe sont globalement des fautes de syntaxe
lors de dérivation, mais en doit conservé une architecture pour intégrer l’analyseur
sémantique .
39
52. CHAPITRE 3. ANALYSE SYNTAXIQUE
• Affirmation des unités lexicale terminaux :
L’analyseur syntaxique doit être avertisse qu’il y’a des mots non dérivable lors de
phase d’analyse et qu’il doit remonté dans l’arbre de dérivation pour qu’il puissent
continuée leur travail.
Ce séquencement représente les suites des unité lexicale, ils doivent avoir le même
suite de caractère décrite lors de l’implémentation de l’analyseur lexicale (valeur entre
accolade suit la commande return).
• Association de type à STR_EXPR et EXPR décrite lors de l’affectation :
Comme en à dit lors de décrire la grammaire de l’affectation, et le table de symbole
établie dans le choix de notre langage, chaque porteur de donnée doit avoir un type
encapsulé sous un entier prédéfinie l’accent ce met maintenant sur les parties droite
de signe d’affectation de l’expression, l’analyseur syntaxique doit être capable de re-
tourner le type de résultat finale de l’expression lors de dérivation.
1 %type <Tint > STR_EXP
2 %type <Tint > EXP
Listing 3.24 – Association de type à STR_EXPR et EXPR décrite lors de l’affectation
• Associer les priorités des opérateurs arithmétique :
Il est important de déclarer l’analyseur syntaxique de respecté les priorités des opéra-
teurs arithmétique, ces opérateurs se distinct en deux partie ou la dérivation ce faite de
manière normale de sous expression avant le symbole arithmétique jusqu’à atteindre
l’autre symbole ou bien fin de ligne, l’autre partie représente un saut de dérivation
vers la partie droite.
Lors de dérivation normale les opérateurs arithmétiques en aussi leur propre priorité,
aux niveau de bison il est importé de déclarer les opérateurs selon leur priorité par
rapport à l’hiérarchie de l’apparence dans le code.
1 %left MULT DIVS
2 %left PLUS MOIN
3 %right PUIS
4 %left NEG
Listing 3.25 – Associer les priorités des opérateurs arithmétique
• Indicateur d’erreur :
Il est obligatoire de compter le nombre des erreurs du lors de l’analyse syntaxique, l’in-
dicateur des l’erreurs est attaché au variable "ligne" déclarer lors de l’analyse lexicale
est qui incrémente lors de retourne à la ligne, respectant le principe de continuité de
dérivation lors de récupération des erreurs chaque erreur doit traité à part c’est-à-dire
de conservé une partie propre à celle ci.
Dans cette phase de processus de compilation, en doit seulement déclarer l’indicateur
de ligne de l’erreur est de l’envoyer à un sous-programme spécifique pour le traiter
comme erreur de syntaxe, les autres erreurs sont décrivent au niveau de chapitre sui-
vant.
40