Exam Compil 2015

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 5

Examen - Compilation (L3 Info) - semestre 2 - Aix, Luminy, St-Charles - 19/05/2015 - A. Nasr, C. Ramisch, S.

Sené

Examen - Compilation (L3 Info)


Durée : 2h - documents interdits
19 mai 2015

1 Grammaires attribuées
On s’intéresse dans cet exercice à des arbres dont les nœuds sont étiquetés par des entiers de 0 à 9. De tels arbres
peuvent être représentés par une expression parenthésée dans laquelle un nœud est représenté par son étiquette suivie
de la liste de ses fils entre parenthèses. L’expression 1(2(3())4()), par exemple, représente l’arbre ayant 1 pour racine.
Cette dernière possède elle même deux fils (2 et 4) et 2 a pour fils 3.
On appelle profondeur d’un noeud sa distance par rapport à la racine, dont la profondeur est 0. La hauteur d’un arbre
est la profondeur maximale de ses nœuds. On appelle degré d’un nœud, le nombre de fils qu’il possède. Les nœuds de
degré 0 sont appelés feuilles, les autres nœuds sont appelés nœuds internes.
Le langage des arbres est généré par la grammaire G ci-dessous 1 :
R → N E → 0
N → E(L) E → 1
L → NL ...
L → ε E → 9
Q.1. Dessinez l’arbre de dérivation correspondant à l’arbre 1(2(3())4())
Dans chacune des questions suivantes on cherche à répondre à une question concernant un arbre généré par la gram-
maire. Pour chacune, il vous est demandé de définir des attributs ainsi que des actions sémantiques sur la grammaire G
permettant de calculer la valeur des attributs. Tous les attributs sont à valeurs entières. Pour chacune des questions, vous
pourrez utiliser les attributs que vous avez défini pour répondre aux questions précédentes.
Pour chaque attribut défini, vous indiquerez :
1. ce qu’il représente
2. s’il est hérité ou synthétisé
3. sa valeur pour chaque nœud de l’arbre de dérivation de la question précédente.
Pour calculer la valeur des attributs, vous pourrez utiliser les opérations arithmétiques, les opérations booléennes,
l’opérateur binaire max(x, y) ainsi que le prédicat binaire egal(x, y) qui vaut 1 si x = y, et vaut 0 sinon.
Q.2. Combien de nœuds possède l’arbre ?
R → N R.n = N.n
N → E(L) N.n = L.n + 1
L → N L1 L.n = N.n + L1 .n
L → ε L.n = 0
Q.3. Quel est le degré de chaque nœud N ?
R → N R.d = N.d
N → E(L) N.d = L.d
L → N L1 L.d = L1 .d + 1
L → ε L.d = 0
Q.4. Quelle est la profondeur de chaque nœud N ?
R → N N.p = 0
N → E(L) L.p = N.p + 1
L → N L1 N.p = L.p
L1 .p = L.p
Q.5. Quelle est la hauteur de l’arbre ?
R → N R.h = N.h
N → E(L) N.h = L.h + 1
L → N L1 L.h = max(N.h, L1 .h)
L → ε L.h = 0
1. La première règle n’est pas nécessaire, mais elle sera utile pour la suite.

1
Examen - Compilation (L3 Info) - semestre 2 - Aix, Luminy, St-Charles - 19/05/2015 - A. Nasr, C. Ramisch, S. Sené

OU
R → N R.h = N.h
N → E(L) N.h = L.h
L → N L1 L.h = max(N.p, L1 .p)
L → ε L.h = 0
Q.6. Combien de feuilles possède l’arbre ?
N → E(L) N.f = L.f + egal(L.d, 0)
L → N L1 L.f = N.f + L1 .f
L → ε L.f = 0
Q.7. Chaque nœud N a-t-il le même degré que la racine ?
R → N N.r = N.d
N → E(L) N.m = egal(N.r, N.d)
L.r = N.r
L → N L1 N.r = L.r
L1 .r = L.r
Q.8. L’arbre est il équilibré (tous ses nœuds internes sont de même degré) ?
R → N R.e = N.e
N → E(L) N.e = egal(N.d, 0) ∨ (N.m ∧ L.e)
L → N L1 L.e = N.e ∧ L1 .e
L → ε L.e = 1

2 Compilateur d’arbres LATEX


Il existe de nombreuses bibliothèques LATEX pour dessiner des graphes et des arbres dans des documents, par exemple
les bibliothèques qtree et tikz. L’objectif de cet exercice est d’écrire un compilateur en C qui transforme un arbre au
format tikz en un arbre au format qtree. La figure 1 présente l’arbre de l’exercice précédent (a) dans les formats qtree
(b) et tikz (c, d).
— Arbre qtree : pour obtenir le résultat (a) avec qtree, nous utilisons une représentation parenthésée similaire
à l’exercice précédent, montrée en (b). Chaque sous-arbre est entouré de crochets [ et ]. Le crochet ouvrant est
immédiatement suivi d’un point ., lui-même suivi de l’étiquette du nœud. Pour simplifier, nous considérons comme
étiquettes possibles les entiers de 0 à 9. L’étiquette est suivie de la liste des sous-arbres séparés par des espaces.
Cette liste est vide dans le cas d’une feuille.
— Arbre tikz : pour obtenir le résultat (a) avec tikz, nous utilisons une liste de nœuds, montrée en (c) et (d).
Chaque nœud commence par la commande \node et se termine par un point-virgule ;. Il contient obligatoirement
une étiquette (un nombre entier entre 0 et 9), entre accolades. Entre la commande \node et l’étiquette, il est possible
de spécifier entre parenthèses un identificateur pour le nœud. Pour simplifier, l’identificateur dans les exemples est
identique à l’étiquette. Après l’étiquette, il est possible de spécifier une liste d’arcs vers des nœuds déjà déclarés. Il
est impossible de rajouter un arc vers un nœud qui est déclaré plus loin. Chaque arc est représenté par le mot-clef
edge suivi, entre parenthèses, de l’identificateur du nœud vers lequel il pointe. Les arcs de la liste sont séparés par
des espaces.
Pour représenter des arbres, deux ordres sont possibles :
1. rootFirst : la racine est le premier nœud à être déclaré. Ensuite, chaque nœud possède exactement un arc vers
son père. Les feuilles n’ont pas besoin d’identificateur. La racine est le seul nœud qui ne possède pas d’arc.
2. rootLast : la racine est le dernier nœud à être déclaré. Chaque nouveau nœud déclaré possède une liste d’arcs
vers ses fils. La racine n’a pas besoin d’identificateur. Les feuilles ne possèdent pas d’arc.
1. Écrivez une grammaire LL(1) qui reconnaît un arbre tikz. Cette grammaire doit en particulier reconnaître les
entrées 1(c) et 1(d). Afin de faciliter la manipulation et l’implémentation de la grammaire, essayez de minimiser le
nombre de symboles non terminaux. Utilisez les symboles terminaux ci-dessous, renvoyés par l’analyseur lexical :
— NODE : commande \node
— EDGE : mot-clef edge
— CHIFFRE : chiffre entre 0 et 9
— Caractères { } ( ) ;

2
Examen - Compilation (L3 Info) - semestre 2 - Aix, Luminy, St-Charles - 19/05/2015 - A. Nasr, C. Ramisch, S. Sené

1 \node (1) {1}; \node (3) {3};


\node (2) {2} edge (1); \node (2) {2} edge (3);
2 4 \Tree[.1 [.2 [.3 ] ] [.4 ] ] \node {4} edge (1); \node (4) {4};
\node {3} edge (2); \node {1} edge (2) edge (4);
3
(a) Exemple d’arbre, (b) Source LATEX de l’exemple, bi- (c) Source LATEX de l’exemple, bi- (d) Source LATEX de l’exemple, bi-
résultat de (b) (c) (d) bliothèque qtree bliothèque tikz, ordre rootFirst bliothèque tikz, ordre rootLast

Figure 1 – Exemple d’un arbre en LATEX (a), avec trois formats (b) (c) (d) qui, compilés, génèrent le même résultat.

1. liste_noeuds -> noeud liste_noeuds


2. liste_noeuds ->
3. noeud -> NODE opt_id ’{’ CHIFFRE ’}’ opt_liste_edges ’;’
4. opt_id -> ’(’ CHIFFRE ’)’
5. opt_id ->
6. opt_liste_edges -> EDGE ’(’ CHIFFRE ’)’ opt_liste_edges
7. opt_liste_edges ->
2. Calculez les Premier et Suivant pour chaque non terminal de la grammaire. Ensuite, construisez la table d’analyse
LL(1) de la grammaire.
Premier Suivant NODE EDGE CHIFFRE { } ; ( ) ⊥
liste_noeuds NODE ε ⊥ 1 2
noeud NODE NODE ⊥ 3
opt_id (ε { 5 4
opt_liste_edges EDGE ε ; 6 7
Remarque : Pour les deux questions suivantes, vous pouvez écrire un seul programme.
3. Écrivez un reconnaisseur LL(1) en C qui implémente la table d’analyse et reconnaît des entrées au format tikz.
Vous pouvez utiliser directement les fonctions fournies dans l’annexe A.
4. Enrichissez l’analyseur de la question précédente de manière à créer un arbre abstrait lors de la reconnaissance.
Nous nous intéressons désormais uniquement aux arbres décrits avec la stratégie rootFirst. L’annexe A présente les
fonctions creer_noeud et ajoute_fils (lignes 20 à 41) que vous devez utiliser pour créer et relier les nœuds entre
eux.
% void opt_liste_edges( t_noeud *fils ) {
% if( uc == EDGE ) {
% consommer( EDGE );
% consommer( ’(’ );
% ajoute_fils( yyval, fils );
% consommer( CHIFFRE );
% consommer( ’)’ );
% opt_liste_edges( fils );
% }
% else if( uc != ’;’ )
% erreur( "’;’ attendu, %c trouvé\n", uc );
% }
% int opt_id() {
% int id = -1;
% if( uc == ’(’ ) {
% consommer( ’(’ );
% id = yyval;
% consommer( CHIFFRE );
% consommer( ’)’ );
% }
% else if( uc != ’{’ )

3
Examen - Compilation (L3 Info) - semestre 2 - Aix, Luminy, St-Charles - 19/05/2015 - A. Nasr, C. Ramisch, S. Sené

% erreur( "’{’ attendu, %c trouvé\n", uc );


% return id;
% }
% t_noeud *noeud() {
% int id = -1, etiquette;
% t_noeud *$$;
% consommer( NODE );
% id = opt_id();
% consommer( ’{’ );
% etiquette = yyval;
% consommer( CHIFFRE );
% consommer( ’}’ );
% $$ = creer_noeud( id, etiquette );
% opt_liste_edges( $$ );
% consommer( ’;’ );
% return $$;
% }
% t_noeud *liste_noeuds() {
% t_noeud *$$;
% if(uc == NODE) {
% $$ = noeud();
% liste_noeuds();
% return $$; /* Seulement le premier noeud compte */
% }
% else if(uc != EOF)
% erreur("noeud ligne %d non reconnu", nbligne);
% }

5. Écrivez une fonction en C ayant pour en-tête void affiche_qtree( t_noeud *racine ). Cette fonction prend
en paramètre la racine de l’arbre abstrait, et affiche à l’écran le même arbre au format qtree. Le résultat de l’appel
à cette fonction sur l’exemple de la figure 1(c) doit être 1(b). Remarquez dans la structure de données (lignes 8 à
13) que chaque nœud possède deux pointeurs : vers son premier fils et vers son frère droit.
% void affiche_qtree_rec( t_noeud *root ) {
% if( root != NULL ) {
% printf( "[.%d ", root->etiquette );
% affiche_qtree_rec(root->fils);
% printf("] ");
% affiche_qtree_rec(root->frere);
% }
% }
% void affiche_qtree( t_noeud *root ) {
% printf( "\\Tree" );
% affiche_qtree_rec( root );
% printf( "\n" );
% }

4
Examen - Compilation (L3 Info) - semestre 2 - Aix, Luminy, St-Charles - 19/05/2015 - A. Nasr, C. Ramisch, S. Sené

A Fonctions disponibles
1 #define NODE ’n’
2 #define EDGE ’e’
3 #define CHIFFRE ’0’
4 #define ISNUMBER(a) ( a >= ’0’ && a <= ’9’ )
5 #define erreur(m, ...) { fprintf(stderr, "ERREUR : "); fprintf(stderr, m, __VA_ARGS__); exit(-1); }
6 /******************************************************************************/
7 // noeud de l’arbre abstrait
8 typedef struct t_noeud {
9 int id;
10 int etiquette;
11 struct t_noeud *fils;
12 struct t_noeud *frere;
13 } t_noeud;
14

15 t_noeud *noeuds[ 10 ] = { NULL }; // ‘‘table des symboles’’ simplifiee


16 int uc; //unite courante
17 FILE *yyin; //fichier source tikz
18 int yyval = -1; //valeur d’un chiffre
19 /******************************************************************************/
20 t_noeud *creer_noeud(int id, int etiquette) {
21 t_noeud *n = malloc(sizeof(t_noeud));
22 n->id = id; n->etiquette = etiquette;
23 n->fils = NULL; n->frere = NULL;
24 if( id >= 0 && noeuds[id] == NULL )
25 noeuds[id] = n;
26 else if( id >= 0 )
27 erreur( "Deux noeuds avec le meme ID %d\n", id );
28 return n;
29 }
30 /******************************************************************************/
31 void ajoute_fils(int id_pere, t_noeud *fils) {
32 t_noeud *na = noeuds[id_pere];
33 if( na->fils == NULL )
34 na->fils = fils;
35 else {
36 na = na->fils;
37 while(na->frere != NULL)
38 na = na->frere;
39 na->frere = fils;
40 }
41 }
42

43 /******************************************************************************/
44 void consommer(char c){
45 if(uc == c)
46 uc = yylex();
47 else
48 erreur("%c attendu, %c trouve\n",c,uc);
49 }
50 /******************************************************************************/

Vous aimerez peut-être aussi