IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Cours complet Pharo par l'exemple


précédentsommairesuivant

3. Chapitre 3 - Un résumé de la syntaxe

Pharo, comme la plupart des dialectes modernes de Smalltalk, adopte une syntaxe proche de celle de Smalltalk-80. La syntaxe est conçue de telle sorte que le texte d’un programme lu à haute voix ressemble à de l’English pidgin ou « anglais simplifié » :

 
Sélectionnez
(Smalltalk hasClassNamed: 'Class') ifTrue: [ 'classe' ] ifFalse: [ 'pas classe']

La syntaxe de Pharo (c.-à-d. les expressions) est minimaliste ; pour l’essentiel conçue uniquement pour envoyer des messages. Les expressions sont construites à partir d’un nombre très réduit de primitives. Smalltalk dispose seulement de 6 mots-clefs et d’aucune syntaxe pour les structures de contrôle ni pour les déclarations de nouvelles classes. En revanche, tout ou presque est réalisable en envoyant des messages à des objets. Par exemple, à la place de la structure de contrôle conditionnelle si-alors-sinon, Smalltalk envoie des messages comme ifTrue: à des objets de la classe Boolean. Les nouvelles (sous-)classes sont créées en envoyant un message à leur superclasse.

3-1. Les éléments syntaxiques

Les expressions sont composées des blocs constructeurs suivants :

  1. six mots-clefs réservés ou pseudovariables : self, super, nil, true, false et thisContext ;
  2. des expressions constantes pour des objets littéraux comprenant les nombres, les caractères, les chaînes de caractères, les symboles et les tableaux ;
  3. des déclarations de variables ;
  4. des affectations ;
  5. des blocs ou fermetures lexicales – block closures en anglais – et ;
  6. des messages.
TABLE 3.1 – Résumé de la syntaxe de Pharo

expression

ce qu’elle représente

startPoint

un nom de variable

Transcript

un nom de variable globale

self

une pseudovariable

1

un entier décimal

2r101

un entier binaire

1.5

un nombre flottant

2.4e7

une notation exponentielle

$a

le caractère ‘a'

'Bonjour'

la chaîne « Bonjour »

#Bonjour

le symbole #Bonjour

#(1 2 3)

un tableau de littéraux

{1. 2. 1+2}

un tableau dynamique

"c’est mon commentaire"

un commentaire

| x y |

une déclaration de 2 variables x et y

x := 1

l’affectation de la valeur 1 à la variable x

[ x + y ]

un bloc qui évalue x+y

<primitive: 1>

une primitive de la VM(26) ou annotation

3 factorial

un message unaire

3 + 4

un message binaire

2 raisedTo: 6 modulo: 10

un message à mots-clefs

↑ true

Le renvoi de la valeur true pour vrai

Transcript show: 'bonjour'. Transcript cr

un séparateur d’expression (.)

Transcript show: 'bonjour'; cr

un message en cascade (;)

Dans la table 3.1, nous pouvons voir des exemples divers d’éléments syntaxiques.

Les variables locales. startPoint est un nom de variable ou identifiant. Par convention, les identifiants sont composés de mots au format d’écriture dit casse de chameau (« camelCase ») : chaque mot excepté le premier débute par une lettre majuscule. La première lettre d’une variable d’instance, d’une méthode ou d’un bloc argument ou d’une variable temporaire doit être en minuscule. Ce qui indique au lecteur que la portée de la variable est privée.

Les variables partagées. Les identifiants qui débutent par une lettre majuscule sont des variables globales, des variables de classes, des dictionnaires de pool ou des noms de classes. Transcript est une variable globale, une instance de la classe TranscriptStream.

Le receveur. self est un mot-clef qui pointe vers l’objet sur lequel la méthode courante s’exécute. Nous le nommons « le receveur », car cet objet devra normalement recevoir le message qui provoque l’exécution de la méthode. self est appelé une « pseudovariable » puisque nous ne pouvons rien lui affecter.

Les entiers. En plus des entiers décimaux habituels comme 42, Pharo propose aussi une notation en base numérique ou radix. 2r101 est 101 en base 2 (c.-à-d. en binaire), qui est égal à l’entier décimal 5.

Les nombres flottants. Ils peuvent être spécifiés avec leur exposant en base dix : 2.4e7 est 2.× 107.

Les caractères. Un signe dollar définit un caractère : $a est le littéral pour ‘a'. Des instances de caractères non imprimables peuvent être obtenues en envoyant des messages ad hoc à la classe Character, tels que Character space et Character tab.

Les chaînes de caractères. Les apostrophes sont utilisées pour définir un littéral chaîne. Si vous désirez qu’une chaîne comporte une apostrophe, il suffira de doubler l’apostrophe, comme dans 'aujourd''hui'.

Les symboles. Ils ressemblent à des chaînes de caractères, en ce sens qu’ils comportent une suite de caractères. Mais contrairement à une chaîne, un symbole doit être globalement unique. Il y a seulement un objet symbole #Bonjour mais il peut y avoir plusieurs objets chaînes de caractères ayant la valeur 'Bonjour'.

Les tableaux définis à la compilation. Ils sont définis par #( ), les objets littéraux sont séparés par des espaces. À l’intérieur des parenthèses, tout doit être constant durant la compilation. Par exemple, #(27 (true false) abc)(27) est un tableau littéral de trois éléments : l’entier 27, le tableau à la compilation contenant deux booléens et le symbole #abc.

Les tableaux définis à l’exécution. Les accolades { } définissent un tableau (dynamique) à l’exécution. Ses éléments sont des expressions séparées par des points. Ainsi { 1. 2. 1+2 } définit un tableau dont les éléments sont 1, 2 et le résultat de l’évaluation de 1+2 (dans le monde de Smalltalk, la notation entre accolades est particulière aux dialectes Pharo et Squeak. Dans d’autres Smalltalks vous devez explicitement construire des tableaux dynamiques).

Les commentaires. Ils sont encadrés par des guillemets. « Bonjour le commentaire » est un commentaire et non une chaîne ; donc il est ignoré par le compilateur de Pharo. Les commentaires peuvent se répartir sur plusieurs lignes.

Les définitions des variables locales. Des barres verticales | | limitent les déclarations d’une ou plusieurs variables locales dans une méthode (ainsi que dans un bloc).

L’affectation. := affecte un objet à une variable.

Les blocs. Des crochets [ ] définissent un bloc, aussi connu sous le nom de block closure ou fermeture lexicale, laquelle est un objet à part entière représentant une fonction. Comme nous le verrons, les blocs peuvent avoir des arguments et des variables locales.

Les primitives. <primitive: ...> marque l’invocation d’une primitive de la VM ou machine virtuelle (<primitive: 1> est la primitive de SmallInteger»+). Tout code suivant la primitive est exécuté seulement si la primitive échoue. La même syntaxe est aussi employée pour des annotations de méthodes.

Les messages unaires. Ce sont de simples mots (comme factorial) envoyés à un receveur (comme 3).

Les messages binaires. Ce sont des opérateurs (comme +) envoyés à un receveur et ayant un seul argument. Dans 3+4, le receveur est 3 et l’argument est 4.

Les messages à mots-clefs. Ce sont des mots-clefs multiples (comme raisedTo:modulo:), chacun se terminant par un deux-points (:) et ayant un seul argument. Dans l’expression 2 raisedTo: 6 modulo: 10, le sélecteur de message raisedTo:modulo: prend les deux arguments 6 et 10, chacun suivant le :. Nous envoyons le message au receveur 2.

Le retour d’une méthode. ↑ est employé pour obtenir le retour ou renvoi d’une méthode. Il vous faut taper ^ pour obtenir le caractère ↑.

Les séquences d’instructions. Un point (.) est le séparateur d’instructions. Placer un point entre deux expressions les transforme en deux instructions indépendantes.

Les cascades. Un point virgule peut être utilisé pour envoyer une cascade de messages à un receveur unique. Dans Transcript show: 'bonjour'; cr, nous envoyons d’abord le message à mots-clefs show: 'bonjour' au receveur Transcript, puis nous envoyons au même receveur le message unaire cr.

Les classes Number, Character, String et Boolean sont décrites avec plus de détails dans le chapitre 8.

3-2. Les pseudovariables

Dans Smalltalk, il y a 6 mots-clefs réservés ou pseudovariables : nil, true, false, self, super et thisContext. Ils sont appelés pseudovariables car ils sont prédéfinis et ne peuvent pas être l’objet d’une affectation. true, false et nil sont des constantes tandis que les valeurs de self, super et de thisContext varient de façon dynamique lorsque le code est exécuté.

true et false sont les uniques instances des classes Boolean : True et False(voir le chapitre 8 pour plus de détails).

self se réfère toujours au receveur de la méthode en cours d’exécution. super se réfère aussi au receveur de la méthode en cours, mais quand vous envoyez un message à super, la recherche de méthode change en démarrant de la super-classe relative à la classe contenant la méthode qui utilise super (pour plus de détails, voyez le chapitre 5).

nil est l’objet non défini. C’est l’unique instance de la classe UndefinedObject. Les variables d’instance, les variables de classe et les variables locales sont initialisées à nil.

thisContext est une pseudovariable qui représente la structure du sommet de la pile d’exécution. En d’autres termes, il représente le MethodContext ou le BlockClosure en cours d’exécution. En temps normal, thisContext ne doit pas intéresser la plupart des programmeurs, mais il est essentiel pour implémenter des outils de développement tels que le débogueur et il est aussi utilisé pour gérer exceptions et continuations.

3-3. Les envois de messages

Il y a trois types de messages dans Pharo.

  1. Les messages unaires : messages sans argument. 1 factorial envoie le message factorial à l’objet 1.
  2. Les messages binaires : messages avec un seul argument. 1 + 2 envoie le message + avec l’argument 2 à l’objet 1.
  3. Les messages à mots-clefs : messages qui comportent un nombre arbitraire d’arguments. 2 raisedTo: 6 modulo: 10 envoie le message comprenant le sélecteur raisedTo:modulo: et les arguments 6 et 10 vers l’objet 2.

Les sélecteurs des messages unaires sont constitués de caractères alphanumériques et débutent par une lettre minuscule.

Les sélecteurs des messages binaires sont constitués par un ou plusieurs caractères de l’ensemble suivant :

 
Sélectionnez
+ - / \ *< > = @ % | & ? ,

Les sélecteurs des messages à mots-clefs sont formés d’une suite de mots-clefs alphanumériques qui commencent par une lettre minuscule et se terminent par :.

Les messages unaires ont la plus haute priorité, puis viennent les messages binaires et, pour finir, les messages à mots-clefs ; ainsi :

 
Sélectionnez
2 raisedTo: 1 + 3 factorial −→ 128

D’abord nous envoyons factorial à 3, puis nous envoyons + 6 à 1, et pour finir, nous envoyons raisedTo: 7 à 2. Rappelons que nous utilisons la notation expression −→ result pour montrer le résultat de l’évaluation d’une expression.

Priorité mise à part, l’évaluation s’effectue strictement de la gauche vers la droite, donc :

 
Sélectionnez
1 + 2 * 3 −→ 9

et non 7. Les parenthèses permettent de modifier l’ordre d’une évaluation :

 
Sélectionnez
1 + (2 * 3) −→ 7

Les envois de message peuvent être composés grâce à des points et des points-virgules. Une suite d’expressions séparées par des points provoque l’évaluation de chaque expression dans la suite comme une instruction, l’une après l’autre.

 
Sélectionnez
Transcript cr.
Transcript show: 'Bonjour le monde'.
Transcript cr

Ce code enverra cr à l’objet Transcript, puis enverra show: 'Bonjour le monde', et enfin enverra un nouveau cr.

Quand une succession de messages doit être envoyée à un même receveur, ou pour dire les choses plus succinctement en cascade, le receveur est spécifié une seule fois et la suite des messages est séparée par des points-virgules :

 
Sélectionnez
Transcript cr;
  show: 'Bonjour le monde';
  cr

Ce code a précisément le même effet que celui de l’exemple précédent.

3-4. Syntaxe relative aux méthodes

Bien que les expressions puissent être évaluées n’importe où dans Pharo (par exemple, dans un espace de travail [Workspace], dans un débogueur [Debugger] ou dans un navigateur de classes), les méthodes sont en principe définies dans une fenêtre du Browser ou du débogueur ; les méthodes peuvent aussi être rentrées depuis une source externe, mais ce n’est pas une façon habituelle de programmer en Pharo).

Les programmes sont développés, une méthode à la fois, dans l’environnement d’une classe précise (une classe est définie en envoyant un message à une classe existante, en demandant de créer une sous-classe, de sorte qu’il n’y ait pas de syntaxe spécifique pour créer une classe).

Voilà la méthode lineCount (pour compter le nombre de lignes) dans la classe String. La convention habituelle consiste à se référer aux méthodes comme suit : ClassName»methodName ; ainsi nous nommerons cette méthode String»lineCount (28).

Méthode 3.1 – Compteur de lignes
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
String»lineCount
  "Answer the number of lines represented by the receiver, where ever y cr adds one line."
  | cr count |
  cr := Character cr.
  count := 1 min: self size.
  self do:
    [:c | c == cr ifTrue: [count := count + 1]].
  ↑ count

Sur le plan de la syntaxe, une méthode comporte :

  1. la structure de la méthode avec le nom (c.-à-d. lineCount) et tous les arguments (aucun dans cet exemple) ;
  2. les commentaires (qui peuvent être placés n’importe où, mais conventionnellement, un commentaire doit être placé au début afin d’expliquer le but de la méthode) ;
  3. les déclarations des variables locales (c.-à-d. cr et count) ;
  4. un nombre quelconque d’expressions séparées par des points ; dans notre exemple, il y en a quatre.

L’évaluation de n’importe quelle expression précédée par un ↑ (saisi en tapant ^) provoquera l’arrêt de la méthode à cet endroit, donnant en retour la valeur de cette expression. Une méthode qui se termine sans retourner explicitement une expression retournera de façon implicite self.

Les arguments et les variables locales doivent toujours débuter par une lettre minuscule. Les noms débutant par une majuscule sont réservés aux variables globales. Les noms des classes, comme Character, sont tout simplement des variables globales qui se réfèrent à l’objet représentant cette classe.

3-5. La syntaxe des blocs

Les blocs apportent un moyen de différer l’évaluation d’une expression. Un bloc est essentiellement une fonction anonyme. Un bloc est évalué en lui envoyant le message value. Le bloc renvoie la valeur de la dernière expression de son corps, à moins qu’il y ait un retour explicite (avec ↑), auquel cas il ne renvoie aucune valeur.

 
Sélectionnez
[ 1 + 2 ] value −→ 3

Les blocs peuvent prendre des paramètres, chacun doit être déclaré en le précédant d’un deux-points. Une barre verticale sépare les déclarations des paramètres et le corps du bloc. Pour évaluer un bloc avec un paramètre, vous devez lui envoyer le message value: avec un argument. Un bloc à deux paramètres doit recevoir value:value: ; et ainsi de suite, jusqu’à 4 arguments.

 
Sélectionnez
[ :x | 1 + x ] value: 2 −→ 3
[ :x :y | x + y ] value: 1 value: 2 −→ 3

Si vous avez un bloc comportant plus de quatre paramètres, vous devez utiliser valueWithArguments: et passer les arguments à l’aide d’un tableau (un bloc comportant un grand nombre de paramètres étant souvent révélateur d’un problème au niveau de sa conception).

Des blocs peuvent aussi déclarer des variables locales, lesquelles seront entourées par des barres verticales, tout comme des déclarations de variables locales dans une méthode. Les variables locales sont déclarées après les éventuels arguments :

 
Sélectionnez
[ :x :y | | z | z := x+ y. z ] value: 1 value: 2 −→ 3

Les blocs sont en fait des fermetures lexicales, puisqu’ils peuvent faire référence à des variables de leur environnement immédiat. Le bloc suivant fait référence à la variable x voisine :

 
Sélectionnez
| x |
x := 1.
[ :y | x + y ] value: 2 −→ 3

Les blocs sont des instances de la classe BlockClosure ; ce sont donc des objets, de sorte qu’ils puissent être affectés à des variables et être passés comme arguments à l’instar de tout autre objet.

3-6. Conditions et itérations

Smalltalk n’offre aucune syntaxe spécifique pour les structures de contrôle. Typiquement, celles-ci sont obtenues par l’envoi de messages à des booléens, des nombres ou des collections, avec pour arguments des blocs.

Les clauses conditionnelles sont obtenues par l’envoi des messages ifTrue:, ifFalse: ou ifTrue:ifFalse: au résultat d’une expression booléenne. Pour plus de détails sur les booléens, lisez le chapitre 8.

 
Sélectionnez
(17 * 13 > 220)
  ifTrue: [ 'plus grand' ]
  ifFalse: [ 'plus petit' ] −→ 'plus grand'

Les boucles (ou itérations) sont obtenues typiquement par l’envoi de messages à des blocs, des entiers ou des collections. Comme la condition de sortie d’une boucle peut être évaluée de façon répétitive, elle se présentera sous la forme d’un bloc plutôt que de celle d’une valeur booléenne. Voici précisément un exemple d’une boucle procédurale :

 
Sélectionnez
n := 1.
[ n < 1000 ] whileTrue: [ n := n*2 ].
n −→ 1024

whileFalse: inverse la condition de sortie.

 
Sélectionnez
n := 1.
[ n > 1000 ] whileFalse: [ n := n*2 ].
n −→ 1024

timesRepeat: offre un moyen simple pour implémenter un nombre donné d’itérations :

 
Sélectionnez
n := 1.
10 timesRepeat: [ n := n*2 ].
n −→ 1024

Nous pouvons aussi envoyer le message to:do: à un nombre qui deviendra alors la valeur initiale d’un compteur de boucle. Le premier argument est la borne supérieure ; le second est un bloc qui prend la valeur courante du compteur de boucle comme argument :

 
Sélectionnez
n := 0.
1 to: 10 do: [ :counter | n := n + counter ].
n −→ 55

Itérateurs d’ordre supérieur. Les collections comprennent un grand nombre de classes différentes dont beaucoup acceptent le même protocole. Les messages les plus importants pour itérer sur des collections sont do:, collect:, select:, reject:, detect: ainsi que inject:into:. Ces messages définissent des itérateurs d’ordre supérieur qui nous permettent d’écrire du code très compact.

Une instance Interval (c.-à-d. un intervalle) est une collection qui définit un itérateur sur une suite de nombres depuis un début jusqu’à une fin. 1 to: 10 représente l’intervalle de 1 à 10. Comme il s’agit d’une collection, nous pouvons lui envoyer le message do:. L’argument est un bloc qui est évalué pour chaque élément de la collection.

 
Sélectionnez
n := 0.
(1 to: 10) do: [ :element | n := n + element ].
n −→ 55

collect: construit une nouvelle collection de la même taille, en transformant chaque élément.

 
Sélectionnez
(1 to: 10) collect: [ :each | each * each ] −→ #(1 4 9 16 25 36 49 64 81 100)

select: et reject: construisent des collections nouvelles, contenant un sous-ensemble d’éléments satisfaisant (ou non) la condition du bloc booléen. detect: retourne le premier élément satisfaisant la condition. Ne perdez pas de vue que les chaînes sont aussi des collections, ainsi, vous pouvez itérer aussi sur tous les caractères. La méthode isVowel renvoie true (c.-à-d. vrai) lorsque le receveur-caractère est une voyelle(29).

 
Sélectionnez
'Bonjour Pharo' select: [ :char | char isVowel ] −→ 'oouao'
'Bonjour Pharo' reject: [ :char | char isVowel ] −→ 'Bnjr Phr'
'Bonjour Pharo' detect: [ :char | char isVowel ] −→ $o

Finalement, vous devez garder à l’esprit que les collections acceptent aussi l’équivalent de l’opérateur fold issu de la programmation fonctionnelle au travers de la méthode inject:into:. Cela vous amène à générer un résultat cumulatif utilisant une expression qui accepte une valeur initiale puis injecte chaque élément de la collection. Les sommes et les produits sont des exemples typiques.

 
Sélectionnez
(1 to: 10) inject: 0 into: [ :sum :each | sum + each ] −→ 55

Ce code est équivalent à 0+1+2+3+4+5+6+7+8+9+10.

Pour plus de détails sur les collections et les flux de données, rendez-vous dans les chapitres 9 et 10.

3-7. Primitives et Pragmas

En Smalltalk, tout est objet et tout se passe par l’envoi de messages. Néanmoins, à certains niveaux, ce modèle a ses limites ; le fonctionnement de certains objets ne peut être achevé qu’en invoquant la machine virtuelle et les primitives.

Par exemple, les comportements suivants sont tous implémentés sous la forme de primitives : l’allocation de la mémoire (new et new:), la manipulation de bits (bitAnd:, bitOr: et bitShift:), l’arithmétique des pointeurs et des entiers (+, -, <, >, *, /, =, ==…) et l’accès aux tableaux (at:, at:put:).

Les primitives sont invoquées avec la syntaxe <primitive: aNumber> (aNumber étant un nombre). Une méthode qui invoque une telle primitive peut aussi embarquer du code Smalltalk qui sera évalué seulement en cas d’échec de la primitive.

Examinons le code pour SmallInteger»+. Si la primitive échoue, l’expression super + aNumber sera évaluée et renvoyée(30).

Méthode 3.2 – Une méthode primitive
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
+ aNumber
"Primitive. Add the receiver to the argument and answer with the result
if it is a SmallInteger. Fail if the argument or the result is not a
SmallInteger Essential No Lookup. See Object documentation whatIsAPr imitive."

<primitive: 1>super + aNumber

Dans Pharo,la syntaxe avec <…> est aussi utilisée pour les annotations de méthode que l’on appelle des pragmas.

3-8. Résumé du chapitre

  • Pharo a (seulement) six mots réservés aussi appelés pseudovariables : true, false, nil, self, super et thisContext.
  • Il y a cinq types d’objets littéraux : les nombres (5, 2.5, 1.9e15, 2r111), les caractères ($a), les chaînes ('bonjour'), les symboles (#bonjour) et les tableaux (#('bonjour' #bonjour)).
  • Les chaînes sont délimitées par des apostrophes et les commentaires par des guillemets. Pour obtenir une apostrophe dans une chaîne, il suffit de la doubler.
  • Contrairement aux chaînes, les symboles sont par essence globalement uniques.
  • Employez #( ... ) pour définir un tableau littéral. Employez { ... } pour définir un tableau dynamique. Sachez que #( 1 + 2 ) size −→ 3, mais que { 1 + 2 } size −→ 1.
  • Il y a trois types de messages :

    • unaire : par ex., 1 asString, Array new ;
    • binaire : par ex., 3 + 4, 'salut' , ' Squeak' ;
    • à mots-clefs : par ex., 'salue' at: 5 put: $t.
  • Un envoi de messages en cascade est une suite de messages envoyés à la même cible, tous séparés par des ; : OrderedCollection new add: #albert; add: #einstein; size −→ 2.
  • Les variables locales sont déclarées à l’aide de barres verticales. Employez := pour les affectations. |x| x:=1.
  • Les expressions sont les messages envoyés, les cascades et les affectations ; parfois regroupées avec des parenthèses. Les instructions sont des expressions séparées par des points.
  • Les blocs ou fermetures lexicales sont des expressions limitées par des crochets. Les blocs peuvent prendre des arguments et peuvent contenir des variables locales dites aussi variables temporaires. Les expressions du bloc ne sont évaluées que lorsque vous envoyez un message de la forme value... avec le bon nombre d’arguments. [:x | x + 2] value: 4 −→ 6.
  • Il n’y a pas de syntaxe particulière pour les structures de contrôle ; ce ne sont que des messages qui, sous certaines conditions, évaluent des blocs.
 
Sélectionnez
(Smalltalk includes: Class) ifTrue: [ Transcript show: Class superclass ]

précédentsommairesuivant
VM est l’abrégé de « Virtual Machine » c.-à-d. « Machine Virtuelle ».
Notez que c’est la même chose que #(27 #(true false) #abc).
Le commentaire de la méthode dit : « Retourne le nombre de lignes représentées par le receveur, dans lequel chaque cr ajoute une ligne »
Note du traducteur : les voyelles accentuées ne sont pas considérées par défaut comme des voyelles ; Smalltalk-80 a le même défaut que la plupart des langages de programmation nés dans la culture anglo-saxonne.
Le commentaire de la méthode dit : « Ajoute le receveur à l’argument et renvoie le résultat en réponse s’il s’agit d’un entier de classe SmallInteger. Échoue si l’argument ou le résultat n’est pas un SmallInteger. Voir la documentation de la classe Object : whatIsPrimitive (qu’est-ce qu’une primitive). »

Licence Creative Commons
Le contenu de cet article est rédigé par Andrew Black et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.