Vous êtes sur la page 1sur 6

Chapitre 2 Développement de programmes

Chapitre 2
_________________________________________________________
Environnement de développement de programmes
____________________________________________________________________________

2.1- Introduction
Pour être exécuté par le processeur physique, un programme doit être préalablement soumis à
une série de transformations. Ces transformations sont réalisées d’une manière totalement
automatique grâce à des outils logiciels fournis par le niveau un (sous couche 12) du système
informatique. L’ensemble de ces outils constitue l’environnement de programmation. Un
environnement ordinaire comprend souvent les outils logiciels tels que : Editeur de Texte,
Traducteurs, Editeur de liens, Chargeur, et le Débogueur. A la différence des programmes
d’application, ces outils sont considérés comme des programmes systèmes dont certains font
partir du système d’exploitation..

2.2- Outils pour le développement de programmes


2.2.1- Editeur de Texte
La vocation principale de cet outil logiciel est de permettre au programmeur de composer
(saisir) le code source de son programme (programme écrit en assembleur ou en langage
évolue Tels que Pascal, C,etc…), et de le conserver sur un support externe. Outre ces
fonctions de base un éditeur de texte doit permettre la modification par insertion ou par
suppression des instructions du code source, et l’impression de celui ci

Exemple 2.1
Edit du DOS, NE (Northon Editor) de Northon, emacs de l’UNIX

2.2.2- Traducteurs
Un traducteur est un programme de service (système) ayant pour vocation la traduction d’un
programme écrit dans un langage évolué en un programme équivalent écrit dans le langage
machine. Nous distinguons deux (02) types de traducteurs qui diffèrent essentiellement par la
manière de procéder :
Interpréteur
Compilateur

2.2.2.1- Interpréteur
Un interpréteur est un logiciel permettant de traduire et d’exécuter ligne par ligne le code
source d’un programme (code écrit dans un langage évolué). L’interpréteur fonctionne selon
le principe suivant (voir schéma 2.1): lecture d’une instruction du code source du programme,
et traduction de celle ci. La traduction commence toujours par une analyse de l’instruction en
question, le résultat confirme la présence ou l’absence d’erreur. En cas d’erreur l’interpréteur
abandonne le processus de traduction, et invite le programmeur à corriger l’erreur détectée.
Dans le cas contraire, l’interpréteur génère la séquence du code machine correspondant à
l’instruction, et envoie celle ci au processeur pour exécution. Une fois, l’ exécution s’achève
l’interpréteur passe à l’instruction suivante et refait le cycle lecture/traduction jusqu’à la fin
du programme.
Notons que, la contrainte majeure de cette catégorie de traducteurs est qu’elle ne produit pas
un code exécutable ( programme équivalent). Ceci impose la présence de l’interpréteur à
chaque fois qu’on veut exécuter le programme.

Module systèmes d’exploitation 2006/2007 page 1 Université de Batna Département d’Informatique


Chapitre 2 Développement de programmes

Tant que(not(EOF(Source))) faire /* Source étant le fichier source*/


Lire_Instruction ;
Analyser_Instruction ;
Si (Instruction_Correcte) alors
Gener_sequence_code_machine ;
Executer_sequence_code_machine ;
Sinon Diagnostic_erreur ;
Abondonner_traduction ;
Finsi ;
FinTantque ;

Figure 2.1 : Le schéma général d'un interpréteur

2.2.2.2- Compilateur
Un compilateur est un logiciel permettant de traduire un programme écrit dans un langage
évolué en un programme équivalent. La notion d’équivalence est utilisée pour indiquer que le
code généré par le compilateur réalise le même objectif que le code source, sauf que celui ci
est un code objet (code en langage machine auquel manque certaines informations pour être
exécuté par le processeur). Le processus de traduction dans ce schéma est assuré par les six
(06) modules qui composent le compilateur.

1-Analyseur lexicographique
Le rôle essentiel de ce module est la reconnaissance des unités lexicales (tokens), en
inspectant le texte du programme source caractère par caractère. Les unités lexicales peuvent
être des nombres, des identificateurs ( de variables, de procédures, etc…) , des mots clés, des
opérateurs, etc….Chaque unité lexicale est décrite par un type, et éventuellement une valeur.
Le type indique le type de unités à savoir mot clé, identificateur, constante, opérateur, etc…
La valeur indique le nom symbolique de l’identificateur tel qu’il figure dans le texte du code
source, elle est sans signification pour les autres types d’unité. Outre cette fonction principale,
l’analyseur lexical, s’occupe également de :
La création de la table des symboles qui contiendra tous les identificateurs et les
constantes accompagnés de leurs attributs (type, N0 de bloc, etc…).
Elimination des blancs et des commentaires.
Traitement d’erreurs à caractère lexical, en particulier : les identificateurs trop longs,
les nombres illégaux et les caractères non inclus dans le vocabulaire du langage.

Exemple 2.2 unités lexicales (Tokens)


Soit l’énoncé suivant :
for i :=1 to vmax do a :=a+i;

La liste des unités lexicales figurant dans ce fragment de programme sont :

For : mot clé a : identificateur


i : identificateur := : affectation
:= : affectation a : identificateur
1 : entier + : opérateur arithmétique
to : mot clé i : identificateur
vmax : identificateur ; : séparateur
do : mot clé

Module systèmes d’exploitation 2006/2007 page 2 Université de Batna Département d’Informatique


Chapitre 2 Développement de programmes

Notons que la réalisation de cet analyseur utilise les automates comme concept de base.
Exemple 2.3 Automate pour la reconnaissance des nombres
Chiffre
Chiffre

1 2 Point décimal 3
Chiffre
2-Analyseur syntaxique
Ce module a pour vocation de vérifier que le programme à compiler respecte bien les règles
syntaxiques du langage. Cette vérification a pour objet de produire l’arbre syntaxique du
programme à compiler en se basant sur la liste des unités lexicales produite par l’analyseur
lexical, et l’ensemble des règles syntaxiques du langage (règles de production). Outre cette
fonction principale, l’analyseur syntaxique mis à jour la table des symboles en ajoutant les
informations manquantes telles que les types des identificateurs, et traite les erreurs telles
que : manque de parenthèse, structure de bloc ou instruction mal construite, manque de
délimiteurs, etc… L’arbre syntaxique peut être obtenue en utilisant une approche ascendante
ou descendante. Dans la première approche, on part des unités lexicales composant la phrase à
analyser, et on essaye de retrouver toutes les règles permettant de remonter jusqu’à l’axiome
(racine). L’ensemble de ces règles constitue l’arbre syntaxique Dans la seconde approche, on
part de l’axiome et on essaye de sélectionner les règles conduisant à la phrase à analyser.

Exemple 2.4 Arbre syntaxique


Considérons l’ensemble des règles syntaxiques définissant une expression arithmétique
<expression> ::= <facteur> | <facteur> <operateur additif> <expression>
<facteur> ::= <terme> | <terme> <operateur multiplicatif> <facteur>
<terme> ::= <identificateur> | <nombre> | ( <expression> )
<operateur additif> ::= + | -
<operateur multiplicatif> ::= * | /
L’arbre syntaxique correspondant à l’expression 23*2+5 en utilisant l’approche descendante
est la suivante
<expression>

<facteur> <operateur additif> <expression>

<terme> <opérateur multiplicatif> <facteur> + <facteur>

<terme>
<nombre> * <terme>

<nombre>
<nombre>
23

5
2

Module systèmes d’exploitation 2006/2007 page 3 Université de Batna Département d’Informatique


Chapitre 2 Développement de programmes

3-Analyseur sémantique
Ce composant prend en charge la vérification de la sémantique du programme à compiler.
Pour réaliser cette tâche l’analyseur utilise l’arbre syntaxique produite par l’analyseur
syntaxique pour identifier les opérandes et les opérateurs. Il utilise également la table des
symboles pour identifier les types des opérandes. Les erreurs traitées par ce composant sont :
Identificateur non déclaré, Identificateur dupliqué, Incompatibilité de type, etc…

4-Génération du code
C’est la partie du compilateur qui réalise effectivement la traduction du programme source
en un programme équivalent. Le programme équivalent est souvent appelé code objet. Le
code objet peut être le code de la machine sur laquelle le programme est appelé à être
exécuter, ou le code d’une machine abstraite par opposition à la machine physique. Le choix
de telle ou telle approche dépend des objectifs du concepteur du compilateur. La première
approche est surtout utilisée lorsque la portabilité du compilateur sur une autre plate forme est
négligée, dans le cas contraire, c’est la seconde approche qui est appelée à être utilisée. La
génération du code basée sur la première approche consiste à parcourir l’arbre syntaxique et
générer pour chaque unité lexicale une séquence de code objet. Cependant dans la seconde
approche, on génère dans un premier temps du code intermédiaire (code de la machine
abstraite), dans ce cas à chaque unité lexicale figurant sur l’arbre syntaxique, on lui fait
correspondre un code de la machine abstraite. Dans le deuxième temps, on génère le code
objet proprement dit, en traduisant chaque instruction de la machine abstraite en un séquence
d’instructions de la machine réelle. Le choix d’une bonne machine abstraite permet de
réduire la complexité de tâche génération du code. La meilleure machine est celle qui
facilite l’obtention du code intermédiaire à partir de l’arbre syntaxique d’une part , et d’autre
part la traduction du code intermédiaire en code objet proprement dit.
Parfois, si dans la machine cible le temps processeur et la taille mémoire sont critiques ; on
réalise une optimisation du code intermédiaire. Plusieurs techniques d’optimisation sont
utilisées, les plus répandues sont l’optimisation locale et l’optimisation des boucles

Exemple 2.5 Optimisation locale

IF(X .LT. Y) GO TO 10 IF(X .GE. Y) GO TO 20


GO TO 20 10 Write(*,*) Y
10 Write(*,*) Y

Exemple 2.6 Optimisation des boucles

DO 10 Indice=1,N X=10
X=10 DO 10 Indice=1,N
Y= X*Indice Y= X*Indice
Write(*,*) Y Write(*,*) Y
10 Continue 10 Continue

Le code objet ainsi généré est un texte qui a la forme du code machine, mais qui est incomplet
dans le sens où certaines instructions contiennent des références non encore résolues et que
les adresses sont toutes relatives au début du programme (objet) qui se trouve par convention
à l’adresse zéro. Ces références peuvent être internes ou externes. Dans le premier cas, ce sont
les sauts en avant et en arrière résultant de la traduction des instructions de contrôle.

Module systèmes d’exploitation 2006/2007 page 4 Université de Batna Département d’Informatique


Chapitre 2 Développement de programmes

Exemple 2.6 Traduction de la boucle Tant que

While(condition) do E : evaluer la condition (* résultat dans le Reg R *)


begin BF R, E1 (* aller à E1 si R=Faux *)
Sequence d’instructions Sequence d’instructions
end ; BE (* aller à E *)
E1 : … suite du code

Au moment de la génération de l’instruction contenant de telle référence (interne) , le


compilateur se trouve dans l’une des situations suivantes , soit que l’instruction référencée est
déjà générée, soit que celle ci est non encore générée. Dans les deux cas, la résolution devient
coûteuse, car les compilateurs ne gardent jamais tout le code généré en mémoire centrale.
L’intervention du compilateur pour ce type de références se limite simplement à une
indication pour dire que cette référence est non encore résolue, ce qui permet au processus
chargé de finaliser le travail du compilateur de procéder à sa résolution. Le second cas
(référence externe) se présente lorsque le programme à compiler est structuré en modules. Les
modules sont liés par une relation de type module utilisateur/module fournisseur dans
laquelle le module utilisateur utilise des objets définis dans le module fournisseur. Ces objets
sont de deux types : les variables et les procédures. La compilation dans ce cas se fait d’une
manière séparée, au moment de génération du code du module utilisateur les adresses des
objets externes référencés sont totalement inconnues, ce qui oblige le compilateur à mettre
également là, une indication au processus chargé de la finalisation du travail.

Remarque Importante : Les références internes peuvent être résolues par le compilateur
moyennement une lecture supplémentaire du code objet. Dans ce cas, le compilation
s’effectuera en deux (02) passes. Dans la première passe le compilateur construit la table des
Labels, et on génère du code objet avec des références internes non résolues. La passe deux
mis à jour le code objet en insérant les adresses manquantes à partir de la table des Labels

2.2.3- Editeur de liens (Linker)


L’éditeur de liens est un module du système d’exploitation ayant pour vocation de compléter
le travail des compilateurs en insérant dans le code généré, les adresses que ces derniers n’ont
pas pu résoudre. Les fonctions ( services) prises en charge par un éditeur de liens sont :
1. Résolution des références internes : Il s’agit ici des sauts en avant et en arrière laissés
par les compilateurs (respectivement Assembleurs) à une seule passe. Cette fonction est
souvent prise en charge par le compilateur
2. Incorporation de procédures de bibliothèque : Les programmes utilisent souvent les
procédures fournies par la bibliothèque du langage ou celle du système d’exploitation. Le
rôle de cette fonction (Incorporation) est d’ajouter au code objet généré par le
compilateur le code se trouvant dans la bibliothèque des procédures utilisées par le
programme.
3. Compilation séparée : les programmes structurés en modules sont souvent compilés
d’une manière séparée. Cette façon de faire permet un gain considérable de temps, en ce
sens que si un module est modifié, il n’est pas nécessaire de recompiler les autres
modules. Le rôle de cette fonction est de réassembler les modules compilés séparément
pour en constituer un module exécutable unique.
4. Mélange de langages : Les programmes de taille importante peuvent utiliser des
procédures écrites dans d’autres langages différents que celui dans lequel le gros
programme est rédigé. Le rôle de cette fonction est d’assurer ce mélange de langages.

Module systèmes d’exploitation 2006/2007 page 5 Université de Batna Département d’Informatique


Chapitre 2 Développement de programmes

2.2.4- Le chargeur (Loader)


Le chargeur est un module du système d’exploitation ayant pour tâche le chargement d’un
programme exécutable en mémoire centrale et lui passe le contrôle (démarre son exécution).
L’opération de chargement consiste à placer les instructions du programme en mémoire, et
rendre les adresses des opérandes de ces instructions absolues en ajoutant à chacune d’elles
l’adresse absolue du chargement. Notons que les instructions contenant une référence relative
sont identifies grâce à une marque laissée par l’éditeur de liens.

2.2.5- Le débogueur (Debugger)


Le débogueur est un module du système d’exploitation dont la vocation est d’aider le
programmeur à mette au point son programme (détection et correction des erreurs). Les
principales fonctions fournies par ce module sont celles qui permettent :
La visualisation du contenu de la mémoire (Dump)
La visualisation du contenu des différents registre de la machine
L’exécution pas à pas du programme.

Module systèmes d’exploitation 2006/2007 page 6 Université de Batna Département d’Informatique