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
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