COMPILATEUR DECA Documentation de conception

Transcription

COMPILATEUR DECA Documentation de conception
PROJET GENIE LOGICIEL
COMPILATEUR DECA
Documentation de conception
GROUPE 8 - EQUIPE GL42
BION Jorey
DOUDECHE Amine
POLISANO Kévin
THIARD Florence
27 janvier 2012
Table des matières
1 Étape A : Analyse syntaxique
1.1 Description . . . . . . . . . . . . . . . . . . .
1.2 Architecture . . . . . . . . . . . . . . . . . . .
1.3 Choix de conception . . . . . . . . . . . . . .
1.3.1 Choix d'accrochage du numéro de ligne
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 Étape B : Vérications contextuelles et Décoration
2.1 Description . . . . . . . . . . . . . . . . . . . . . . .
2.2 Architecture . . . . . . . . . . . . . . . . . . . . . . .
2.3 Choix de conception . . . . . . . . . . . . . . . . . .
2.3.1 Rôle des procédures Verif_NonTerminal . . .
2.3.2 Utilisation systématique du case . . . . . . .
2.3.3 Traitement itératif des règles récursives . . . .
2.3.4 Messages d'erreur . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3 Étape C : Génération de code
3.1 Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Choix de conception . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1 Considérations générales sur les paramètres des procédures
3.3.2 Convention d'utilisation des registres . . . . . . . . . . . .
3.3.3 Gestion du contexte grâce au paramètre Ctx . . . . . . . .
3.3.4 Structure de données pour la table des méthodes . . . . .
3.3.5 Rôle de la procédure Optimise . . . . . . . . . . . . . . .
3.3.6 Gestion des tests et réservation de mémoire dans la pile . .
3.3.7 Sauvegarde des registres dans le code des méthodes . . . .
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
4
4
.
.
.
.
.
.
.
6
6
6
7
7
8
8
8
.
.
.
.
.
.
.
.
.
.
9
9
9
10
10
11
11
11
12
13
13
Introduction
Le compilateur Deca que nous proposons est composé de 3 grandes parties distinctes, correspondant aux 3 étapes de la compilation. L'étape A, ou Analyse Syntaxique, a pour rôle de lire
le chier source et de construire un arbre qui représente le programme. L'étape B, ou Analyse
Contextuelle, décore cet arbre en ajoutant des informations de contexte (typage, super-classe,
numéro de champ ou de méthode. . .). Enn, l'étape C utilise cet arbre décoré pour générer le
code assembleur correspondant.
Au cours des étapes A et B, on eectue également les vérications correspondant à leur niveau
d'analyse, et lèvent les erreurs correspondantes.
Chacune des 3 parties est composée de diérents modules que nous allons présenter.
1 Étape A : Analyse syntaxique
Cette section concerne les chiers se trouvant dans le répertoire Syntaxe/.
1.1
Description
Dans cette première partie de l'analyse, nous créons une représentation intermédiaire du programme d'entrée sous forme d'arbre abstrait non décoré, en suivant la grammaire abstraite de la
spécication.
Pour cela, nous avons utilisé les utilitaires Aex et Ayacc qui génèrent les analyseurs respectivement lexical et syntaxique en Ada, composés de plusieurs paquetages dont nous ne détaillerons
pas le fonctionnement.
Cette génération est eectuée à partir de 2 chiers :
• lexical.l : contient les expressions régulières permettant de reconnaître les diérents
lexèmes du langage, et une suite d'instructions permettant de stocker des informations
(numéro de ligne, valeur éventuelle) pour chaque mot reconnu.
• syntaxe.y : contient l'ensemble des règles de la grammaire syntaxique du langage Deca,
des informations sur l'associativité et la priorité des opérateurs, ainsi qu'une suite d'instructions permettant de construire le sous-arbre associé à chaque règle.
1.2
Architecture
L'architecture adoptée pour cette partie est la suivante :
2
Syntaxe
Packages
générés
par Ayacc
Lexical
Dictionnaire
Packages
générés
par Aflex
Attributs
Arbres
Erreurs_Lexicales
Fig.
1 Architecture de la partie Syntaxe
Description du rôle des paquetages qui apparaissent ci-dessus :
• Lexical : c'est l'analyseur lexical généré par Aex à partir de lexical.l. Il lit le chier
source, reconnaît les lexèmes, et construit les feuilles de l'arbre correspondantes grâce au
type abstrait Attribut décrit plus loin, en accrochant le numéro de ligne du mot reconnu
(voir 1.3.1).
• Syntaxe : c'est l'analyseur syntaxique généré par Ayacc à partir de syntaxe.y. Il lit les
lexèmes fournis par Lexical et construit l'arbre à l'aide des règles de la grammaire syntaxique, en utilisant aussi le type abstrait Attribut.
• Les carrés colorés contiennent les autres paquetages générés par les utilitaires. Ces paque-
tages servent à la gestion plus bas niveau de la lecture du chier (buers, etc.). Etant le
résultat de la génération automatique, ils n'entrent pas dans le cadre de cette documentation.
• Erreurs_Lexicales : contient les procédures d'achage de messages d'erreurs pour les
erreurs lexicales.
• Dictionnaire : paquetage générique utilisé ici par Lexical pour répertorier les lexèmes.
• Attributs : contient les primitives permettant de gérer les éléments de type abstrait
Attribut qu'utilise Ayacc. Un attribut correspond en fait à un noeud de l'arbre agrémenté.
Dans le cas des feuilles, une valeur éventuelle (entier, ottant, nom, ou chaîne) est ajoutée
par Lexical à sa création, ainsi que le numéro de ligne. Dans le cas des non-terminaux,
c'est Syntaxe qui ajoute le numéro de ligne (voir 1.3.1).
• Arbres : contient les primitives de construction de l'arbre.
3
1.3
Choix de conception
La plupart des paquetages de cette partie sont issus de la génération automatique par Aex
et Ayacc, c'est pourquoi il y a peu de décisions de conception ici.
1.3.1
Choix d'accrochage du numéro de ligne
Comme vu en 1.2, nous ajoutons à chaque noeud un attribut contenant un numéro de ligne
lors de la construction de l'arbre.
Le choix du numéro de ligne est évident pour tous les terminaux du lexique correspondant
aux feuilles de l'arbre abstrait : il s'agit du numéro de ligne du lexème dans le chier original1 .
C'est pour cela qu'il est aecté par Lexical.
Cependant, en ce qui concerne les non-terminaux de la grammaire abstraite, et donc les noeuds
qui ne sont pas des feuilles, nous avons du choisir le numéro de ligne de l'un des termes de la
partie droite de la règle correspondante (c'est-à-dire de l'un de ses ls). Ceci est donc eectué
dans Syntaxe.
Exemple :
Le choix du terminal pour récupérer le numéro de ligne pour le noeud aectation n'a pas d'inuence dans le cas de gauche, mais en a une dans le cas de droite :
1:
x=1;
1: x
2: =
3: 1
4: ;
Nous avons choisi ici de donner le numéro de ligne de l'opérateur = (ici ligne 2) au noeud de
l'aectation.
Le tableau qui suit récapitule les choix que nous avons fait pour l'ensemble des noeuds de
l'arbre abstrait. Lorsque le numéro de ligne est accroché à une liste, il s'agit en réalité du numéro
de ligne du premier arbre de cette liste (ou 0 si la liste est vide).
Noeud
non-terminal
Programme
Classe
Ident
Champ
program
classe
ident
champ
champ
Partie droite de la règle
Accrochage
ligne sur :
liste_classes principal
principal
Classe_Lex ident ext_classe
ident
Idf_Lex
Idf_Lex
type suite_decl_champ ' ;'
suite_decl_champ
Protected_Lex type
suite_decl_champ
suite_decl_champ ' ;'
Decl_Champ
decl_champ
ident initialisation
ident
Methode
methode
type ident '(' liste_param ')'
ident
liste_declarations bloc
Parametre
parametre
type ident
ident
Decl_Variable decl_variable
type suite_decl_var ' ;'
type
Decl_Var
decl_var
ident initialisation
ident
1 aucun
lexème ne contient de retour à la ligne, donc aucun ne se trouve sur 2 lignes à la fois
4
Noeud
non-terminal
Nop
Retour
Si
Tantque
Cond
Ecriture
Ecriture_Ligne
Aect
Ou
Et
Egal_Egal
Di
Inf
Sup
Inf_Egal
Sup_Egal
Instanceof
Plus
Moins
Mult
Div
Reste
Moins_Unaire
Non
Conversion
Entier
Flottant
Chaine
Vrai
Faux
Null
This
Lecture_Entier
Lecture_Flottant
Creation
Appel
Selection
Principal
inst
inst
inst
inst
suite_cond
inst
inst
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
exp
place
place
Partie droite de la règle
Accrochage
ligne sur :
' ;'
' ;'
Return_Lex exp ' ;'
Return_Lex
suite_cond sinon
suite_cond
While_Lex '(' exp ')' bloc
While_Lex
If_Lex '(' exp ')' bloc
If_Lex
Print_Lex '(' liste_exp ')' ' ;'
Print_Lex
Println_Lex '(' liste_exp ')' ' ;'
Println_Lex
place '=' exp
'='
exp Or_Lex exp
Or_Lex
exp And_Lex exp
And_Lex
exp Egal_Egal_Lex exp
Egal_Egal_Lex
exp Diff_Lex exp
Diff_Lex
exp '<' exp
'<'
exp '>' exp
'>'
exp Inf_Egal_Lex exp
Inf_Egal_Lex
exp Sup_Egal_Lex exp
Sup_Egal_Lex exp
exp Instanceof_Lex
Instanceof_Lex
exp '+' exp
'+'
exp '-' exp
'-'
exp '*' exp
'*'
exp '/' exp
'/'
exp '%' exp
'%'
'-' exp
'-'
' !' exp
' !'
Cast_Lex '<' type '>' '(' exp ')'
Cast_Lex
Entier_Lex
Entier_Lex
Flottant_Lex
Flottant_Lex
Chaine_Lex
Chaine_Lex
True_Lex
True_Lex
False_Lex
False_Lex
Null_Lex
Null_Lex
This_Lex
This_Lex
ReadInt_Lex '(' ')'
ReadInt_Lex
ReadFloat_Lex '(' ')'
ReadFloat_Lex
New_Lex ident '(' ')'
New_Lex
place '(' liste_exp ')'
place
exp '.' ident
'.'
liste_declarations bloc
liste_declarations
5
2 Étape B : Vérications contextuelles et Décoration
Cette section concerne les chiers situés dans le répertoire Verif/.
2.1
Description
Cette partie est chargée de décorer l'arbre abstrait avec des informations contextuelles. Elle
utilise pour cela la grammaire attribuée fournie dans la spécication. Au cours de cette phase, le
compilateur eectue aussi la vérication contextuelle et l'enrichissement de l'arbre.
Elle fonctionne en 3 passes :
1. Recensement des noms de classes, vérication de l'utilisation des extends
2. Recensement des noms de méthodes et de champs, vérication des types des champs
3. Vérication du corps des méthodes, vérication du programme principal
2.2
Architecture
L'architecture adoptée pour cette partie est la suivante :
Verif
Verif_Passe_1
Tests_Types
Verif_Passe_2
Verif_Commun
Verif_Passe_3
Decors
Erreurs_Contextuelles
Symboles
Utilitaires pour la vérification
Fig.
2 Architecture de la partie Verif
Description du rôle des paquetages qui apparaissent ci-dessus :
• Verif_Passe_[123] : contiennent chacun un certain nombre de procedures dont le nom
est de la forme Verif_NonTerminal , ainsi que quelques procédures annexe au besoin. Cha-
cune de ces procedures est chargée de traiter la ou les règles correspondant au non-terminal
donné par son nom (voir 2.3.1).
6
• Symboles : contient les primitives de gestion de la table des symboles. Il consiste principalement en 2 variables globales : Env_Types qui contient les types prédénis et les classes
déclarées, et Env_Exp_Object, qui contient la méthode equals de la classe par défaut
Object. L'initialisation contient le codage en dur des types prédénis et de la classe Object.
• Tests_Types : contient l'implémentation du type Operateur ainsi que diérentes fonc-
tions utilisées pour dénir la grammaire attribuée. C'est le cas des fonctions d'accès au
type de retour de certaines opérations, ainsi que des opérations sur les environnements
(Union_disjointe et Empilement).
• Verif_Commun : contient les procédures traitant les règles communes aux trois passes (pour
les non-terminaux ident et type ), ainsi que les exceptions et fonctions utilitaires communes
aux packages de vérication contextuelle.
• Erreurs_Contextuelles : permet de gérer les messages d'erreurs de manière centralisée,
en utilisant les numéros de passe, de règle, et d'erreur.
• Decors : contient les primitives de gestion des décors des arbres.
2.3
2.3.1
Choix de conception
Rôle des procédures Verif_NonTerminal
Les paquetages Verif_Passe_[123] sont essentiellement constitués de procédures appelées
Verif_NonTerminal , où NonTerminal désigne un non-terminal de la grammaire attribuée du
langage Deca fournie dans la spécication.
Elles comportent toutes un paramètre A qui est l'arbre correspondant au non-terminal à traiter (ou L s'il s'agit d'une liste2 ), ainsi que des paramètres in et out représentant les attributs
respectivement hérités et synthétisés.
Concrètement, chacune de ces procédures eectue les actions suivantes :
1. choix de la règle à utiliser à l'aide d'un case sur le type de noeud du paramètre (voir 2.3.2)
2. appel des procédures correspondant aux non-terminaux de la partie droite
3. aectations implicites et explicites
4. ltrages d'attributs et vérication des conditions
5. ajout d'un décor aux noeuds de l'arbre où c'est pertinent, ce qui permet de stocker les
informations de contexte pour la partie C
N.B. : L'ordre est parfois modié selon les cas
Si une erreur contextuelle est décelée au point 4, un appel à la procédure Message_Erreur
est eectué (voir 2.3.4) et l'exception Erreur_Verif_Locale est levée.
2 les
listes ne sont pas traitées de la même manière, veuillez vous référer au point 2.3.3 pour plus de précisions
7
2.3.2
Utilisation systématique du case
Dans les procédures vues en 2.3.1, l'utilisation de la structure case en Ada paraît superue
lorsqu'un seul cas est possible. C'est en réalité un choix qui s'est avéré intéressant pour plusieurs
raisons, il a permis en eet de :
1. programmer de manière défensive car l'appel à Acces_Noeud_XXX lève une exception en cas
d'incohérence interne
2. s'assurer que tous les types de noeuds soient bien traités3 pour les procédures où c'est
nécessaire
3. rester général et systématique pour toutes les autres procédures
4. synthétiser le code à l'inverse d'une structure conditionnelle en if
2.3.3
Traitement itératif des règles récursives
Nous souhaitons attirer l'attention du lecteur sur le traitement des règles récursives, qui sont
traduites sous forme de listes dans l'arbre abstrait. La traduction exacte de ces règles récursives
suggérait d'utiliser une procédure récursive également, an de rester systématique. Cependant,
cela aurait compliqué le code avec des accès à une sous-liste de la liste mère.
Nous avons donc choisi de traiter ces règles de manière itérative, à l'aide du parcours de liste
fourni par le package Listes. Il nous a donc fallu adapter les opérations sur les attributs pour
qu'elles conviennent à cette nouvelle forme.
2.3.4
Messages d'erreur
Pour plus de lisibilité du code, les messages d'erreur sont tous contenus dans le paquetage
Erreurs_Contextuelles. Ainsi, dans les paquetages Verif_Passe_[123], il sut d'appeler la
procedure Message_Erreur avec les bons numéros de passe, de règle et d'erreur sans se soucier
du message descriptif.
3 erreur
de compilation des sources Ada si un cas est oublié
8
3 Étape C : Génération de code
Cette section concerne les chiers situés dans le répertoire Gencode/.
3.1
Description
Cette partie implémente la dernière étape de la compilation, à savoir la génération de code.
C'est sur cette partie que la conception de l'architecture a pris le plus de temps.
Elle fonctionne en 2 passes :
1. Génération du code de construction de la table des méthodes
2. Génération du code des sous-programmes d'initialisation des classes
Génération du code des méthodes des classes
Génération du code du programme principal
Génération du code des messages d'erreur
3.2
Architecture
L'architecture adoptée pour cette partie est la suivante :
Gencode
Gencode_Tables_Methodes
Gencode_Classes
Table_Etiquettes
Gencode_Bloc
Passe 1
Passe 2
Gencode_Instructions
Gencode_Expressions
Gencode_Assembleur
Gencode_Commun
Pseudo_Code
Ensemble de primitives pour la génération de code
Fig.
3 Architecture de la partie Gencode
9
Description du rôle des paquetages qui apparaissent ci-dessus :
• Gencode_Commun : fournit les exceptions, ainsi que des macros pour récupérer plus simple-
ment les informations dans l'arbre abstrait.
• Gencode_Assembleur : contient les étiquettes d'erreur et la dénition du type Non_Scratch,
et fournit des macros et utilitaires pour tout ce qui se rapproche du code assembleur. Il
fournit en particulier la procedure Optimise (voir 3.3.5), les fonctions Prepare_TSTO et
Prepare_ADDSP (voir 3.3.6), ainsi que les procédures de gestion de Ctx, variable sur laquelle
nous reviendrons en 3.3.3.
• Gencode_Expressions : fournit les procédures de traitement des expressions arithmétiques
et booléennes, mais aussi de l'instruction new et de l'aectation (qui sont des expressions).
• Gencode_Instructions : fournit la procédure Gen_Inst qui génère le code de toute instruction, en prenant en paramètre un noeud de type Noeud_Arbre_INST.
• Table_Etiquettes : abstrait une structure de données qui permet de représenter la table
des étiquettes des méthodes de chaque classe. Il instancie pour cela le package générique
Tables.
• Gencode_Tables_Methodes : fournit une procédure de génération du code permettant de
créer la table des méthodes au début du programme. Il s'appuie principalement sur les
procédures du paquetage Table_Etiquettes (voir 3.3.4).
• Gencode_Bloc : fournit les procédures de génération de code pour une liste d'instructions
ou une liste de déclarations.
• Gencode_Classes : fournit une procédure de génération de code pour les classes. Pour
chaque classe, il génère le code d'initialisation de ses champs et le code de ses méthodes.
• Gencode : contient la procédure qui orchestre la génération du code, ainsi que la procédure
Gen_Principal qui génère le code du programme principal à l'aide des packages précédem-
ment cités.
3.3
3.3.1
Choix de conception
Considérations générales sur les paramètres des procédures
La génération de code s'eectue en parcourant l'arbre abstrait. Ainsi toutes les procédures de
génération de code prennent en paramètre un noeud de l'arbre (ou une liste).
Elles possèdent pour la plupart un autre paramètre appelé Ctx sur lequel nous reviendrons
en 3.3.3.
Les procédures de génération du code des expressions prennent en outre en paramètre un
registre Res (Non_Scratch) qui indique où stocker la valeur de l'expression. Cette valeur est de
nature diérente suivant que l'expression est arithmétique (valeur entière ou ottante), booléene
(0 ou 1) ou s'applique à des objets (adresse).
10
3.3.2
Convention d'utilisation des registres
Nous avons choisi pour plus de commodité d'utiliser les registres banalisés non scratch dans
l'ordre de leur numérotation. Ainsi, si l'on demande l'évaluation d'une expression dans R4, cela
signie que les registre R2 et R3 ne doivent pas être écrasés (à moins d'être sauvegardés) et que
les registres R4 à R15 sont libres4 , donc utilisables à souhait sans sauvegarde.
Il est également convenu que lors d'un appel à Gen_Inst, tous les registres non scratch (R2 à
R15) sont disponibles. Ils ont donc été sauvegardés au préalable s'ils sont utilisés.
Le type Non_Scratch déni dans Gencode_Assembleur permet de ltrer les registres utilisés
lors d'appel aux procédures de génération des expressions, an d'éviter l'utilisation malencontreuse d'un registre scratch qui pourrait s'avérer problématique5 .
3.3.3
Gestion du contexte grâce au paramètre Ctx
Le paramètre in out Ctx apparaît dans la plupart des procédures de cette section. Il est de
type Contexte déni dans Gencode_Assembleur comme un enregistrement comme suit :
type Contexte
Classe :
Methode :
Pile
:
PileMax :
Rmax
:
end record;
is record
access String;
access String;
Natural;
Natural;
Non_Scratch;
Les 2 premiers champs servent à savoir dans quelle classe et quelle méthode on se trouve
depuis n'importe quelle procédure (ceci est notamment nécessaire dans la procédure qui génère
le code de return pour le choix de l'étiquette de saut).
Les 3 champs suivants peuvent être mis à zéro6 par un appel à la procédure RAZ_Stats.
• Pile est incrémenté et décrémenté par les PUSH, POP, ADDSP, SUBSP, ce qui permet de
connaitre la taille courante de la pile7 .
• PileMax garde la valeur maximale atteinte par Ctx.Pile depuis la dernière mise à zéro.
(son utilisation est expliquée en 3.3.6)
• Rmax contient le registre non scratch maximum (en terme de numéro) utilisé depuis la
dernière mise à zéro. Ceci est notamment utilisé pour connaître les registres utilisés par une
méthode, et donc savoir lesquels sauvegarder (voir 3.3.7). C'est en eet possible grâce à la
convention vue en 3.3.2.
3.3.4
Structure de données pour la table des méthodes
Nous utilisons le package Table_Etiquettes comme un dictionnaire : nous rangeons dans
la table chaque classe avec sa table des méthodes. La structure de donnée associée est appelée
TableMethodes et est dénie comme suit :
type Tableau is array (Positive range <>) of Etiq;
4 en
réalité, les registres disponibles ne vont jusqu'à R15 que si l'option -r ne les limite pas
expression peut contenir un appel de méthode qui pourrait écraser les registres non scratch
6 pour Rmax, il s'agit de lui donner pour valeur le registre par défaut R_Defaut, qui est une constante xée à
R2 dans gencode_assembleur.ads
7 c'est une taille relative, dépendant de la dernière mise à zéro
5 une
11
type EtiqMethodes is access Tableau;
type TableMethodes is record
Adr_TM : Operande;
Super : Operande;
Tab
: EtiqMethodes;
end record;
Les deux champs suivants représentent la table telle qu'elle sera dans la pile :
• Super : adresse de la table des méthodes de la super-classe
• Tab : tableau des étiquettes des méthodes
Nous avons également ajouté un troisième champs pour plus de commodité dans le paquetage
utilisateur Gencode_Tables_Methodes :
• Adr_TM : adresse de la table des méthodes de la classe courante
Nous avons adopté cette structure de façon à pouvoir facilement changer l'opérande de la
dénition de chaque classe.
Nous avons par ailleurs masqué l'utilisation de la table grâce à un ensemble de mutateurs et
accesseurs dont les noms sont susamment explicites pour se passer d'explications.
3.3.5
Rôle de la procédure Optimise
La procédure Optimise permet d'abstraire les choix de registres dans le calcul des expressions.
En eet, il est bienvenu d'économiser des PUSH et POP qui ne sont pas obligatoires en utilisant un
maximum de registres disponibles, plutôt que de sauvegarder ceux que l'on utilise déjà.
Sa déclaration est la suivante :
generic
with procedure Modifier(R : in Non_Scratch; Ctx : in out Contexte);
procedure Optimise(Res : in Non_Scratch;
Res2 : out Registre;
Ctx : in out Contexte);
Cette procédure prend tout son sens lorsqu'il s'agit d'évaluer une expression à 2 opérandes,
c'est pourquoi la procédure Modifier est souvent un simple appel à Gen_Exp qui doit évaluer le
deuxième opérande.
Une utilisation usuelle de Optimise est donc de commencer par évaluer le premier opérande
dans Res et de laisser Optimise évaluer le deuxième dans un registre Res2 qui dépend de la
disponibilité du registre qui suit Res :
Si ce registre est autorisé, alors c'est lui qui est utilisé pour stocker le deuxième opérande.
Si Res était le dernier registre autorisé, alors Optimise le sauvegarde, l'utilise pour l'évaluation8 , puis stocke le deuxième opérande dans R0 avant de restaurer Res.
Le paramètre out Res2 contient nalement le registre où se trouve eectivement le résultat
de Modifier. On eectue alors l'opération souhaitée entre Res et Res2.
8 car
on ne peut pas utiliser un registre scratch pour l'évaluation d'une expression (voir 3.3.2)
12
3.3.6
Gestion des tests et réservation de mémoire dans la pile
Le problème des instructions TSTO et ADDSP est qu'on ne connaît pas leur opérande à priori.
En eet, la quantité à tester ou réserver dépend de ce qui suit. C'est pourquoi nous avons créé
des fonctions et procédures utilitaires pour préparer leur utilisation. Les fonctions suivantes
retournent l'Inst (de type TSTO ou ADDSP) qu'il faut conserver pour modication ultérieure :
function Prepare_ADDSP return Inst;
function Prepare_TSTO return Inst;
La procédure suivante permet plus tard de changer l'opérande en plaçant la valeur Val dans
l'instruction.
procedure Change_TSTO_ADDSP(I : in Inst; Val : in Integer);
On utilise souvent Ctx.PileMax (voir 3.3.3) pour connaître la valeur à placer comme argument
Val ici.
3.3.7
Sauvegarde des registres dans le code des méthodes
Comme indiqué dans la spécication, les registres non scratch utilisés par une méthode doivent
être sauvegardés au début de son exécution. Cependant, il se pose le même problème qu'en 3.3.6,
à savoir que l'on ne connaît pas les registres à sauvegarder lorsqu'on génère le début du code de
la méthode.
Pour pallier ce problème, nous avons créé une procédure Prepare_Sauvegarde_Regitres qui
retourne la ligne courante du code lors de son appel. La mémorisation de cette ligne permet
ensuite d'insérer les lignes de sauvegarde des registres une fois que l'on connaît les registres à
insérer.
Cette insertion est eectuée avec la procédure suivante de Gencode_Assembleur :
-- Insere la sauvegarde des registres de Non_Scratch'First a Ctx.Rmax
procedure Sauvegarde_Registres(L : in Ligne; Ctx : in out Contexte);
Pour savoir quels registres insérer, cette procédure se base sur la valeur de Ctx.Rmax (voir
3.3.3).
13
PROJET GENIE LOGICIEL
Documentation de Validation
GROUPE 8 - EQUIPE GL42
BION Jorey
DOUDECHE Amine
POLISANO Kévin
THIARD Florence
23 janvier 2012
1
Table des matières
1 Conception de tests
3
2 Conventions de nommage
4
3 Resultats de la couverture des tests avec Gcov
5
4 Tests syntaxiques
6
5 Tests lexicaux
7
2
1
Conception de tests
An de s'assurer que le code écrit implantait dèlement les fonctionnalités que l'on attendait
du compilateur, nous avons eectué de nombreux de tests tout en respectant les conventions de
nommage décrite dans les consignes de Validation du compilateur Deca. En eet, chaque
test est précédé d'un chire allant de 0 à 7, celui-ci correspond à l'itération dans laquelle nous
nous situions, l'écriture du code s'étant fait par étapes :
0 Programme vide
1 Hello World print et println de diverses expressions
2 Expressions simples
3 Boucle conditionnelle if et itérative while 4 Variables
5 Classe
6 Classe avec méthodes
7 Cast & Instanceof
Par ailleurs, l'écriture du test consistant à vérier la bonne implémentation de chaque itération s'est accompagnée de l'écriture de tests valides et invalides. En eet, un test a pour but de
prouver que le compilateur fonctionne correctement mais aussi d'exhiber le plus grand nombre
d'erreurs possibles, s'il y'en a, et de s'assurer que les exceptions étaient bien toutes relevées correctement (pour éviter des erreurs telles que Constraint_Error ou Access_Check_Failed
qui ne sont d'aucune utilité pour le client dont la connaissance de l'aspect technique du produit
est très partielle).
Nous avons, dans un premier temps, classé les règles de la grammaire utile pour chaque
itération. Dans un second temps, nous avons relevé toutes les erreurs susceptibles d'être levées
par l'écriture du code associée à l'itération courante. Ainsi, nous avons pu créé des tests invalides
et s'assurer que toutes les erreurs étaient levées et cela, de plusieurs manières diérentes, an
d'avoir une base de tests la plus riche possible. D'autre part, an de tester la pertinence de nos
tests, nous avons utilisé l'outil gcov et vérié que le programme de test passait par toutes les
règles de la grammaire nécessaire à l'itération courante.
A noter, qu'une couverture de 100% dans gcov est dicile à obtenir pour certains chiers
tels que verif_passe_3.adb car certaines lignes du code sont associées à des erreurs interne
et par conséquent, le programme n'est pas supposé passer par ces lignes lors de l'exécution des
tests. Nous y reviendrons plus tard.
De plus, nous avons tenté d'eectuer deux types de tests. D'une part des tests dit en boite
blanche qui est une technique de conception de test, en général fonctionnel, fondée sur l'analyse
de l'implémentation du code, d'autre part, des tests dits en boite noire qui est une technique
de conception de test, fonctionnel ou non, qui n'est pas fondée sur l'analyse de l'implémentation
du code mais sur une manière plus générale de coder les fonctionnalités testées.
3
2
Conventions de nommage
Concernant les tests de vérication contextuelle, nous avons nommé les chiers de tests
invalides de la façon suivante :
NuméroItération_NuméroException_TypeErreur.deca
//
//
//
//
//
Description
Trop
de
Resultat
parametres
pour
la
methode
translater
:
Erreur
Ligne
:
contextuelle
18
//
Ligne
18
//
Regle
3.97
//
Historique
//
Cree
le
:
Trop
de
parametres
pour
l ' appel
de
methode
(3.97
−2)
:
12/01/2012
c l a s s Point {
void t r a n s l a t e r ( int a ) {
}
}
int a ;
{
( new Point ( ) ) . t r a n s l a t e r ( a , a ) ;
}
Voici l'exemple d'un chier de test invalide. Nous prenons bien soin à relever le numéro de
la ligne ou le programme renvoie une erreur, le numéro de la règle associée à l'erreur, et enn le
message précis renvoyé par le programme lors de la vérication contextuelle. En eet, le script de
vérication des tests vérie que le message attendu et le message renvoyé par le programme en
cas d'erreur est bien identique, un espace supplémentaire pouvant aboutir à une erreur du script.
Enn, une description est fournie au cas où le message d'erreur n'est pas susamment explicite.
4
3
Resultats de la couverture des tests avec Gcov
Nom Fichier
Pourcentage de lignes ... Nb lignes dans le code
erreurs_contextuelles.adb
65.22%
92
gencode_expressions.adb
92.39%
289
gencode_assembleur.adb
96.69%
242
gencode_classes.adb
98.81%
168
gencode_instructions.adb
97.22%
72
gencode_tables_methodes.adb
97.01%
67
gencode.adb
92.11%
38
gencode_bloc.adb
100.00%
34
gencode_commun.adb
95.83%
24
listes.adb
100.00%
6
symboles.adb
98.18%
55
table_etiquettes.adb
81.48%
54
tables.adb
100.00%
9
tests_types.adb
97.46%
118
verif_passe_3.adb
95.50%
511
verif_passe_2.adb
92.35%
196
verif_passe_1.adb
100.00%
46
verif_commun.adb
88.00%
25
verif.adb
83.33%
12
Nous constatons que la couverture est globalement satisfaisante. Comme nous l'avions précisé précédemment, il est dicile d'obtenir une couverture parfaite (100%) pour tout les chiers.
En eet, certaines lignes de ces derniers correspondent à des erreurs internes, et sont donc inaccesibles au programme via les tests eectués.
5
4
Tests syntaxiques
Nom Fichier
Initialisation_erronee.deca
Nature de l'erreur
int x = .3
Omission_Type.deca
"x = 3 ;"
Separateur_variables.deca
Point_Virgule.deca
"int x y"
"int x"
Accolade_fermante.deca
Accolades_manquantes_if.deca
"class A { ..."
"if (true) exp"
Aectation_Egalite.deca
"int x = .3"
Description
Erreur sur le Non-Terminal EXP :
après = on attend une exp
qui ne peut commencer par "."
on ne trouve pas deux idf
de suite (cf. règle decl_var)
Séparateur de variables inexistant
Point virgule manquant
(cf. règle decl_variable)
Accolade_fermante manquante (cf. 3.7)
Invalide car il faut un bloc
(cf. regle suite_cond)
Après "=" on attend une exp,
qui ne peut commencer par "."
Commentaires_ Multilignes.deca "/* commentaires */"
Else_seul.deca
Methode_sans_corps.deca
Pb_parenthesage.deca
Puissance_indenie.deca
Return_seul.deca
Separateur_parametres.deca
Accolade_ouvrante.deca
Accolades_Inversees.deca
Bloc_Avec_
Instruction_Fausse.deca
Boucle_avec_condition_
non_parenthesee.deca
Boucle_while_condition_
manque_parenthese.deca
cast_sans_crochets.deca
Declaration_dans_
un_bloc.deca
Elsif_sans_condition.deca
Exp_non_reconnue.deca
Exp_non_reconnue1.deca
Exp_non_reconnue2.deca
Exp_non_reconnue3.deca
aucune instruction
ne commence par "/"
"else { }"
Une instruction ne peut commencer
par else (cf. inst)
"methode( ) ;"
On attend un bloc (cf. 3.19)
"((1+2)*(3+4)"
Parenthésage incorrect (cf. 3.60)
"n**2"
N'est pas une expression correcte (cf. 3.52)
"return ;"
Impossible, ; il faut renvoyer une exp et ;
n'en est pas une (cf. règles inst et exp)
"Ma_methode(x ; y)"
Les séparateurs de paramètre
doivent être des virgules
(cf. règle suite_param)
"class A { ..."
accolade_fermante manquante (cf 3.6)
"class A } ..."
accolade_fermante manquante (cf 3.6)
"{ - }"
Instruction incorrecte
"while cond ..."
"while (cond ..."
" y = cast(oat) x ; "
"{ int x=1 ; }"
"} elsif {"
"A => B"
"A <> B"
"A & B"
"A | B"
6
Condition pour la boucle
while sans parentheses
Condition pour la boucle
while sans parentheses
Omission de crochets <>
pour l'operateur cast
Declaration à l'interieur d'un
bloc d'instruction ( cf regle 3.19 )
Elsif sans condition
Expression non reconnue
Expression non reconnue (cf 3.76)
Expression non reconnue (cf 3.81)
Expression non reconnue (cf 3.82)
Expression_Conditionelle_ "{ if Booleen }"
Expression conditionelle sans parenthèses
Sans_Parentheses.deca
Instructions_en_dehors
"x=1 ; {}"
Instructions en dehors d'un bloc b (cf 3.31)
_bloc.deca
Listes_declarations_
"{ int x = 1 ; }"
Liste de declarations à l'interieur d'un
dans_un_bloc.deca
bloc, contraire à la regle 3.5.
Omission_separateur_
"print(x y) ; "
Omission séparateur dans l'achage de
print_variables.deca
plusieurs variables
Omission_separateur_
"println(x y) ; "
Omission séparateur dans l'achage de
println_variables.deca
plusieurs variables
Print_sans_
" print x ; "
Omission parentheses pour l'achage
parentheses.deca
Println_sans_
" printn x ; "
Omission parentheses pour l'achage
parentheses.deca
readFloat_sans
" x = readFloat ; " Omission de parentheses pour l'operateur readFloat
_parentheses.deca
readInt_sans
" x = readInt ; "
Omission de parentheses pour l'operateur readInt
_parentheses.deca
5
Tests lexicaux
On rappele qu'un identicateur doit impérativement commencer par une lettre, $ ou _
d'après la dénition de IDF :
IDF = (LETTRE +'$' + '_' )(LETTRE + CHIFFRE + '$' + '_')*.
Nom Fichier
Nature de l'erreur
Description
Idf_non_reconnu_1.deca
""
idf non reconnu,
Idf_non_reconnu_2.deca
"@"
idf non reconnu
Idf_non_reconnu_3.deca
"["
idf non reconnu
Idf_non_reconnu_4.deca
"]"
idf non reconnu
Idf_non_reconnu_5.deca
" &"
idf non reconnu
Idf_non_reconnu_6.deca
"|"
idf non reconnu
Idf_non_reconnu_7.deca
"34"
idf non reconnu
Idf_non_reconnu_8.deca
"à"
idf non reconnu
Idf_non_reconnu_9.deca
"é"
idf non reconnu
Idf_non_reconnu_10.deca
"ù"
idf non reconnu
Idf_non_reconnu_11.deca
"Ÿ"
idf non reconnu
Idf_non_reconnu_12.deca
"ç"
idf non reconnu
Idf_non_reconnu_13.deca
"ν "
idf non reconnu
Idf_non_reconnu_14.deca
""
idf non reconnu
Idf_non_reconnu_15.deca
"?"
idf non reconnu
Declaration_type_ada.deca
" :"
n'est pas un token
Trop_Caracteres.deca
Plus de 1024 caractères
7
Erreurs
gl42
23 janvier 2012
1
Erreurs contextuelles
Règle
Type
0.1/0.2 Filtrage d'attribut synthétisé
0.3
1.4-1
1.4-2
1.4-3
2.4-1
2.4-2
2.9
2.12
2.13
2.14
2.16-1
2.16-2
2.16-3
Opération partielle
Condition
Opération partielle
Opération partielle
Condition
Operation partielle
Operation partielle
Condition
Condition
Opération partielle
Opération partielle
Condition
Condition
2.17-1
2.17-2
2.17-3
Opération partielle
Condition
Condition
2.17-4
Condition
2.17-5
Condition
2.22
3.6-1
3.6-2
3.23
3.27
3.30
3.34
Condition
Opération partielle
Condition
Opération partielle
Condition
Opération partielle
Condition
Message
Erreur interne (il n'y a que des idf
de type ou de classe dans Env_Types)
Identicateur non déni
Identicateur de classe attendu pour [super]
Redénition de l'identicateur de classe [nom]
Identicateur de classe non déni [super]
Erreur interne (dejà testée en 1.4)
Erreur interne (dejà testée en 1.4)
Champ ou méthode [nom] déjà déclaré
Déclaration de champs de type `void'
Déclaration de champs de type `void'
Champ [nom] déjà déclaré
Erreur interne (dejà testée en 1.4)
Erreur interne (dejà testée en 1.4)
Masquage de la méthode héritée [nom]
par une déclaration de champ
Erreur interne (dejà testée en 1.4)
Erreur interne (dejà testée en 1.4)
Masquage du champ hérité [nom]
par une déclaration de méthode
Signature incompatible pour la
Redénition de la méthode [nom]
Type de retour incompatible pour la
redénition de la méthode [nom]
Paramètre formel [nom] de type `void' (interdit)
Erreur interne (dejà testée en 1.4)
Erreur interne (dejà testée en 1.4)
Pusieurs paramètre formels de même nom [nom]
Déclaration de variable(s) de type `void' interdite
Variable [nom] déjà déclarée
Instruction `return' incompatible
avec le type de retour `void'
1
3.44
Condition
3.45
3.50
Filtrage d'attribut synthétisé
Condition
3.52
Opération partielle
3.53
Opération partielle
3.54
Condition
3.57
Filtrage d'attribut synthétisé
3.58
Opération partielle
3.59
Condition
3.64..84 Filtrage d'attribut synthétisé
3.86/87-1 Filtrage d'attribut synthétisé
3.86/87-2
3.86/87-3
3.87-4
3.87-5
Opération partielle
Condition
Condition
Condition
3.87-6
Condition
3.88..90
Filtrage d'attribut synthétisé
3.91
3.93
Filtrage d'attribut synthétisé
Filtrage d'attribut synthétisé
(ou condition)
3.94
Filtrage d'attribut synthétisé
3.95
Filtrage d'attribut hérité
3.97/98-1
Filtrage d'attribut hérité
3.97/98-2
Filtrage d'attribut hérité
Types [type1] et [type2] incompatibles
pour l'aectation
Condition non booléenne
print/println : type [type] incompatible
(type `int', `oat', ou `String' attendu)
Opérateur [op] indéni
pour les types [type1] et [type2]
Opérateur [op] indéni pour le type [type]
Utilisation de this en dehors d'une classe
new : identicateur de classe attendu pour [nom]
instanceof : types [type1] et [type2] incompatibles
cast : types [type1] et [type2] incompatibles
Erreur Interne
Objet attendu en partie gauche
de sélection (avant `.')
Erreur Interne (déjà testée car 3.86/87-1 est passée)
Erreur Interne (déjà testée par 3.86/87-1)
Champ protégé non visible dans le programme principal
Champ protégé non visible dans [classe1]
qui n'est pas un sous-type de [classe2]
Champ protégé non visible dans [classe1]
qui n'est pas un sous-type de [classe2]
Identicateur de champ, de paramètre
ou de variable attendu
Identicateur de champ attendu
Objet attendu en partie gauche d'appel
de méthode (avant `.')
Identicateur de méthode attendu
Cette méthode attend des paramètres
Paramètre(s) manquant(s) pour l'appel de méthode
Trop de paramètres pour l'appel de méthode
2
Charte de travail en équipe : Projet Génie Logiciel
BION Joffrey, DOUDECHE Amine, POLISANO Kevin, THIARD Florence, Ensimag, Equipe 42
1. Les compétences de l’équipe, ses points forts et ses points faibles
Synthèse des rendus personnels
Nous avons effectué une synthèse des rendus personnels après quelques jours de travail sur le
projet génie logiciel. Globalement, le leadership est contrasté mais est plus élevé pour Joffrey, ce qui
nous a mené à le nommer chef de projet, nous y reviendrons plus tard.
Concernant la planification et l'organisation, Amine s'assure que le planning est à jour et qu'il est
respecté par l'ensemble de l'équipe. L'esprit d'équipe est quant à lui omniprésent et laisse présager un
bon déroulement du projet ainsi qu'une bonne ambiance de travail, mais peut également compenser les
faiblesses individuelles dans d'autres domaines.
Enfin, aucun membre n'a de problème particuliers de communication orale comme écrite, ce qui
facilitera le déroulement de la soutenance et des suivis.
Matrice SWOT : Points forts et points faibles en tant qu’équipe
FORCES
Très bonne communication au sein du groupe
Niveau de compétence technique globalement
élevé
OPPORTUNITES
Avoir un lieu où se réunir en dehors de
l'Ensimag, accessible et convivial
FAIBLESSES
Ponctualité
MENACES
Le retard d’un membre de l’équipe entrainant
un retard général pour le rendu (travail en
parallèle)
Les surcharges de travail
2. Les valeurs communes
Chaque membre, ayant des taches de difficultés variables à réaliser, et étant conscient des dates
limites de rendu, doit bien évidemment donner son maximum afin de réaliser ces dernières et ne pas
hésiter à solliciter de l’aide de la part de ses camarades afin d’éviter tout retard sur le projet dont le
temps de réalisation est relativement court.
Conformément au planning joint en annexe, chacun a un certain nombre de tâches à effectuer
en un temps souhaité. A partir du moment ou le travail est complet à la date butoir, des comportements
tels que des retards ou des absences ponctuels à certaines réunions d’équipe sont acceptés. En effet,
chacun est conscient des responsabilités qui lui incombent ainsi que de la difficulté dans laquelle il peut
mettre son groupe en cas de laisser allers répétés et injustifiés.
Des réunions d’équipe sont mises en place régulièrement afin de faire un bilan de l’état
d’avancement de chacun, faire part des initiatives prises et des difficultés rencontrées. Lesdites
initiatives, prises seul, sont les bienvenues tant qu'elles n'entraînent pas de changement irréversible ni de
modification majeure. Dans le cas inverse, une consultation d'au moins 2 autres membres est souhaitée.
Somme toute, il est souhaitable que chaque membre soit présent lors de ces réunions, sauf problème
personnel (rendez-vous, maladie …). Chacun doit essayer d’apporter un regard constructif et critique
sur le travail des autres tout en les respectant.
3. Rôles et responsabilités dans l’équipe
La répartition des taches s’est effectuée en fonction des préférences des uns et des autres, mais
également en fonction des compétences techniques de chacun (Assembleur, Ada…).
Étant le seul membre du groupe à avoir suivi l'enseignement Architecture 2 au deuxième
semestre en première année, Joffrey a une bonne connaissance du langage d'assemblage, ce qui facilite.
Ayant en outre une vision assez globale du projet, nous l'avons désigné en tant que « chef de projet ».
En effet, ses connaissances lui ont facilité la compréhension du sujet ainsi que les attentes vis-à-vis du
projet. Il devra s’assurer que chaque membre, éventuellement aidé par ses camarades, accomplisse les
tâches qui lui auront été attribuées en respectant les délais du planning. Il devra également s'assurer de
la cohérence des différents modules.
Cependant, les membres du groupe ne devront pas voir le chef de projet comme une personne
exigeante et inflexible mais au contraire comme une personne à l’écoute et soucieuse du bon
avancement de l’équipe et à qui ils n’hésiteront pas à lui faire part de leur problèmes.
Mis à part ce rôle qui demeurera fixe, les autres rôles (conception, développement, tests...)
pourront varier selon les tâches à effectuer et les envies afin que chacun puisse apprendre quelque
chose du projet.
4. La communication au sein de l’équipe
De manière générale, chaque étape qui précède l'écriture de code sera effectuée au minimum par
binôme, ceci afin de confronter nos points de vue de façon constructive et d'avancer efficacement dans
le projet.
Nous nous séparerons ensuite les taches pour le code brut dans le but de paralléliser l'écriture
des procédures pour optimiser notre rendement. Les différentes difficultés rencontrées seront traitées
en équipe afin que chacun d'entre nous maîtrise au mieux la totalité du projet.
Toutefois cette organisation doit rester souple et adaptative : si l'un des membres a terminé sa
tâche en avance, ou si à l'inverse une tâche s'avère plus complexe ou longue que prévue, une
redistribution des efforts est possible et encouragée.
Par ailleurs, concernant la communication dans l'équipe, chaque membre doit faire preuve d'un
très bon état d'esprit en étant à l'écoute de chacun de ses camarades. En effet, cela doit être facilité par
une confiance globale au sein de notre groupe qui nous permettra partager l'ensemble des problèmes
rencontrés afin de faciliter leurs résolutions. Des réunions d’équipe auront lieu régulièrement et devront
contribuer à cette bonne communication (celles-ci se tiendront la plupart du temps à l'Ensimag ou chez
Joffrey qui a un appartement de taille adéquate pour nous accueillir, en particulier pour Kévin,
l’appartement se situant au rez-de-chaussée facilitant ainsi l’accès aux personnes à mobilité réduite).
Les membres de l'équipe essayeront au maximum de travailler dans un même lieu durant la
journée, afin de faciliter davantage la communication ; de plus ils s'engagent à être joignable (dans les
limites du raisonnable)
Le fait de se retrouver en dehors de l'Ensimag nous permettra de développer une meilleure
cohésion des membres de l'équipe et de mieux nous connaître, mais également de se réunir et d’aborder
des sujets autres que le projet GL. Ainsi, au choix, un goûter ou un repas devra être organisé chaque
semaine.
Trois des membres de l'équipe (Amine, Kévin, Joffrey) avaient déjà travaillé ensemble pour le
projet C en première année. Les méthodes de travail sont sensiblement similaires à celle de l'année
précédente. La présence d'une quatrième personne n'a pas été un problème, au contraire. Cela a souvent
apporté un point de vue pertinent concernant de nombreuses problématiques afin d'étayer notre
réflexion pour apporter des solutions simples et partagées.
2EQI
%REP]WI PI\MGSW]RXE\MUYI
0I\IYV
4EVWIYV
'SYVW 8(
6¬HEGXMSR HI PE GLEVXI HI XVEZEMP
%GXMZMX¬W 4IVWSRRIPPIW 7OM
'SQTMPEXIYV K¬VERX ,IPPS ;SVPH
0IGXYVI (SGYQIRXEXMSR
8IWXW
%REP]WI GSRXI\XYIPPI
+IWXMSR HI PE XEFPI HIW W]QFSPIW
-QTPIQIRXEXMSR HI PE +VEQQ %XXVMFY¬I
+¬R¬VEXMSR HI GSHI
%REP]WI HIW IVVIYVW GSRXI\XYIPPIW
-QTP¬QIRXEXMSR HIW IVVIYVW GSRXI\XYIPPIW
'SQTMPEXIYV WERW SFNIX
)\TVIWWMSRW 7MQTPI
)XETI &
)XETI '
)GVMXYVI HIW TVSKVEQQI HI XIWXW
8IWXW
:EVMEFPIW
)XETI &
)XETI '
)GVMXYVI HIW TVSKVEQQI HI XIWXW
8IWXW (IFSKEKI
7XVYGXYVI GSRHMXMSRRIPPI -J
)XETI &
)XETI '
)GVMXYVI 8IWXW 4EVXMI &
)GVMXYVI 8IWXW 4EVXMI '
8IWXW (IFSKEKI
&SYGPI ;LMPI
)XETI &
)XETI '
)GVMXYVI 8IWXW 4EVXMI &
)GVMXYVI 8IWXW 4EVXMI '
8IWXW (IFSKEKI
'SQTMPEXIYV TSYV PE XSXEPMX¬ HY PERKEKI
6IZYI H EVGLMXIGXYVI 4EVXMI '
3FNIXW 7ERW Q¬XLSHIW
)XETI &
)XETI & (¬GSVEXMSR
)XETI '
)GVMXYVI 8IWXW 4EVXMI &
)GVMXYVI 8IWXW 4EVXMI '
8IWXW (IFSKEKI
3FNIXW EZIG Q¬XLSHIW
)XETI &
)XETI & (IGSVEXMSR
)XETI '
)GVMXYVI HIW 8IWXW 4EVXMI &
)GVMXYVI HIW 8IWXW 4EVXMI '
8IWXW (IFSKEKI
'EWX -RWXERGI SJ
)XETI &
)XETI '
)GVMXYVI HIW 8IWXW 4EVXMI &
)GVMXYVI HIW 8IWXW 4EVXMI '
8IWXW (IFSKEKI
(SGYQIRXEXMSR 9XMPMWEXIYV
(SGYQIRXEXMSR 'SRGITXMSR
(SGYQIRXEXMSR :EPMHEXMSR
6IRHY ZIVWMSR MRXIVQ¬HMEMVI
6IRHY *MREP
6IRHY HSGYQIRXEXMSR
6IRHY &MPER
7SYXIRERGI
;SVO
;IIO
;IIO
;IIO
;IIO
1
H
H
H
H
H
H
H
L
H L
H
L
H L
H
H L
H L
H
H
H L
H L
H L
H L
L
H
L
H
H L
H L
H L
H
L
H L
H
H L
L
H L
H L
H L
H L
H L
L
H L
H L
H L
H L
H L
H
H L
H L
H
H L
H L
H L
H L
L
H L
H L
H
L
H
L
L
L
*PSVIRGI %QMRI
.SJJVI] /IZMR
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] ? A %QMRI ? A
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] /IZMR *PSVIRGI %QMRI
/IZMR
.SJJVI]
.SJJVI] /IZMR %QMRI
*PSVIRGI
/IZMR *PSVIRGI
/IZMR
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] %QMRI
*PSVIRGI
/IZMR
/IZMR *PSVIRGI
.SJJVI] %QMRI
/IZMR
*PSVIRGI
/IZMR *PSVIRGI
.SJJVI] %QMRI
/IZMR *PSVIRGI
*PSVIRGI
/IZMR
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] %QMRI
/IZMR *PSVIRGI
*PSVIRGI
/IZMR %QMRI
.SJJVI] /IZMR *PSVIRGI
.SJJVI] *PSVIRGI
.SJJVI] %QMRI
.SJJVI]
/IZMR *PSVIRGI
.SJJVI] %QMRI
*PSVIRGI
.SJJVI] /IZMR *PSVIRGI
%QMRI
.SJJVI]
.SJJVI] /IZMR
%QMRI
*PSVIRGI
.SJJVI] /IZMR *PSVIRGI %QMRI
L
H
H L
H
L
.SJJVI] /IZMR *PSVIRGI
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] /IZMR *PSVIRGI %QMRI
PROJET GENIE LOGICIEL
Bilan
GROUPE 8 - EQUIPE GL42
BION Jorey
DOUDECHE Amine
POLISANO Kévin
THIARD Florence
27 janvier 2012
Introduction
Durant les trois semaines qui constituent le projet GL, nous avons pu mettre à l'épreuve
l'organisation et les méthodes de travail que nous nous étions xées au départ. Il s'agit ici de
faire le point sur notre évolution et les enseignements que nous en avons tiré. Dans un premier
temps, nous aborderons le sujet sous l'angle du travail d'équipe et de la coopération. Nous nous
attarderons ensuite sur les objectifs réalisés (ou non) et l'organisation du projet lui même. Enn,
nous développerons les apports et les conclusions que nous tirons de notre expérience pour notre
vie professionnelle et à plus court terme pour nos successeurs.
1 L'équipe
1.1 Ses forces, ses faiblesses, ses hauts et ses bas...
... Plus de hauts que de bas. En eet, le principal point fort de l'équipe relevé dans la charte au
début du projet - à savoir sa bonne ambiance et sa bonne communication - n'a pas été démenti.
Ni sang, ni larme, ni hurlement à rapporter, donc. Globalement, le niveau de stress de l'équipe est
demeuré relativement bas... peut-être trop ? En eet, si cela nous a permis d'aborder sereinement
les dicultés, cela a aussi pu parfois nous faire perdre le sens de l'urgence et des priorités.
Dans la même tonalité, nous avons abordé la gestion des risques avec un peu trop d'optimisme,
en négligeant les risques non inhérents à notre équipe mais pourtant bien réels, typiquement les
faiblesses de l'infrastructure technique mise à notre disposition (serveur ensibm). Si ces problèmes
n'avaient pas été rapidement résolus, notre avancement en aurait réellement souert, faute d'anticipation.
Quant à la ponctualité que nous avions identiée comme une faiblesse dans la charte, elle s'est
en n de compte révelée peu gênante. Les retards du matin pour certains ont été compensés par
un travail plus tardif, selon les rythmes de chacun.
Toutefois, durant l'intersection (non vide !) de ces diérentes plages horaires, la plus grande
partie du travail a été comme nous l'avions prévu réalisée dans un lieu commun, ce qui nous
a eectivement permis d'être très réactifs lors du développement : par exemple, un bug mis en
évidence par un membre de l'équipe chargé des tests et de la validation pouvait être aussitôt
rapporté au développeur, qui pouvait à son tour solliciter l'aide des autres membres de l'équipe
an de le corriger. De même, les doutes concernant les choix de conception précédents, leurs
faiblesses et leurs limitations par rapport aux nouvelles fonctionnalités exigées par l'itération
courante pouvaient être immédiatement partagés (en particulier avec le chef de projet), étudiés
et résolus. Une question technique, par exemple sur le détail d'un des langages utilisés, pouvait
également trouver de cette manière une réponse immédiate en protant des compétences diverses
des membres de l'équipe.
1
1.2 L'organisation
Les choix principaux de notre organisation étaient initialement de désigner un chef de projet,
xe, et de faire tourner les autres rôles au sein de l'équipe.
Concernant le deuxième point, si nous avons eectivement permuté au cours du temps les
tâches type développement et type validation (à première vue moins passionnantes1 ), il y a eu
peu d'échange entre les parties B et C du projet. Parvenu à un certain niveau de complexité,
il aurait été contre-productif de devoir se réapproprier l'architecture, l'esprit et l'ensemble des
reexions menées sur une partie sans y avoir participé depuis le début.
L'organisation s'est donc nalement stabilisée, en fonction des compétences techniques et préférences de chacun, autour de deux binômes développeur/testeur (Jorey/Amine pour la partie
B, Kevin/Florence pour la partie C), les rôles alternant éventuellement au sein du binôme. Les
deux membres conservaient un point de vue technique sur la partie concernée, ainsi les questions
délicates de conception et d'algorithmique ont été résolues conjointement.
Cette organisation segmentée nous a permis de mettre en évidence l'importance du chef de
projet. En eet, de nous quatre, Jorey a pu et su garder une vision globale du projet et en
particulier de l'architecture et du travail restant à réaliser, matérialisée par des notes synthétiques
envoyées à l'ensemble de l'équipe. Bien qu'il n'existe pas de relation hiérarchique entre nous, et que
la plupart des décisions aient été prises collectivement, nous avons jugé important qu'un membre
de l'équipe puisse trancher et prendre une décision en cas de divergence, an que l'ensemble de
l'équipe puisse avancer - notamment les décisions concernant les choix de conception à adopter.
Jorey a parfaitement rempli ce rôle.
1 Bien
que débusquer le test qui mettra au jour le bug dissimulé dans le code de ses co-équipiers ne soit pas
dénué d'intérêt et demande un certain sens du dé...
2
2 Organisation et déroulement du projet
2.1 Planication
Le planning prévisionnel établi en début de projet a été amené à évoluer de façon signicative.
En eet, à mesure que notre connaissance du sujet gagnait en profondeur, nous nous sommes fait
une idée plus claire des diérentes tâches et surtout du temps nécessaire à leur réalisation, et avons
eectué divers changements de conception : il était donc logique de modier les durées allouées aux
tâches inachevées et à venir. An de conserver cette souplesse du planning, des bilans informels
étaient eectués à la n de chaque journée pour évaluer l'avancement de chacun et les tâches à
attribuer. Nous avons de plus proté du rythme suggéré par les suivis et le rendu intermédiaire
pour eectuer des bilans formels2 et plus en profondeur, au cours desquels le planning prévisionnel
était systématiquement réévalué.
2.2 Historique et déroulement
Après l'étape A, réalisée au début du projet, nous avons choisi de paralléliser la conception
et le développement des étapes B et C, selon des itérations de complexité croissante :
"Hello World"
Mini-Deca (sans objet)
Expressions simples
Variables
Structure de contrôle "If"
Structure de contrôle "While"
Deca (avec objet)
Objet sans méthodes
Objet avec méthodes
Nous avions tablé au départ sur un parallélisme complet des étapes B et C, mais cela n'a pas
toujours été le cas dans les faits, l'étape B progressant plus rapidement et "débordant" régulièrement de l'itération. La souplesse du planning nous a permis de nous adapter à cette progression
asymétrique, en reportant les ressources ainsi libérées sur les tâches de documentations, et, en n
de projet, sur la partie C, particulièrement exigeante sur les deux dernières itérations.
Le fonctionnement par itérations est une des caractéristiques des méthodes de développement
"agile", et nous nous sommes rendu compte que nous avions de façon naturelle adopté certaines
de ces méthodes. En particulier, nous avons consacré relativement peu de temps à la conception
des partie B et C au début du projet : lorsque l'objectif est de compiler un simple "Hello world", il
est dicile d'imaginer et d'élaborer ecacement l'architecture adéquate pour compiler l'ensemble
du langage. En revanche, à mesure de l'évolution des besoins à chaque itération, l'architecture
à été anée et le code remanié an de le rendre plus léger, général et évolutif : la partie C en
particulier a ainsi fait l'objet de nombreux "refactorings". Ne pas hésiter à revenir sur ce qui était
déjà établi constituait une prise de risques certaine - surtout à l'approche des dates de rendu mais nous a en n de compte permis de soumettre un produit plus lisible.
2 toutes
proportions gardées
3
En revanche, ce fonctionnement itératif a été moins respecté pour l'écriture des tests et la
validation. De fait, accaparés par la compréhension du sujet, nous avons commencé tardivement
l'écriture des tests - avec des conséquences néfastes, notamment :
La négligence des tests de l'analyse lexicale en début de projet, d'où la détection après le
rendu d'un bug pourtant simple à corriger, et que des tests adéquat aurait pu et du mettre
en évidence bien plus tôt.
Un manque de synchronisation entre le développement et la validation, de sorte que certains
bugs du compilateur Mini-Deca n'ont été détectés qu'après le rendu intermédiaire.
2.3 Objectifs xés et réalisés
Dès l'amorce de ce projet, notre équipe s'était xé deux objectifs :
fournir un compilateur Deca fonctionnel pour la date de rendu (23 janvier 2012 à 16h)
éventuellement aborder la réalisation, même partielle, de l'extension du produit initial,
"Maxi-Deca"
Le premier objectif a été accompli avec succès. En revanche, le second n'a pu être réalisé. En
eet, le bon avancement du projet à son commencement nous avait laissé présager une bonne
marge concernant le rendu du produit nal. Cependant, à 72h de la date butoir, nous avons du
faire un choix entre :
réaliser le second objectif et limiter l'exhaustivité des tests concernant le produit initial
se focaliser sur le produit initial, et optimiser sa conception en essayant de factoriser le code
au maximum et faciliter sa lisibilité
Dans un souci de satisfaction du client, nous avons préféré nous concentrer sur l'optimisation
et la validation du produit initial. An d'améliorer la lisibilité du code et le rendre plus évolutif,
la conception de l'architecture du produit a été revue une dernière fois, nous avions donc besoin
du temps restant pour garantir que ce remaniement n'aurait pas d'impact sur la fonctionnalité
du produit. Cette stratégie a d'ailleurs porté ses fruits puisqu'en multipliant le nombre de tests,
nous avons pu détecter certaines erreurs qui nous avaient échappé jusque-là.
4
3 Apport du projet
3.1 Apports personnels
Le projet nous a permis de conrmer ou d'inrmer les caractéristiques personelles que nous
avions décrites individuellement dans la che d'auto évaluation.
3.1.1 Jorey
Si je devais résumer ce que ce projet m'a apporté concernant l'organisation, je dirais qu'étant
chef de projet, j'ai appris qu'il fallait vraiment savoir gérer les priorités. Je suis personnellement
très maniaque en terme de propreté du code, commentaires et choix des noms de variables, ce
qui porte parfois préjudice à mon ecacité, surtout quand il s'agit de compléter du code écrit
par quelqu'un d'autre (que je me sens alors obligé de remanier). Il faut donc que j'apprenne à
déterminer correctement si j'ai le temps de m'occuper de ces détails qui sont, bien qu'importants,
malgré tout secondaires. En terme de gestion de projet, il m'est apparu plus ecace aussi de
prendre le temps de rééchir de manière précise aux tâches de chacun, plutôt que de dénir des
rôles approximatifs qui au nal poussent à se reposer des questions et nuisent à la productivité.
Cependant, il est dicile de savoir à l'avance quelles choses précises chacun pourra faire, et
combien de temps cela prendra avant d'avoir une nouvelle tâche à assigner.
3.1.2 Kevin
J'ai pour habitude lors de projets d'approfondir certains points qui m'intéressent. Cela me
prend beaucoup de temps, mais je prends d'ordinaire sur mon temps libre pour assouvir ma
curiosité . Lors du projet GL j'ai du déroger à ma règle pour me focaliser sur les choses essentielles
sans m'égarer sur des questions qui sortent du cadre de ce projet, car nous étions tenu par le
planning et que le temps était un élément clef a prendre en compte. Cela m'a donc forcé à
travailler avec des contraintes de temps d'une part et d'autre part à beaucoup échanger avec mes
co-équipiers avant d'entreprendre quelque-chose de façon à obtenir au nal un tout cohérent et
uniforme, alors que je suis bien souvent autonome dans mon travail. Le projet GL fut pour ma
part une bonne expérience de coopération et enrichissante également au niveau informatique.
3.1.3 Amine
Quelques jours après avoir commencé le projet GL, nous avons rempli une che d'auto évaluation sur nos caractéristiques personnelles et techniques. L'un de mes traits de caractère qui
me semblait le moins pertinent pour le projet était le leadership. En général, c'est un aspect que
j'aime mettre en avant à travers les diérentes disciplines (sport, associations . . . ). Cependant,
au vu des compétences techniques des autres membres de l'équipe, qui sont, il faut l'avouer, supérieures aux miennes, il m'a parut déplacé de vouloir faire preuve de leadership d'autant plus
que je ne faisais pas partie des membres de l'équipe qui maitrisaient le mieux le sujet. J'ai ainsi
été étonné de voir que j'ai fait preuve de leadership malgré moi en me souciant régulièrement
du respect du planning, de la mise à jour de ce dernier, mais également de s'assurer que chacun
respecterait ses engagements, moi inclus.
Je pense que le projet m'a permis d'avoir le sens des responsabilités dans une nouvelle discipline : le travail scolaire en équipe (car jusque là, la plupart des projets se faisaient par binôme,
et les durées de projets étaient bien plus longues que le projet GL). En eet, lorsque vous savez
qu'un de vos collègues ne peut avancer dans son travail, tant que votre tâche n'est pas accomplie,
5
vous avez une pression positive qui dynamise votre travail et qui optimise votre rendement (ce
qui rejoint une autre caractéristique commune de l'équipe : l'absence de stress).
3.1.4 Florence
Issue d'une formation plutôt axée sur le calcul scientique, j'abordais le projet GL avec une
certaine appréhension technique : je n'avais que très peu "touché" aux divers sujets abordés
(théorie des langages, langage d'assemblage...) et encore moins au langage utilisé pour le développement ; je craignais donc d'être un poids pour l'équipe. Je me suis cependant rendu compte
que j'étais capable d'assimiler rapidement ces nouveaux aspects techniques, et de m'adapter en
comblant mes manques éventuels par une recherche ecace d'information, an d'apporter une
réelle contribution à l'avancement du projet : j'ai donc particulièrement pris conance en la
"débrouillardise" mentionnée sur ma che d'évaluation.
L'organisation et la planication n'ont jamais été mon point fort : lors des précédents projets
réalisés en binôme, j'avais souvent tendance à m'éparpiller et à laisser des questions certes importantes mais non pertinentes lors des premiers stades bloquer mon avancement en début de projet
- quitte à compenser en prenant sur moi une grosse partie du travail à réaliser en urgence en n de
projet. Un tel fonctionnement est bien entendu inapplicable dans le cadre du projet GL, à cause
des contraintes de temps et des étroites dépendances entre les tâches réalisées par les diérents
membres de l'équipe. Le développement itératif et le cadre formé par l'équipe m'ont donc aidée à
dépasser cette faiblesse en me focalisant sur l'essentiel et en respectant la planication des tâches
à réaliser.
6
4 Conclusion et facteurs clés de succès
Le premier conseil que nous pourrions donner aux futurs élèves, serait de constituer au plus
tôt leur équipe de façon à s'entourer de personnes avec lesquelles ils pourraient travailler à plein
temps, dans la bonne humeur et sans créér de conits. Nous avons pu réaliser à quel point la
bonne entente au sein d'un groupe était primordiale, en voyant la tournure que prenaient les
choses dans certaines équipes au fur et à mesure que les échéances approchaient.
Ces trois semaines nous ont permis de nous rendre compte que le niveau technique d'une
équipe ne sut pas à assurer le bon déroulement et la nalité d'un projet tel que celui-ci, s'il
n'est pas soumis à une certaine rigueur et à une organisation carrée. Même en équipe de quatre il
devient nécessaire d'avoir un bon équilibre entre les domaines techniques et relationnels, d'où l'importance des interventions de l'équipe SHEME. Le second conseil serait donc de ne pas prendre la
préparation des suivis comme une perte de temps mais au contraire de rentabiliser ce temps pour
faire le point sur l'avancement du projet et revoir si besoin le planning. Les suivis se sont en fait
imposés d'eux mêmes comme des jalons, et en particulier le rendu intermédiaire qui permettait
à mi-parcours d'avoir un compilateur fonctionnel pour le langage Deca non objet.
Enn, d'un point de vue plus technique, nous avons pris conscience de l'importance cruciale de
la validation dans le cycle de développement. Certains cas tests peuvent paraître sur le moment
inutiles ou superus et permettent cependant de détecter des erreurs, inévitables sur un projet de
cette taille, souvent faciles à corriger mais néfastes pour la crédibilité du produit. Notre conseil
serait donc de ne pas négliger cette étape et d'y consacrer une part importante des ressources dès
le début du projet.
7
5 Annexe - Planning eectif du projet
2EQI
%REP]WI PI\MGSW]RXE\MUYI
0I\IYV
4EVWIYV
'SYVW 8(
6¬HEGXMSR HI PE GLEVXI HI XVEZEMP
%GXMZMX¬W 4IVWSRRIPPIW 7OM
'SQTMPEXIYV K¬VERX ,IPPS ;SVPH
0IGXYVI (SGYQIRXEXMSR
8IWXW
%REP]WI GSRXI\XYIPPI
+IWXMSR HI PE XEFPI HIW W]QFSPIW
-QTPIQIRXEXMSR HI PE +VEQQ %XXVMFY¬I
+¬R¬VEXMSR HI GSHI
%REP]WI HIW IVVIYVW GSRXI\XYIPPIW
-QTP¬QIRXEXMSR HIW IVVIYVW GSRXI\XYIPPIW
'SQTMPEXIYV WERW SFNIX
)\TVIWWMSRW 7MQTPI
)XETI &
)XETI '
)GVMXYVI HIW TVSKVEQQI HI XIWXW
8IWXW
:EVMEFPIW
)XETI &
)XETI '
)GVMXYVI HIW TVSKVEQQI HI XIWXW
8IWXW (IFSKEKI
7XVYGXYVI GSRHMXMSRRIPPI -J
)XETI &
)XETI '
)GVMXYVI 8IWXW 4EVXMI &
)GVMXYVI 8IWXW 4EVXMI '
8IWXW (IFSKEKI
&SYGPI ;LMPI
)XETI &
)XETI '
)GVMXYVI 8IWXW 4EVXMI &
)GVMXYVI 8IWXW 4EVXMI '
8IWXW (IFSKEKI
'SQTMPEXIYV TSYV PE XSXEPMX¬ HY PERKEKI
6IZYI H EVGLMXIGXYVI 4EVXMI '
3FNIXW 7ERW Q¬XLSHIW
)XETI &
)XETI & (¬GSVEXMSR
)XETI '
)GVMXYVI 8IWXW 4EVXMI &
)GVMXYVI 8IWXW 4EVXMI '
8IWXW (IFSKEKI
3FNIXW EZIG Q¬XLSHIW
)XETI &
)XETI & (IGSVEXMSR
)XETI '
)GVMXYVI HIW 8IWXW 4EVXMI &
)GVMXYVI HIW 8IWXW 4EVXMI '
8IWXW (IFSKEKI
'EWX -RWXERGI SJ
)XETI &
)XETI '
)GVMXYVI HIW 8IWXW 4EVXMI &
)GVMXYVI HIW 8IWXW 4EVXMI '
8IWXW (IFSKEKI
(SGYQIRXEXMSR 9XMPMWEXIYV
(SGYQIRXEXMSR 'SRGITXMSR
(SGYQIRXEXMSR :EPMHEXMSR
6IRHY ZIVWMSR MRXIVQ¬HMEMVI
6IRHY *MREP
6IRHY HSGYQIRXEXMSR
6IRHY &MPER
7SYXIRERGI
;SVO
;IIO
;IIO
;IIO
;IIO
1
H
H
H
H
H
H
H
L
H L
H
L
H L
H
H L
H L
H
H
H L
H L
H L
H L
L
H
L
H
H L
H L
H L
H
L
H L
H
H L
L
H L
H L
H L
H L
H L
L
H L
H L
H L
H L
H L
H
H L
H L
H
H L
H L
H L
H L
L
H L
H L
H
L
H
L
L
L
*PSVIRGI %QMRI
.SJJVI] /IZMR
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] ? A %QMRI ? A
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] /IZMR *PSVIRGI %QMRI
/IZMR
.SJJVI]
.SJJVI] /IZMR %QMRI
*PSVIRGI
/IZMR *PSVIRGI
/IZMR
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] %QMRI
*PSVIRGI
/IZMR
/IZMR *PSVIRGI
.SJJVI] %QMRI
/IZMR
*PSVIRGI
/IZMR *PSVIRGI
.SJJVI] %QMRI
/IZMR *PSVIRGI
*PSVIRGI
/IZMR
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] %QMRI
/IZMR *PSVIRGI
*PSVIRGI
/IZMR %QMRI
.SJJVI] /IZMR *PSVIRGI
.SJJVI] *PSVIRGI
.SJJVI] %QMRI
.SJJVI]
/IZMR *PSVIRGI
.SJJVI] %QMRI
*PSVIRGI
.SJJVI] /IZMR *PSVIRGI
%QMRI
.SJJVI]
.SJJVI] /IZMR
%QMRI
*PSVIRGI
.SJJVI] /IZMR *PSVIRGI %QMRI
L
H
H L
H
L
.SJJVI] /IZMR *PSVIRGI
.SJJVI] /IZMR *PSVIRGI %QMRI
.SJJVI] /IZMR *PSVIRGI %QMRI
8