kooc .pdf



Nom original: kooc.pdf

Ce document au format PDF 1.4 a été généré par LaTeX with hyperref package / pdfTeX-1.40.10, et a été envoyé sur fichier-pdf.fr le 29/11/2013 à 16:16, depuis l'adresse IP 129.12.x.x. La présente page de téléchargement du fichier a été vue 1079 fois.
Taille du document: 263 Ko (20 pages).
Confidentialité: fichier public


Aperçu du document


Kind of objective C

Documentation technique
EPITECH
Correspondant LabTxT : Amaury De La Vieuville
Project Manager : Thomas Coudray

Ahmed Bougacha
Charly Chevalier
Geo roy Aubey
Tom-Brent Yau
9 novembre 2012

Table des matières
1 Fonctionnement

1

2 Modules

3

2.1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3

2.2

@import et kpp . . . . . . . . . . . . . . . . . . . . . . . . . .

4

2.3

@module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.4

@implementation . . . . . . . . . . . . . . . . . . . . . . . . .

6

2.5

Appels KOOC et typage . . . . . . . . . . . . . . . . . . . . .

6

2.6

Mangling

8

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3 Classes

10

3.1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

3.2

@class

10

3.3

[* new] [* alloc] [* delete]

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .

4 Héritage

11

14

4.1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

4.2

Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

4.3

@virtual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

4.4

Object*

16

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5 Conclusion

17

1

Résumé
Le KOOC est une surcouche orientée objet au langage C, implémentant une
partie des fonctionnalités de langages tels que C++, Java. Le KOOC sera
similaire à l'Objective C (d'où le nom : Kind Of Objective C). Pour ce faire,
nous disposons d'outils : le CodeWorker

1 et le CNORM 2 . Grâce à ces outils,

le KOOC transforme le langage du même nom en code C standard. Ce code
sera ensuite compilé comme n'importe quel autre code C.

1. CodeWorker est un outil de parsing et de génération de code
2. CNORM est une bibliothèque en CodeWorker permettant de parser du C

Chapitre 1

Fonctionnement
L'éxécutable

kcc

transforme des chiers KOOC (.kc et .kh) en chiers C

valides. Cette transformation se fait en plusieurs étapes :

kpp
Le passage du pré-processeur KOOC, pour la gestion des
des

#includes.

cpp
Le passage du pré-processeur C, pour la gestion des

kcc

@import

et

#defines.
1

Le parsing avec notre grammaire basée sur CNORM, création de l'AST .

cnorm2c
Pretty-printing de l'AST pour avoir une sortie en C.

gcc
Compilation de la sortie C avec le runtime KOOC en utilisant un compilateur tiers (gcc, clang, . . .).

1. L'AST, (Abstract Syntax Tree, pour Arbre Syntaxique Abstrait), est une structure
de données représentant le code source et permettant son analyse.

1

Les phases qui nous concernent sont la première et la troisième.
La dernière phase, la compilation, nécessite l'ajout du runtime KOOC.
Le runtime KOOC comprend :
1. Des chiers headers C qui seront automatiquement inclus lors de la
compilation d'un chier KOOC :
(a)

Object.kh,

déclarant la classe du même nom dont sont dérivées

toutes les autres.
(b)

Exception.kh,

déclarant le type Exception de base.

2. La bibliothèque partagée

krt.so,

fournissant l'implémentation de ces

deux classes, mais aussi les fonctions utilisées par le code C généré.

2

Chapitre 2

Modules
2.1

Introduction

La première chose à faire est l'implémentation des trois fonctionnalités suivantes :

a) @import, b) @module, c)

et

@implementation.

Pour les implémenter, nous avons ajouté nos propres règles dans la grammaire de CNORM. Chacune de ces règles va parser la directive associée. Ces
mots-clés étant nécessairement au plus haut niveau du chier KOOC, les
règles correspondantes doivent être au plus haut niveau de la grammaire.

translation_unit, c'est
translation_unit,
declaration du CNORM :

Dans CNORM, un chier C est parsé par la règle

la première règle appelée. Nous avons donc ajouté, dans
la règle

kooc_directive

alternative à la

kooc_directive :=
kooc_import | kooc_module | kooc_implementation
;
#overload
translation_unit :=
[
kooc_directive | declaration
]+
;
Le mot-clé

#overload

permet de redé nir la règle

3

translation_unit.

2.2

@import et kpp

@import

#include

en C, sauf qu'il s'agit ici d'inclusion

Il faut en plus faire en sorte de pouvoir

@importer un même chier plusieurs

se comporte comme

de chiers headers KOOC.

@import fichier.kh
équivaut à :

#include fichier.h

fois sans que cela entraîne une erreur. Cette directive est implémentée par
le script CodeWorker

kpp.

C'est la règle

kooc_import

qui va la parser. Une

fois le nom du chier à importer connu, le chier est inclus dans le ux

#parsedFile. Ceci permet de gérer
@import qui se trouvent dans le chier inclus. Le chemin du
sauvegardé dans un tableau (this.kooc.included), permettant

d'entrée à l'aide du mot-clé CodeWorker
récursivement les
chier est

donc d'éviter d'inclure le même chier plusieurs fois.
Dans le cas où une erreur est détectée par gcc dans un chier importé, le
diagnostic doit indiquer la ligne dans ce dernier, et non pas dans le chier .c
créé par notre KOOC. À la manière de cpp lorsqu'il rencontre une directive

#include,

nous ajoutons des line directives

1 avant et après le contenu du

chier importé. Le support des line directives prends la forme de noeuds

Raw

(text brut) dans l'AST.
Module.c :

int x = 1;
@import module.kh
devient :

int x = 1;
# 1 module.kh
/* contenu de module.kh */
# 3 module.kc

1. Les line directives sont des directives de préprocesseur insérées par cpp. Elles permettent de signaler au compilateur que le code source suivant la line directive appartient
au chier indiqué.
4

2.3

@module

Un module est un groupement logique de fonctions et de variables globales
associées. Le mot-clé

@module

permet d'en déclarer un, et est suivi d'un

bloc de code contenant les déclarations des fonctions et variables du module.
Seules ces déclarations sont acceptées. Le module est identi é par un nom
qui sera utilisé dans le reste du code source.
L'intégration de

@module

se fera en 2 étapes :

1. parser la déclaration du module.
2. mangler

2 les identi ants.

On utilisera les règles

cdecl

de cnorm pour parser le contenu du bloc et

ainsi l'ajouter à l'AST. Pour travailler sur les données nouvellement parsées,
on va se servir de deux indices,

blockStartIdx

qui correspond à l'entrée du

bloc et blockEndIdx correspondant à la n du bloc, pour pouvoir itérer sur
la partie de l'AST correspondant au bloc.
En itérant sur cette partie, on va procéder à la seconde étape, à savoir le
mangling (la décoration des identi ants). Durant cette étape nous allons
aussi sauvegarder les di érentes dé nitions de variables a n de les injecter
dans le bloc

@implementation correspondant. Lors de la transformation vers

du C standard, les déclarations de variables de module sont modi ées pour
ajouter le

storage-class specifier extern, permettant d'en faire des déc-

larations de variables globales. Omettre ce mot-clé en aurait fait des dé nitions de variables globales, ce qui est contraire à la sémantique du

@module

du KOOC.

@module M {
int a = 1;
float a;
const char c = `C';
int a(int b, float* c);
}
donnera en C :

extern int _K1M1ai;
extern float _K1M1af;
extern const char _K1M1cc;
int _K1M1aiiPf(int a, float* c);
(Voir la partie sur le mangling pour plus de précisions)

2. Le Mangling est la décoration d'identi ant. Il est par exemple utilisé pour modi er
l'identi ant d'une variable/fonction de manière à le distinguer de variables/fonctions du
même nom appartenant à un module di érent.

5

2.4

@implementation

À la manière de la déclaration de module,
dé nir les variables/fonctions déclarées dans le

@implementation permet de
@module correspondant (ayant

le même identi ant). Ne sont autorisées que les dé nitions de fonctions. On
utilisera les mêmes méthodes que

@module

pour récupérer le bloc (parsing)

et itérer dessus a n d'en mangler les identi ants (mangling). De plus, nous
injectons les di érentes dé nitions de variables faites par le biais de

@module.

@implementation M {
int a(int b, float* c) {
return b * *c;
}
}
donnera en C :

int _K1M1ai = 1;
float _K1M1af;
const char _K1M1cc = `C';
int _K1M1aiiPf(int b, float *c) {
return b * *c;
}

2.5

Appels KOOC et typage

Dans le langage dé ni par notre KOOC, la syntaxe permettant l'accès aux
éléments d'un module est basée sur les crochets. À l'intérieur de ces derniers
se trouvent le nom du module et le nom de la variable ou de la fonction
souhaitée. Les modules permettant la surcharge de fonctions et de variables,

a) l'identi ant, b) pour les fonctions, les types
c) le type de retour, et en n, d) pour les variables, le type

il est nécessaire de connaître
des paramètres, ou

de celles-ci pour avoir la signature de la fonction/variable. C'est la résolution
de surcharge.
Pour l'utilisateur, il y a deux manières de procéder :
1. La première est de spéci er explicitement le type avec
les valeurs de retour ou bien

(type)

@!(type)

pour

pour les paramètres.

Exemple :

@!(int)[Module foo :(int)12 :(char *)str]
Dans ce cas, il su t de reconstruire le type de la fonction foo grâce aux
indications puis de mangler (en prenant en compte le nom du module
et de la fonction).

6

2. Dans le deuxième cas, les types ne sont pas clairement spéci és et il
nous appartient donc de les déduire d'après le contexte.
Exemples :

[Module bar:12 :str :1 + 1]
[Module.var] = 1
Algorithme de résolution de type

1: procedure GetTypeCandidates(expr)
2:
if IsLiteral(expr) then
3:
candidates ← {TypeOfLiteralExpression(expr)}
4:
else if IsIdentif ier(expr) then
5:
declaration ← FindVariableDeclaration(child)
6:
candidates ← {declaration.type}
7:
else
8:
candidates ← GetTypeCandidates(Head(expr.children))
9:
for all child ∈ expr.children \ Head(expr.children) do
10:
candidates ← candidates ∩ GetTypeCandidates(child)
11:
end for
12:
end if
13:
return candidates
14: end procedure

15: procedure ResolveTypes(expression_statement)
16:
T ← GetTypeCandidates(expression_statement)
17:
Ensure |T | = 1
18: end procedure
Comme décris dans le pseudocode, on procède de la manière suivante : Après
chaque

expression_statement, on va e ectuer une résolution de types. Pour

cela, on va descendre récursivement dans l'arbre de l'expression et remonter
une liste de types correspondant aux types possibles pour le sous-arbre (descendre jusqu'aux expressions terminales, dans le cas d'un littéral, l'associer
à un type, sinon trouver la déclaration de la variable pour avoir son type).
Dans le cas où deux noeuds ls renvoient des listes di érentes on va simplement renvoyer l'intersection de ces deux listes. Par exemple, dans l'exemple
ci-dessus, la variable var a été surchargée en int et double. Le noeud correspondant à l'opérateur = va donc récupérer les listes {int,

double} et {int}

dont l'intersection est {int}. Une fois le type de l'expression déterminé et
s'il n'y a pas d'ambiguïté (c'est-à-dire qu'il n'y a qu'un seul type possible

une fois au noeud racine), on redescend dans le sous-arbre pour transformer
les appels KOOC correspondants. Dans le cas où l'on se retrouve avec deux

char - il est possible de garder int
puisqu'un char peut être converti en int (covariance des types). Si jamais il
n'y avait pas d'appel KOOC dans l'expression_statement, il est inutile de
procéder à cette phase de typage. gcc s'en occupera et permettra un typage
types compatibles - par exemple

int

et

moins strict.

7

2.6

Mangling

Le mangling consiste à décorer les symboles (variables, fonctions) exportés
par le module et donc de les rendre uniques.
Les éléments suivants du KOOC seront soumis au mangling :
1. Les variables et fonctions exportées par les modules, sauf ceux qui sont
statiques.
2. Les variables membres et les fonctions membres de classes.
3. Tous les appels KOOC de la forme

[...].

Les types simples ont chacun une lettre minuscule :
Mangling - types atomiques

float
int
auto
char
void
double
short
long
long long

f
i
i
c
v
d
s
l
j

Les types composés sont représentés par une lettre pré xant le nom du type :
Mangling - types composés

union u_data
struct s_data

U6u_data
S6s_data

Les pointeurs sont pre xés :
Mangling - types pointeurs

pointeur
int*

p
pi

Tous les identi ants sont de plus pré xés par leurs longueurs, pour les différencier des lettres standard :
Mangling - identi ants

variable

8variable

Le symbole nal est créé en composant :
1.

_K,

c'est la signature du KOOC.

2. L'identi ant
3. Le type pour les variables, ou le type de retour pour les fonctions
4. Les types successifs des paramètres

8

Mangling - fonctions/variables dans le module m

void fun(int a, float b)
const int var

void _K1m3funif(int a, float b)
const int_K1m3varKi

Les quali eurs ne sont pas pris en compte pour le mangling. Pour les variables
dont le type est un
par le

typedef,

typedef,

il faut d'abord récupérer le type e ectif dé ni

car c'est celui-ci qui est utilisé pour le mangling.

9

Chapitre 3

Classes
3.1

Introduction

Une classe est un module qui présente en plus, une notion de membres. La
notion de membre implique celle d'instance. C'est sur ces deux fonctionnalités que se concentre cette partie.

3.2

@class

Une classe est declarée de la même manière qu'un module, mais en utilisant le

@class. Nous utiliserons la règle kooc_class pour parser le contenu
de cette dernière. Tout comme @module, la partie déclarative se trouve être
dans le bloc correspondant à @class, et la partie dé nition dans le bloc
@implementation portant le même identi ant que @class. Pour déclarer
une fonction ou une variable membre, nous fournissons le mot-clé @member,
mot-clé

qui peut s'appliquer sur une seule ou un bloc de déclarations.
Les variables et fonctions membres sont traitées de manières di érentes. Lors
de la déclaration des variables membres (ou attributs) dans

@class, nous al-

lons les sauvegarder dans l'AST de manière à isoler ces dé nitions dans une
structure associée à la

@class courante. C'est cette structure qui sera instan@class. Dans cette étape,

ciée lors de la création d'un nouvel objet de type

pas besoin de mangler des noms de variables, seul un mangling de la structure appartenant à la classe est nécessaire. Quant aux fonctions membres,
nous les traitons de la même manière que les fonctions non-membres, à ceci
près que nous rajoutons à la fonction un paramètre en première position,
nommé self qui est un pointeur sur l'instance de la classe.

10

@class C {
@member int i;
@member float f;
@member void fun();
}
@implementation C {
@member void fun() {}
}
donnera en C :

typedef struct __cls_C C;
void _K1C3funvPS7__cls_C(C * self);
struct __cls_C {
int i;
float f;
};
void _K1C3funvPS7__cls_C(C * self) {}

3.3

[* new] [* alloc] [* delete]

[* new], [* alloc], et [* delete] sont des fonctions non-membres dé nies
implicitement permettant de gérer l'allocation, l'initialisation et la destruction de l'instance de notre objet. Comme nous ajoutons des fonctions dans
chaque classe, il faut à la fois les déclarer et aussi les dé nir dans chaque partie correspondante (@class pour les déclarations et
les dé nitions). On utilisera

alloc :

@implementation

pour

cnormPatchCode pour injecter le code nécessaire.

Allocation

[* alloc] est une fonction retournant une instance de notre classe, allouée sur le tas (heap). Cette fonction reviens donc à un simple malloc
de la taille de la structure générée pour la classe.

new :

Allocation et Initialisation

[* new]

sont un ensemble de fonctions qui permettent à la fois l'in-

stanciation d'un objet de notre classe mais aussi l'initialisation de ce
dernier. Pour cela, pour chaque fonction membre
dé nissons une fonction

new

init

déclarée, nous

prenant les mêmes paramètres, en omet-

tant le premier (self, l'instance de la classe). Dans un premier temps
nous stockons dans l'AST toutes les déclarations de fonctions nommées

init.

Une fois ces informations obtenues, nous allons nous occuper de

déclarer les fonctions

new

associées dans la partie déclarative de la

classe. Puis, par la suite on dé nira ces fonctions dans la partie dé nition.

delete :

Destruction

[* delete]

est une fonction qui permet de libérer la mémoire allouée

11

par

[* new]

ou

[* alloc].

Elle correspond à un simple

stance de la classe. Cependant, si une fonction membre

free de l'inclean existe,

on s'assurera de l'appeler avant de libérer la mémoire occupée. Pour
cela nous allons regarder si cette fonction a été dé nie et déclarée dans
l'ensemble de la classe. Si c'est le cas, nous injecterons un appel à cette
fonction avant de libérer la mémoire.
Par exemple :

@class C {
@member void init(int i);
@member void clean();
}
@implementation C {
@member {
void init(int i) {
printf("%d\n", i);
}
void clean() { }
}
}
donnera en C :

typedef struct __cls_C C;
void _K1C4initvPS7__cls_Ci(C *
void _K1C5cleanvPS7__cls_C(C *
struct __cls_C {};
C * _K1C5allocPS7__cls_Cv(void
C _K1C3newS7__cls_Ci(int i);
void _K1C5cleanvPS7__cls_C(C *

self, int i);
self);
);
self) {}

void _K1C4initvPS7__cls_Ci(C * self, int i) {
printf("%d\n", i);
}
C * _K1C5allocPS7__cls_Cv() {
return malloc(sizeof (C ));
}
void _K1C6deletevPS7__cls_C(C * self) {
_K1C5cleanvPS7__cls_C(self);
return free(self);
}
C * _K1C3newPS7__cls_Ci(int i) {
C * self = _K1C5allocPS7__cls_Cv();
12

}

_K1C4initvPS7__cls_Ci(self, i);
return self;

Appel :

C* c = [C alloc];
[c init :10];
[C init :c :10];
C a;
[&a init :42];
[tab[12]->niania func :12];

13

Chapitre 4

Héritage
4.1

Introduction

Dans cette partie, nous allons rajouter un système d'héritage simple entre
classes, et la notion de fonctions polymorphes.

4.2

Héritage

Pour implémenter l'héritage, nous allons stocker le nom de la classe parente
dans l'AST au niveau de la classe lle

this.kooc.modules[fils].parent.

Ainsi nous pourrons accéder à l'ensemble des membres du parent facilement
en passant par son nom :

this.kooc.modules[parent].*.

Pour l'héritage au niveau des attributs, nous allons ajouter chaque variable
membre de la classe parente dans la classe lle. Comme nous possédons
l'ensemble des attributs de chaque classe dans notre AST, il nous su t alors
d'itérer sur l'ensemble des attributs de la classe parente et ainsi de les ajouter
à ceux de la classe lle.
Pour ce qui est des fonctions virtuelles, nous utilisons le même procédé. Nous
ajoutons des références sur les méthodes virtuelles de la classe mère dans la
section virtuals de la classe lle. Ainsi lors de la dé nition de la vtable de la
classe lle, toutes les fonctions virtuelles héritées seront elles aussi ajoutées
à la vtable.

4.3

@virtual

C'est la directive

kooc_class qui s'occupera de récupérer les fonctions mem-

bres virtuelles.
Le mot-clé

@virtual

permet la surcharge des fonctions membres lors de

l'héritage. Un appel de fonction membre virtuelle s'applique au niveau du
type réel, et non du type apparent de l'objet. Ainsi, c'est toujours la fonction
virtuelle du type réel, celui qui a été instancié, qui est appelée.

14

Lors de la transformation des sources KOOC en sources C, nous sauvegardons ces fonctions virtuelles dans

this.kooc.modules[].virtuals.

Cette

1 de la foncpartie de l'AST est indexée sur le nom partiellement décoré
tion virtuelle (mangledMethodName). Cela permet de gérer simplement la
surcharge des fonctions virtuelles lors de l'héritage. En e et, lors de la création d'une nouvelle classe, nous allons hériter directement ces fonctions de
la classe parente, et l'implémentation d'une fonction écrase l'ancienne implémentation.
Une fois toutes les fonctions virtuelles récupérées, nous allons déclarer la
vtable qui sera représentée par une structure globale qui va contenir des
pointeurs sur ces dernières.
Ce n'est que lors de l'initialisation de l'objet (fonctions membres init) que
cette vtable viendra se gre er à l'objet, permettant ainsi l'utilisation des
fonctions virtuelles.
Par exemple :

@class C {
@member int a;
@virtual void foo(void);
}
@class D : C {
@virtual void foo(void);
}
@implementation C {
@virtual void foo(void) { }
}
@implementation D {
@virtual void foo(void) { }
}
D *d;
...
[d foo];
donnera :

typedef struct __cls_C C;
struct __vtbl_C {
void ( * 3foovv)(C * self);
} ;
extern struct __vtbl_C __vtable_for_C;
1. Les méthodes virtuelles sont manglées de manière particulière, a n d'enlever le nom
du module de l'identi ant nal. Cela permet d'appeler une méthode virtuelle sans expliciter l'origine de cette méthode.
15

struct __cls_C {
int _K1C1ai;
} ;
...
struct __vtbl_D {
void ( * 3foovv)(D * self);
} ;
extern struct __vtbl_D __vtable_for_D;
struct __cls_D {
int _K1C1ai;
} ;
struct __vtbl_C __vtable_for_C = { _K1CfoovP1C};
struct __vtbl_D __vtable_for_D = { _K1DfoovP1D};
D *d;
[...]
d->__vptr->_K3foovv(d);

4.4

Object*

Object sert à obtenir des informations sur l'instance. Toutes les classes vont
donc en hériter. Il va entre autre contenir le nom de la classe. Object va
proposer les méthodes suivantes :

[Object isInstanceOf]

permet de savoir si les types réels des deux classes

sont les mêmes. Il su t pour cela de comparer les pointeurs vers leurs
deux vtables.

[Object isKindOf]

permet de savoir si l'instance est dérivée de la classe

passée en paramètre.
Plusieurs fonctionnalités nécessitent d'avoir accès à des informations concernant la hiérarchie des classes au runtime. Ces informations sont centralisées
dans l'instance de métaclasse. Chaque instance de la classe contient un pointeur vers celle-ci. Par exemple, pour implémenter

[Object isKindOf], il est

nécessaire de savoir quelle classe est la parente de la classe courante : la
métaclasse contient ainsi un pointeur vers la métaclasse parente.

16

Chapitre 5

Conclusion
En résumé, nous décrivons dans ce document le KOOC, un langage et un
compilateur implémentant une extension du C. À travers les mécanismes
de

@module,

de

@class,

et d'héritage, ce langage permet de pro ter du

paradigme objet tout en gardant la exibilité du C. Le CodeWorker et le
CNORM permettent de se concentrer sur le problèmes les plus intéressants,
grâce au fait que l'on évite d'une part les détails rébarbatifs du parsing, et
d'autre part le besoin de recoder le C.

17




Télécharger le fichier (PDF)

kooc.pdf (PDF, 263 Ko)

Télécharger
Formats alternatifs: ZIP







Documents similaires


struct td0
programme
intro cs poo
code suiveur avec uno et schield r3
code du suiveur avec pro mini avec l293d
3 les structures en c