15.07.2013 Views

Mini-compilateur C 1 Arbre de syntaxe abstraite 2 Types et ... - LRI

Mini-compilateur C 1 Arbre de syntaxe abstraite 2 Types et ... - LRI

Mini-compilateur C 1 Arbre de syntaxe abstraite 2 Types et ... - LRI

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Master M1 2012–2013 Proj<strong>et</strong> <strong>de</strong> programmation<br />

<strong>Mini</strong>-<strong>compilateur</strong> C<br />

analyse sémantique<br />

à rendre avant le lundi 5 Novembre 2012, 17h00<br />

1 <strong>Arbre</strong> <strong>de</strong> <strong>syntaxe</strong> <strong>abstraite</strong><br />

Dans l’arbre <strong>de</strong> <strong>syntaxe</strong> <strong>abstraite</strong> du corrigé <strong>de</strong> la première partie, les expressions<br />

e1[e2] <strong>et</strong> e->id ne sont pas présentes. Celles-ci sont éliminées par l’analyseur syntaxique<br />

qui produit les expressions équivalentes *(e1+e2) <strong>et</strong> (*e).id respectivement.<br />

2 <strong>Types</strong> <strong>et</strong> environnements <strong>de</strong> typage<br />

Dans tout ce qui suit, les expressions <strong>de</strong> types sont <strong>de</strong> la forme suivante :<br />

τ ::= void | int | char | struct id | union id | τ* | typenull<br />

où id désigne un i<strong>de</strong>ntificateur <strong>de</strong> structure ou d’union <strong>et</strong> s une constante entière. Il<br />

s’agit là d’une notation pour la <strong>syntaxe</strong> <strong>abstraite</strong> <strong>de</strong>s expressions <strong>de</strong> types. On introduit<br />

la relation ≡ sur les types comme la plus p<strong>et</strong>ite relation réflexive <strong>et</strong> symétrique telle que<br />

τ1, τ2 ∈ {int, char, typenull}<br />

τ1 ≡ τ2 typenull ≡ τ* void* ≡ τ*<br />

Un environnement <strong>de</strong> typage Γ est une suite <strong>de</strong> déclarations <strong>de</strong> types <strong>de</strong> la forme<br />

τ x, <strong>de</strong> déclarations <strong>de</strong> structures <strong>de</strong> la forme struct S {τ1 x1 · · · τn xn}, <strong>de</strong> déclarations<br />

d’union <strong>de</strong> la forme union S {τ1 x1 · · · τn xn} <strong>et</strong> <strong>de</strong> déclarations <strong>de</strong> profils <strong>de</strong> fonctions <strong>de</strong><br />

la forme τ f(τ1, . . . , τn). On notera struct S {τ x} (resp. union S {τ x}) pour indiquer<br />

que la structure S (resp. l’union S) contient un champ x <strong>de</strong> type τ.<br />

Dans la suite, on dira qu’un type est numérique, <strong>et</strong> on notera num(τ) si τ est compatible<br />

avec typenull, char, int ou un type pointeur.<br />

3 Typage<br />

3.1 Bonne formation <strong>de</strong>s types<br />

On dit qu’un type τ est bien formé dans un environnement Γ, <strong>et</strong> on note Γ ⊢ τ bf,<br />

si tous les i<strong>de</strong>ntificateurs <strong>de</strong> structures ou d’unions apparaissant dans τ correspon<strong>de</strong>nt à<br />

<strong>de</strong>s structures ou <strong>de</strong>s unions déclarées dans Γ.<br />

1


3.2 Typage <strong>de</strong>s expressions<br />

En C, il existe une classe d’expressions particulières nommées « valeurs gauches ».<br />

Intuitivement, les valeurs gauches sont les expressions que l’on a le droit <strong>de</strong> placer à gauche<br />

d’une affectation (d’où leur nom). À l’inverse d’autres langages, le fait d’être une valeur<br />

gauche en C dépend du type <strong>de</strong> l’expression. C’est pourquoi on introduit simultanément<br />

le jugement Γ ⊢ e : τ signifiant « dans l’environnement Γ, l’expression e est bien typée<br />

<strong>de</strong> type τ » <strong>et</strong> le jugement Γ ⊢l e : τ signifiant « dans l’environnement Γ, l’expression e<br />

est une valeur gauche bien typée <strong>de</strong> type τ ». Ces jugements sont définis par les règles<br />

d’inférence suivantes :<br />

(constantes)<br />

(affectations)<br />

Γ ⊢ 0 : typenull<br />

c constante <strong>de</strong> type τ<br />

Γ ⊢ c : τ<br />

Γ ⊢l e : τ num(τ) op ∈ {++, --}<br />

Γ ⊢ op e : τ<br />

(comparaisons)<br />

Γ ⊢l e1 : τ1 Γ ⊢ e2 : τ2 τ1 ≡ τ2<br />

Γ ⊢ e1 = e2 : τ1<br />

Γ ⊢ τ bf τ ≡ void<br />

Γ ⊢ sizeof(τ) : int<br />

Γ ⊢l e : τ num(τ) op ∈ {++, --}<br />

Γ ⊢ e op : τ<br />

Γ ⊢ e1 : τ1 Γ ⊢ e2 : τ2 τ1 ≡ τ2 op ∈ {==, !=, =}<br />

Γ ⊢ e1 op e2 : int<br />

(arithmétique <strong>et</strong> logique)<br />

Γ ⊢ e : τ τ ≡ int op ∈ {+, -}<br />

Γ ⊢ op e : int<br />

Γ ⊢ e : τ num(τ)<br />

Γ ⊢ !e : int<br />

τ1 ≡ struct s<br />

τ1 ≡ union s<br />

τ1 ≡ void<br />

Γ ⊢ e1 : τ1 Γ ⊢ e2 : τ2 τ1 ≡ τ2 τ1 ≡ int op ∈ {+, -, *, /, %, ||, &&}<br />

Γ ⊢ e1 op e2 : int<br />

(arithmétique <strong>de</strong> pointeurs)<br />

Γ ⊢ e1 : τ1 Γ ⊢ e2 : τ2 τ1 ≡ τ ′ 1* τ2 ≡ int op ∈ {+, -}<br />

Γ ⊢ e1 op e2 : τ ′ 1*<br />

Γ ⊢ e1 : τ1 Γ ⊢ e2 : τ2 τ1 ≡ τ ′ 2* τ2 ≡ τ ′ 2*<br />

Γ ⊢ e1 - e2 : int<br />

2<br />

Γ ⊢ e2 + e1 : τ<br />

Γ ⊢ e1 + e2 : τ


(appel <strong>de</strong> fonction)<br />

(accès)<br />

Γ ⊢ e : struct S struct S {τ x} ∈ Γ<br />

Γ ⊢ e.x : τ<br />

(adresse)<br />

Γ ⊢l e : τ<br />

Γ ⊢ &e : τ*<br />

(valeurs gauches)<br />

Γ ⊢ ei : τi τ f(τ ′ 1, . . . , τ ′ n) ∈ Γ τi ≡ τ ′ i<br />

Γ ⊢ f(e1, . . . , en) : τ<br />

Γ ⊢l e : τ<br />

Γ ⊢ e : τ<br />

τ x ∈ Γ<br />

Γ ⊢l x : τ<br />

Γ ⊢l e : struct S struct S {τ x} ∈ Γ<br />

Γ ⊢l e.x : τ<br />

3.3 Typage <strong>de</strong>s instructions<br />

Γ ⊢ e : union S union S {τ x} ∈ Γ<br />

Γ ⊢ e.x : τ<br />

Γ ⊢ e : τ*<br />

Γ ⊢l *e : τ<br />

Γ ⊢l e : union S union S {τ x} ∈ Γ<br />

Γ ⊢l e.x : τ<br />

On introduit le jugement Γ, τ0 ⊢i i signifiant « dans l’environnement Γ, l’instruction i<br />

est bien typée, pour un type <strong>de</strong> r<strong>et</strong>our τ0 ». Intuitivement, τ0 représente le type <strong>de</strong> r<strong>et</strong>our<br />

<strong>de</strong> la fonction dans la quelle se trouve l’instruction i. Ce jugement est établi par les règles<br />

d’inférence suivantes :<br />

3


Γ, τ0 ⊢i ;<br />

Γ, τ0 ⊢ e : τ<br />

Γ, τ0 ⊢i e; Γ, void ⊢i r<strong>et</strong>urn;<br />

Γ ⊢ e : τ num(τ) Γ, τ0 ⊢i i1 Γ, τ0 ⊢i i2<br />

Γ, τ0 ⊢i if (e) i1 else i2<br />

Γ ⊢ e : τ num(τ) Γ, τ0 ⊢i i<br />

Γ, τ0 ⊢i while(e) i<br />

Γ, τ0 ⊢i i1 Γ ⊢ e : τ num(τ) Γ, τ0 ⊢i i2 Γ, τ0 ⊢i i3<br />

Γ, τ0 ⊢i for(i1; e; i2) i3<br />

Γ ⊢ e : τ0<br />

Γ, τ0 ⊢i r<strong>et</strong>urn e;<br />

∀j ≤ k, Γ ⊢ τj bf τj ≡ void ∀j ≤ n, {τ1 x1, . . . , τk xk} ∪ Γ ⊢i ij<br />

Γ, τ0 ⊢i {τ1 x1 · · · τk xk;i1 · · · in}<br />

C<strong>et</strong>te <strong>de</strong>rnière règle signifie que pour typer un bloc constitué <strong>de</strong> k déclarations <strong>de</strong><br />

variables (locales au bloc) <strong>et</strong> <strong>de</strong> n instructions, on vérifie d’abord la bonne formation <strong>de</strong>s<br />

déclarations puis on type chacune <strong>de</strong>s instructions dans l’environnement augmenté <strong>de</strong>s<br />

nouvelles déclarations.<br />

De plus, on a les équivalences suivantes :<br />

– if (e1) e2 équivaut à if (e1) e2 else;<br />

– si i1 ou i3 est omis dans for(i1;e2;i3) alors il équivaut à ;<br />

– si e2 est omis dans for(i1;e2;i3) alors il équivaut à 1<br />

– dans la construction for, une liste d’expressions e1, e2, . . . , en équivaut à la séquence<br />

e1;e2; . . . ;en.<br />

3.4 Typage <strong>de</strong>s fichiers<br />

On rappelle qu’un fichier est une liste <strong>de</strong> déclarations. On introduit le jugement « Γ ⊢<br />

d → Γ ′ » qui signifie « dans l’environnement Γ, la déclaration d est bien formée <strong>et</strong> produit<br />

un environnement Γ ′ ». Ce jugement est dérivable grâce aux règles suivantes :<br />

Déclarations <strong>de</strong> variables (globales)<br />

Déclarations <strong>de</strong> structures <strong>et</strong> d’unions<br />

Γ ⊢ τ bf τ ≡ void<br />

Γ ⊢ τ x → {τ x} ∪ Γ<br />

Γ, struct id {τ1 x1 · · · τn xn} ⊢ τi bf<br />

Γ ⊢ struct id {τ1 x1; · · · τn xn; } → {struct id {τ1 x1 · · · τn xn}} ∪ Γ<br />

Γ, union id {τ1 x1 · · · τn xn} ⊢ τi bf<br />

Γ ⊢ union id {τ1 x1; · · · τn xn; } → {union id {τ1 x1 · · · τn xn}} ∪ Γ<br />

On vérifiera d’autre part que les types <strong>de</strong> champs τi ne font référence à la structure ou à<br />

l’union id elle-même que sous un pointeur.<br />

4


Déclarations <strong>de</strong> fonctions<br />

Γ ⊢ τi bf ∀i > 0, τi ≡ void {τ0 f(τ1, . . . , τn), τ1 x1, . . . , τn xn} ∪ Γ, τ0 ⊢i b<br />

Γ ⊢ τ0 f(τ1 x1, . . . , τn xn) b → {τ0 f(τ1, . . . , τn)} ∪ Γ<br />

On remarque que le prototype d’une fonction est ajouté à l’environnement pour le typage<br />

<strong>de</strong> c<strong>et</strong>te <strong>de</strong>rnière, cela dans le but <strong>de</strong> typer les fonctions récursives.<br />

Fichiers. On introduit finalement le jugement Γ ⊢f d1 · · · dn signifiant « dans l’environnement<br />

Γ le fichier constitué par la suite <strong>de</strong> déclarations d1, . . ., dn est bien formé ».<br />

Le typage d’un fichier consiste à typer successivement les déclarations dans le contexte<br />

étendu par chaque nouvelle déclaration, d’où les règles :<br />

Γ ⊢f ∅<br />

Γ ⊢ d1 → Γ ′ Γ ′ ⊢f d2 · · · dn<br />

Γ ⊢f d1 d2 · · · dn<br />

Règles d’unicité. Enfin, on vérifiera l’unicité :<br />

– <strong>de</strong>s i<strong>de</strong>ntificateurs <strong>de</strong> structures sur l’ensemble du fichier ;<br />

– <strong>de</strong>s i<strong>de</strong>ntificateurs d’union sur l’ensemble du fichier ;<br />

– <strong>de</strong>s champs <strong>de</strong> structure à l’intérieur d’une même structure ;<br />

– <strong>de</strong>s symboles (variables globales <strong>et</strong> fonctions) sur l’ensemble du fichier.<br />

On notera qu’une structure <strong>et</strong> une union peuvent avoir le même i<strong>de</strong>ntificateur.<br />

Fonctions prédéfinies. Les fonctions suivantes sont supposées prédéfinies <strong>et</strong> <strong>de</strong>vront<br />

être connues à l’analyse sémantique :<br />

int putchar(int n);<br />

void *sbrk(int n);<br />

De plus, on vérifiera la présence d’une fonction main avec l’un <strong>de</strong>s <strong>de</strong>ux prototypes<br />

suivants :<br />

int main();<br />

int main(int argc, char** argv);<br />

dans le fichier.<br />

4 Travail <strong>de</strong>mandé<br />

Vous <strong>de</strong>vez étendre votre <strong>compilateur</strong>, minic <strong>de</strong> la première partie (ou repartir du<br />

corrigé) <strong>de</strong> manière à ce qu’il accepte sur la ligne <strong>de</strong> comman<strong>de</strong> exactement un fichier C<br />

<strong>et</strong> éventuellement l’option -type-only (l’option -parse-only <strong>de</strong> la partie 1 doit arrêter<br />

le <strong>compilateur</strong> après l’analyse syntaxique, i.e. avant la phase <strong>de</strong> typage).<br />

Si le fichier est conforme à la <strong>syntaxe</strong> <strong>et</strong> bien typé, le programme doit terminer avec<br />

le co<strong>de</strong> <strong>de</strong> sortie 0 (exit 0 explicite ou terminaison normale du programme), sans rien<br />

afficher. En cas d’erreur lexicale, syntaxique ou <strong>de</strong> typage, celle-ci doit être signalée comme<br />

en partie 1 <strong>et</strong> le programme doit terminer avec le co<strong>de</strong> <strong>de</strong> sortie 1 (exit 1). En cas d’autre<br />

erreur (une erreur du <strong>compilateur</strong> lui-même), le programme doit terminer avec le co<strong>de</strong> <strong>de</strong><br />

sortie 2 (exit 2).<br />

5


Anticipation La connaissance du type d’une expression est nécessaire pour la génération<br />

<strong>de</strong> co<strong>de</strong> (par exemple, dans l’expression e1+e2, il est nécessaire <strong>de</strong> connaître les<br />

types <strong>de</strong> e1 <strong>et</strong> e2 pour savoir si on va générer le co<strong>de</strong> pour une addition <strong>de</strong> pointeurs, une<br />

addition d’entiers, <strong>et</strong>c. Vous ne pouvez donc pas vous contenter d’écrire une fonction <strong>de</strong><br />

typage renvoyant vrai ou faux (selon que le fichier est bien typé ou pas) mais vous <strong>de</strong>vez<br />

générer un nouvel AST contenant les informations <strong>de</strong> typage.<br />

5 Notation<br />

Le barème suivant est donné à titre indicatif<br />

– Rapport : 6 points ;<br />

– Qualité du co<strong>de</strong> : 6 points (commentaires, in<strong>de</strong>ntation <strong>et</strong> lisibilité générale, élégance)<br />

;<br />

– Validité du <strong>compilateur</strong> : 8 points (obtenus par <strong>de</strong>s tests automatiques, il est donc<br />

très important <strong>de</strong> respecter scrupuleusement les modalités <strong>de</strong> rendu, sous peine<br />

d’avoir 0/8 à c<strong>et</strong>te partie).<br />

Afin d’individualiser les notes au sein du binôme, il vous est <strong>de</strong>mandé d’annoter chaque<br />

fonction (ou fichier) conséquent par son auteur.<br />

Modalités <strong>de</strong> remise <strong>de</strong> votre proj<strong>et</strong>. Votre proj<strong>et</strong> doit se présenter sous forme d’une<br />

archive tar compressée (option “z” <strong>de</strong> tar), appelée binome_1-binome_2 .tar.gz qui doit<br />

contenir un répertoire appelé binome_1-binome_2. Exemple : dupont-durand.tar.gz.<br />

Utilisez <strong>de</strong>s « _ » pour les noms composés <strong>et</strong> utilisez uniquement « - » pour séparer<br />

les <strong>de</strong>ux noms. Utilisez uniquement <strong>de</strong>s minuscules, sans accent <strong>et</strong> sans espace. Dans ce<br />

répertoire doivent se trouver les sources <strong>de</strong> votre programme (ne donnez pas les fichiers<br />

compilés). Quand on se place dans ce répertoire, la comman<strong>de</strong> make doit créer votre<br />

<strong>compilateur</strong>, qui sera appelé minic. La comman<strong>de</strong> make clean doit effacer tous les fichiers<br />

que make a engendré <strong>et</strong> ne laisser dans le répertoire que les fichiers sources.<br />

Le proj<strong>et</strong> est à faire impérativement en binôme. Il doit être remis à votre chargé <strong>de</strong><br />

TP par e-mail 1 au plus tard<br />

le Lundi 5 Novembre 2012, 17h00<br />

Le non respect (même minime) <strong>de</strong>s consignes ci-<strong>de</strong>ssus entraînera une perte <strong>de</strong> points.<br />

1. Groupe 1 : atafat@lri.fr, Groupe 2 : kn@lri.fr, Groupe 3 : iguer@lri.fr<br />

6

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!