La machine Parrot, première partie
Signature : | Mis en ligne le : 03/02/2008
Catégorie(s) :
  • GNU/Linux Magazine
  • | Domaine :
    Commentez creative commons

    Retrouvez cet article dans : Linux Magazine 97

    Ce premier article sur Parrot a pour but de décrire la machine virtuelle qui servira de support au développement du projet Perl 6. Après quelques rappels sur les notions de base nécessaires pour appréhender les réalisations en langage d’assemblage, nous étudierons plus en détail les caractéristiques propres à cette plate-forme et à sa programmation.

    /img-articles/lm/97/cc-art-parrot/fig-1.jpg One bytecode to rule them all

    Le langage assembleur

    Le langage d’assemblage est le seul langage de programmation lisible par un homme et aussi proche que possible du langage machine. Le code étant exclusivement binaire, il est impossible à un être humain de l’approcher en l’état. Ce sera donc par l’intermédiaire de mnémoniques et de symboles spécifiques qu’il sera rendu abordable. La particularité est que, contrairement à ce qui se passe dans un langage évolué, il existe une correspondance biunivoque entre les lignes du programme source et les instructions de la machine. Un programme spécifique, l’assembleur, transformera le code source en son équivalent binaire. Afin d’en faciliter l’utilisation, il autorise la gestion d’adresses symboliques, la manipulation de modules et la définition de macros. La structure syntaxique du langage d’assemblage est intimement liée à l’architecture du support. Ainsi, contrairement aux langages évolués, il n’en existe pas d’universel multiplateforme et une connaissance approfondie de la topologie de la machine est fondamentale pour le maîtriser et bien en comprendre l’utilisation. De cette constatation est né le principe de la machine virtuelle. Au début des années 1970, Niklaus Wirth de l’École Polytechnique de Zurich et initiateur de Pascal a défini pour son nouveau langage un interpréteur logiciel capable de s’exécuter sur différents systèmes, la P-machine [Pascal]. Cette même idée a été reprise chez Sun dans les années 1990 pour le développement de Java.

    Genèse de Parrot

    La machine virtuelle Parrot

    Parrot est une machine virtuelle développée par la communauté des programmeurs Perl. Elle servira, entre autres, de support au futur langage Perl6. Contrairement à la Java machine qui est construite autour d’une structure de pile, Parrot est une machine à base de registres. Cette conception influe particulièrement sur la programmation des expressions arithmétiques en les rendant plus lisibles. Dans une machine à pile, ce sont les éléments supérieurs qui représentent les opérateurs implicites de toute opération. Il est donc nécessaire de convertir une expression arithmétique en notation polonaise post-fixée avant d’effectuer un calcul [Lukasiewicz]. Méthode utilisée dans les années 1970 sur les anciennes calculatrices Hewlett-Packard [HP 35]. Dans ces conditions, (A * X * X ) + (B * X) + C doit être transformé en AXX**BX*C++ pour pouvoir être évalué, alors que, sur une machine registre, il suffit de spécifier explicitement les sources et la destination. Les développeurs ont fait en sorte que la machine virtuelle soit aussi proche que possible d’une machine réelle afin de pouvoir mettre en œuvre tout ce que la technique apporte en termes d’optimisation de code généré. En fait, à l’origine, ce projet était une avrilologie.

    Le poisson d’avril

    Le premier avril 2001, Simon Cozens annonce sur le site de O’Reilly le livre Programming Parrot [Parrot]. Cet ouvrage présente la programmation Parrot comme étant la référence ultime des nouveaux langages de programmation dynamique. Parrot, un langage qui, in fine, fusionnera les forces vives des jumeaux de l’open source Perl et Python. En unissant la flexibilité de Perl avec la simplicité d’exploitation de Python, Parrot deviendra le premier langage de développement du vingt et unième siècle. C’est l’annonce officielle que tous les utilisateurs de Perl et Python attendaient, le point culminant d’un an de collaboration entre Larry Wall et Guido van Rossum, les développeurs de Perl et de Python. À l’appui, la jaquette du livre qu’ils ont écrit en commun et l’interview exclusive de Larry et Guido qui évoquent cette excitante perspective. Extrait :
    What about the name of the new language? GvR : Well, that was my idea. We went over lots of possible names: Chimera, Pylon, Perth, before finally coming up with Parrot. We had a few basic ideas: we wanted it to begin with “P”; it had to be something that wouldn’t sound stupid on the end of /usr/bin/. LW : We also wanted the name of an animal, to represent the combination of the camel and the python. It also helps with the book covers... GvR : Eventually, I came up with Parrot after thinking about Monty Python’s finest hour, the Parrot sketch. LW : It just sounded right – dynamic, colourful, exotic. I love it!
    C’est ainsi que le poisson d’avril est en fin de compte devenu réalité sous forme de perroquet [Monty Python]

    Premiers pas en Parrot

    /img-articles/lm/97/cc-art-parrot/fig-2.jpg Le livre de référence

    Documentation

    Il n’existe qu’un livre sur ce sujet. Il est disponible chez O’Reilly et a été écrit en 2003 par Allison Randal, Dan Sugalski et Léopold Tötsch. Il détaille plusieurs aspects du projet, et, bien que ne reflétant pas tout à fait son état de développement actuel, c’est un bon ouvrage pour commencer à aborder la question.

    Installation

    Le site de référence du projet est <http://www.parrotcode.org/>. On peut y trouver la documentation en ligne et télécharger la dernière version Unix de la machine virtuelle http://svn.perl.org/snapshots/parrot/parrot-latest.tar.gz L’installation par elle-même ne pose aucun problème majeur. Téléchargez le fichier parrot-latest.tar.gz et décomprimez la distribution dans le répertoire parrot/.

    • configurez l’installation : perl Configure.pl ;
    • lancez la compilation : make ;
    • testez le résultat : make test ;
    • installez l’application : make install.

    Une version Windows précompilée est disponible sur le site de Jonathan Worthington [JWS]. Il ne faut jamais oublier que Parrot évoluant continuellement, certaines fonctions peuvent se transformer. Pour se maintenir informé des changements, il est bon de consulter régulièrement le site et la documentation en ligne http://www.parrotcode.org/docs/ Tous les exemples de cet article ont été testés sur un système Mac OS X.

    Principe de fonctionnement

    Le système Parrot est structuré en plusieurs éléments fondamentaux destinés chacun à une tâche bien spécifique.

    /img-articles/lm/97/cc-art-parrot/fig-3.jpg

    Tout commence avec un programme source qui sera transmis à l’analyseur afin d’en vérifier la syntaxe. Il sera par la suite converti en une structure que le compilateur pourra manipuler avec plus de facilité. À la sortie de l’analyseur, le programme source aura été transformé en un Arbre Syntaxique Abstrait (AST, Abstract Syntax Tree). C’est une configuration un peu sommaire constituée d’une suite de jetons. Un jeton (token) est un terme générique pour désigner un terminal qui sera ultérieurement pris en compte par l’analyseur syntaxique. Par exemple, une expression arithmétique de type (B * X) + C serait représentée sous la forme (Terme opérateur Terme) opérateur Terme. Au cours de cette étape sont aussi construites les tables permettant d’associer les adresses réelles et les adresses symboliques. Le source ainsi transformé sera pris en compte par le compilateur, et le byte code, qui pourra être directement transmis à la machine pour être exécuté, sera généré. Pour terminer, l’interpréteur exécutera le byte code et fournira les résultats attendus. Parrot propose aussi de créer un fichier à partir du byte code et de le stocker sur disque sous forme figée. Il peut ainsi être soumis à l’interpréteur sans repasser par les étapes d’analyse et de compilation. Cette option permettra la création et le catalogage de bibliothèques pré-compilées que l’on pourra lier au code principal grâce à l’optimiseur.

    La machine virtuelle

    La structure de la machine virtuelle est extrêmement simple. On ne distingue que quatre types de données, les entiers, les flottants, les chaînes de caractères et les PMC. À chaque type, sera associé un jeu de 32 registres. On disposera donc de :

    • 32 registres IV (entiers) (I0 .. I31)
    • 32 registres NV (flottants) (N0 .. N31)
    • 32 registres STRING (chaînes de caractères) (S0 .. S31)
    • 32 registres PMC (Parrot Magic Cookie) (P0 .. P31)

    Si les trois premières appellations ne présentent aucune difficulté pour comprendre à quel type de données elles font référence, leur nom parlant par lui-même, le dernier mérite quelques explications supplémentaires sur lesquelles nous reviendrons ultérieurement. PMC signifie Parrot Magic Cookies.

    Les piles

    La machine dispose de sept piles distinctes. Chacun des quatre jeux de registres possède sa propre pile qui va permettre de sauvegarder et restaurer aussi rapidement que possible leur contenu. Dans ce cas, les opérations ne portent pas sur un registre particulier, mais sur la famille concernée afin d’empiler ou de dépiler en une seule instruction les 32 registres. L’utilité première de ce mécanisme est de procéder à une sauvegarde très rapide lorsque cette opération s’avère nécessaire, lors des appels de fonction par exemple. Une pile spécifique est dédiée à la sauvegarde et à la restauration des entiers utilisés intensivement par les expressions régulières. Une pile garde la trace de toutes les informations de contrôle des gestionnaires d’exceptions. En fin de compte, on dispose d’une pile banalisée qui va permettre le stockage des données individuelles. Elle sera utilisée pour répondre aux opérations nécessaires à un travail de programmation standard, car elle permet d’empiler ou de dépiler individuellement un registre, quel qu’il soit. Il est cependant important de noter que les informations sont typées, ce qui interdit, par exemple, d’empiler une valeur numérique pour le dépiler ultérieurement dans un registre chaîne.

    Les débuts en programmation Parrot

    Le premier programme

    Pour le réaliser, nous devons disposer d’un éditeur de texte et, selon une tradition désormais bien établie, le premier programme que nous allons écrire nous affichera le classique message de bienvenue " Bonjour. ". Une fois écrit, le source sera stocké dans un fichier dont l’extension doit impérativement être .pasm (Parrot assembly). Pour procéder à son exécution, il suffit d’ouvrir une fenêtre terminal, de se positionner dans le dossier qui contient le programme à exécuter et de le soumettre à l’interpréteur.

    Ce que nous venons d’écrire est, volontairement, quelque peu provocateur. Cela ne ressemble que de très loin à ce que l’on a l’habitude de voir lorsque l’on pratique l’assembleur. C’est l’avantage de Parrot, mettre à notre disposition un langage qui a toutes les caractéristiques d’un véritable assembleur sans en avoir les inconvénients. Reprenons le même programme mais, cette fois, en utilisant un registre pour mémoriser la chaîne de caractères que nous désirons imprimer. L’instruction set permet de stocker la valeur spécifiée dans le registre cible. Dans notre exemple, la donnée à manipuler étant une chaîne de caractères, elle doit être mémorisée dans l’un des 32 registres string, au hasard, S1.

    Syntaxe de l’assembleur

    Ceci nous amène à définir la syntaxe générale de l’assembleur. Elle est simple et se résume en quelques principes. Une instruction tient sur une ligne dont le format est constant. Si l’opération retourne un résultat, c’est le toujours premier argument qui représente la destination et, dans certains cas, il peut simultanément être une source et la destination. Les arguments sont indifféremment des registres ou des valeurs explicites, mais seules les sources peuvent être représentées par des constantes. L’étiquette permet d’identifier une ligne de code à laquelle d’autres instructions pourront faire référence (rupture de séquence, branchement à un sous-programme). Les caractères utilisables pour définir une étiquette sont :
    • les lettres majuscules et minuscules ;
    • les chiffres ;
    • le blanc souligné.
    L’identification d’une ligne se fait au moyen d’un nom d’étiquette suivi d’un deux points (:). La référence à une étiquette se fait par le nom de cette dernière, sans les deux points. Traditionnellement, et pour des raisons de clarté, on représente les étiquettes par des lettres majuscules. Toute ligne qui commence par un dièse (#) sera considérée comme un commentaire et sera ignorée au moment de l’assemblage. Il en est de même des marqueurs POD (Plain Old Documentation).

    La représentation des données

    Les constantes numériques

    Une constante entière précédée d’un signe (+) ou (-) sera considérée comme une valeur décimale. Les constantes binaires sont précédées de 0b ou 0B, alors que les constantes hexadécimales sont précédées de 0x ou 0X. Leur destination doit être un registre entier (I). Les constantes flottantes sont, elles aussi, signées, c’est-à-dire qu’elles peuvent être précédées d’un signe (+) ou (-). La notation scientifique permet une représentation sous forme de mantisse et d’exposant. L’exposant est précédé de la lettre e ou E. Son signe est optionnel. Leur destination doit être un registre flottant (N).

    Les chaînes de caractère

    La syntaxe des chaînes de caractères est identique à celle utilisée dans Perl. Une chaîne de caractères peut être encadrée par des doubles quotes (") pour activer le mécanisme de substitution ou par des simples quotes () pour le désactiver. Leur destination doit être un registre chaîne (S).

    Les registres

    Référence à un registre

    Un registre sera référencé par son type et par son numéro. Le type est toujours une lettre majuscule :
    • I pour un registre entier ;
    • N pour un registre flottant ;
    • S pour un registre chaîne de caractères ;
    • P pour un registre PMC.
    Le numéro est un nombre compris entre 0 et 31. Une instruction spécifique exchange permet d’intervertir la valeur de deux registres. Les registres doivent impérativement être de même type.

    Particularité des registres

    Les registres N et I contiennent une valeur alors que les registres S et P contiennent un pointeur. On comprend aisément que la recopie d’un registre N ou I porte sur la valeur qu’il avait préalablement mémorisée. Dans un premier temps, la valeur 33 est stockée dans le registre entier I0, puis, dans un second temps, elle est transférée du registre I0 dans le registre I1. À terme, les deux contiendront la valeur 33. Les registres chaînes (S) travaillent sur une référence. La recopie d’un registre dans un autre duplique la référence, et la nouvelle assignation au registre crée un nouveau pointeur vers une nouvelle constante, l’ancienne référence restant inchangée. En définitive, tout se passe donc comme si le transfert agissait sur une valeur et non sur une adresse. Nous verrons plus loin que la même opération sur les registres PMC produit un résultat fondamentalement différent.

    Manipulation des données

    Travail sur les valeurs numériques

    Voici un programme simple mettant en évidence quelques-unes des opérations arithmétiques et des fonctions de base disponibles. Cette liste est, bien entendu, non exhaustive.

    Travail sur les chaînes de caractères

    À l’instar de Perl, la machine Parrot propose de nombreux outils pour permettre la manipulation des chaînes de caractère. Dans la majorité des cas, les opérations concernées génèrent de nouvelles chaînes dans le registre destination. Il est toutefois possible, dans certaines conditions, de faire porter la modification sur la source elle-même. Par exemple, l’instruction de concaténation concat peut avoir deux ou trois paramètres. Les arguments source peuvent indifféremment être des registres string ou des chaînes explicites. La variante à trois arguments (Si, Sj, Sk) ajoute la chaîne Sk à la fin de la chaîne Sj et stocke le résultat dans Si. Dans la variante à deux arguments, c’est le premier qui joue à la fois le rôle de source et de destination. L’instruction substr va permettre d’extraire une sous-chaîne. Dans sa version la plus simple, elle comporte 4 arguments : Un registre string (Sb) contient une chaîne de référence. Les valeurs numériques sont stockées dans des registres entiers (I). Ce sont l’indice du premier caractère de la sous-chaîne à extraire (Ic), et sa longueur (Id). Le résultat de l’opération est rangé dans un registre string (Sa). Comme dans toutes les instructions Parrot, on peut indifféremment utiliser comme sources des références à des registres ou des valeurs explicites. Si l’indice du premier caractère est une valeur négative, le décompte se fera à partir de la fin de la chaîne, le dernier caractère ayant la position -1. L’instruction substr, comme la fonction Perl de même nom, peut avoir plusieurs comportements distincts. Une forme plus développée comporte 5 arguments. Dans cette configuration, le cinquième argument représente la valeur alternative par laquelle sera remplacée la sous-chaîne qui vient d’être sélectionnée. La modification porte sur le second argument et la chaîne qui a été supprimée se retrouve dans le registre de destination. La valeur de remplacement peut être vide. Dans ce cas, la sous-chaîne concernée est tout simplement supprimée. La même opération peut aussi être réalisée sans procéder à la capture de la sous-chaîne. Dans ce cas, le registre destination n’apparaît pas. L’instruction length nous permet de connaître la longueur d’une chaîne. Comme en Perl, il existe une instruction de multiplication de chaîne : son code est repeat.

    Suppression de caractères

    L’instruction chopn permet de retirer un certain nombre de caractères à la fin d’une chaîne de donnée. Elle nous sera, entre autres, très utile pour supprimer les \n lors d’un accès à STDIN. Dans sa forme de base, elle comporte deux arguments, la chaîne de référence et le nombre de caractères à retirer. Si le nombre spécifié dans l’instruction chopn est négatif, il n’indique pas le nombre de caractères que l’on souhaite retirer à la fin, mais plutôt combien on veut en conserver au début. Il est important de noter que pour ces deux variantes, la modification porte sur la source elle-même. Une variante à trois arguments permet de conserver intacte la chaîne d’origine. Le résultat de l’opération est alors stocké dans un registre explicitement spécifié.

    Indexation d’une chaîne

    L’instruction index a les mêmes fonctionnalités que son homonyme en Perl. Elle permet de repérer l’emplacement dans une chaîne de référence d’une sous-chaîne donnée. Si cette dernière n’apparaît pas, la valeur retournée sera -1.

    Conversion de caractères

    On dispose de deux opérations qui convertissent une valeur numérique en caractère et inversement. L’instruction chr qui a comme registre destination un registre S et comme source un registre I, transforme l’entier représentatif d’un code ASCII en son caractère correspondant. L’instruction ord effectue l’opération inverse. Une variante à trois arguments de l’instruction ord permet de désigner un caractère particulier dans une chaîne par l’intermédiaire d’un entier. L’emplacement est compté à partir du début en commençant à zéro si le nombre est positif. Il est compté en partant de la fin et en commençant à un si le nombre est négatif.

    Changement de type

    Le simple fait de transférer une valeur d’un registre vers un autre effectue le changement de type de celui correspondant au registre d’origine vers celui correspondant au registre de destination.

    Les Parrot Magic Cookies

    Qu’est ce qu’un PMC ?

    Un PMC définit un type qui se comporte de manière particulière. Il va utiliser une structure particulière appelée " v-table " pour référencer des méthodes spécifiques. De plus, des fonctions appropriées permettent de remplacer l’implémentation de la classe de base par une séquence définie par l’utilisateur. Pour simplifier, un registre PMC contient un pointeur vers la v-table qui est elle-même une liste de pointeurs vers des fonctions dont le code réalise l’opération voulue pour le PMC concerné. Ainsi, toute instruction qui fait référence à un PMC, utilise la v-table qui lui a été associée pour accéder à la fonction appropriée. Essentiellement, les PMC héritent d’une classe de base et exécutent les opérations demandées en accord avec les caractéristiques spécifiques inhérentes aux structures concernées. Nous détaillerons tout ceci lorsque nous aborderons la programmation objet. Par exemple, pour deux langages distincts, une fonction équivalente peut occasionner deux comportements fondamentalement différents. Pour illustrer ceci, considérons deux programmes équivalents, le premier écrit en Perl, le second en Python et comparons les résultats obtenus. C’est pour tenir compte de cette singularité, que l’assembleur disposera à terme de deux PMC .PerlString et .PythonString afin de calquer le comportement des chaînes de caractères sur le comportement du langage qui devra être compilé. À noter, par ailleurs, que les PMC ne se limitent pas aux méthodes qui exécutent des opérations spécifiques. Ils sont utilisés chaque fois que le comportement d’un objet ou d’une construction diffère en fonction de l’environnement qui l’utilise. D’un autre côté, les PMC permettent de travailler sur les structures de données, qui portent ici le nom d’" agrégats ".

    Quels sont les PMC disponibles ?

    Le nombre de PMC installés peut varier d’une version à l’autre. En connaître la liste peut se révéler utile. Voici un petit programme qui permet de la générer. Dans la version utilisée pour les tests, son exécution, dans la version actuelle, génère 81 lignes, correspondant aux 81 PMC existants. C’est pourquoi j’en ai volontairement tronqué les résultats.

    Manipulation des registres PMC

    Il a été précisé que les registres PMC stockent un pointeur. C’est ainsi que le code opération new crée une instance de la classe sollicitée. La v-table de la classe en question permet de spécifier comment le PMC pointé par le registre concerné doit réaliser une opération spécifique qui le concerne. Considérons le programme ci-dessous. Après la création de l’instance .String dans le registre P1, la première instruction set fait appel à la méthode set_string_native (référencée en P1) qui affecte la chaîne "Chaine un\n" au PMC en question. L’instruction set P2, P1 se contente de recopier le pointeur contenu en P1 vers P2. C’est ainsi que les deux registres pointent sur la même structure. Dans ces conditions, l’affectation d’une nouvelle valeur à P2 affecte le PMC référencé à la fois par P1 et par P2. Si on désire réellement dupliquer le PMC, c’est-à-dire créer une nouvelle instance d’un PMC donné, il est nécessaire de le cloner. On dispose pour réaliser cette opération de l’instruction clone P2, P1. Nous pouvons reprendre le programme ci-dessus en tenant compte de cette remarque.

    Les entrées sorties

    Lecture d’information

    La lecture d’information à partir du clavier se fait sous la forme d’une chaîne de caractères. On dispose de deux instructions différentes pour effectuer cette opération. La première ne fait pas appel à un PMC : Elle effectue la lecture d’une chaîne de caractères dans le registre S1, la longueur maximum de la chaîne est spécifiée par le nombre indiqué en I1. Il existe une autre possibilité pour accéder à une information. C’est l’instruction readline qui attend comme argument un PMC. Si ce dernier est getstdin, c’est l’entrée standard qui sera concernée. L’opération réalisée est la lecture d’une ligne dans un registre chaîne, la ligne étant terminée par un "\n". Si besoin est, ce dernier peut être retiré au moyen de l’instruction chopn. Nous verrons ultérieurement que le PMC peut aussi contenir la référence d’un gestionnaire de fichier.

    Sortie d’information

    L’instruction print dont nous avons jusqu’à présent utilisé la version simple, peut, elle aussi, passer par l’intermédiaire du PMC getstdout. L’utilisation de PMC pour effectuer les entrées sorties se justifie pleinement lorsqu’on désire pouvoir changer facilement de gestionnaire d’entrée. Cette possibilité sera, elle aussi, développée dans la partie consacrée aux fichiers.

    Conclusion

    Cette approche du langage machine peut sembler quelque peu surprenante à ceux qui utilisent de manière courante un assembleur. Elle a toutefois un grand mérite, celui de permettre l’écriture facile et rapide de programmes qui auraient demandé beaucoup de conception et de travail sur un support réel. La caractéristique principale est de mettre à la disposition des utilisateurs un jeu d’instruction extrêmement varié et particulièrement développé. Cet aspect du problème a été mis en évidence dans cette première présentation. Par la suite, nous en détaillerons d’autres caractéristiques et nous verrons comment gérer sans difficulté des structures plus complexes. Références
    Vous souhaitez commenter cet article ?
    Brèves Flux RSS
    Édito : GNU/Linux Magazine 149
    Édito : GNU/Linux Magazine HS N°60
    Édito : Misc 61
    Édito : Linux Pratique 71
    Édito : Linux Essentiel N°25
    Communication RSS Com. RSS Presse
    Lancement de la plateforme de vente en ligne de PDF des Éditions Diamond ! Un...
    Misc N°61 – Communiqué de presse
    GNU/Linux Magazine N°149 – Communiqué de presse
    GNU/Linux Magazine HS N°60 – Communiqué de presse
    Linux Pratique N°71 – Communiqué de presse
    prochainement moteur de recherches des articles
     
    :
    :
    Jours heures minutes secondes
    En kiosque Flux RSS

    Le tout nouveau GNU/Linux Magazine est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Pratique est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau GNU/Linux Magazine HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Essentiel est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...