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.

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

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.
coruscant:~/Langages/asmparrot chris$ parrot -V This is parrot version 0.4.10-devel built for ppc-darwin. Copyright (C) 2001-2007, The Perl Foundation.
Principe de fonctionnement
Le système Parrot est structuré en plusieurs éléments fondamentaux destinés chacun à une tâche bien spécifique.

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.
coruscant:~/Langages/asmparrot chris$ cat hello.pasm
print «Bonjour.\n»
end
coruscant:~/Langages/asmparrot chris$ parrot hello.pasm
Bonjour.
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat hello.pasm
set S1, «Bonjour.»
print S1
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot hello.pasm
Bonjour.
coruscant:~/Langages/asmparrot chris$
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.
[Etiquette] Code Opération Destinations, Source_1, ..., Source_n
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).
set I1, 42 # Constante entière positive. set I2, -68 # Constante entière négative. set I3, 0x2A # Constante hexadécimale. set I4, 0b1001 # Constante binaire. set I5, -0B1101 # Constante binaire négative.
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).
set N2, 1E6 # Constante en notation scientifique. set N3, -1.5E-2 # Constante en notation scientifique.
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).
set S1, «Chaine.\n» # Le mot : Chaine suivi d’un retour chariot. set S2, «\\» # un antislash. set S3, ‘L\’etau’ # La chaine : L’etau set S4, ‘a\n’ # La lettre a, suivie d’un antislash puis d’un n.
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 :
Ipour un registre entier ;Npour un registre flottant ;Spour un registre chaîne de caractères ;Ppour un registre PMC.
Le numéro est un nombre compris entre 0 et 31.
coruscant:~/Langages/asmparrot chris$ cat regs.pasm
# Stockage des caractères «Bonjour.» dans le registre chaîne 10.
set S10, «Bonjour.»
# Stockage de la valeur entière 2005 dans le registre entier 17.
set I17, 2005
# Stockage de la valeur réelle 3.14159 dans le registre flottant 20.
set N20, 3.14159
# Impression diverses valeurs précédemment mémorisées.
print S10
print «\n»
print I17
print “\n”
print N20
print “\n”
end
coruscant:~/Langages/asmparrot chris$ parrot regs.pasm
Bonjour.
2005
3.14159
coruscant:~/Langages/asmparrot chris$
Une instruction spécifique exchange permet d’intervertir la valeur de deux registres. Les registres doivent impérativement être de même type.
coruscant:~/Langages/asmparrot chris$ cat regs.pasm
set I10, 10
set I20, 20
print “Avant exchange \n”
print “Le registre 10 contient : “
print I10
print «\n»
print “Et le registre 20 contient : “
print I20
print «\n»
exchange I10, I20
print «Apres exchange \n»
print “Le registre 10 contient : “
print I10
print «\n»
print “Et le registre 20 contient : “
print I20
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot regs.pasm
Avant exchange
Le registre 10 contient : 10
Et le registre 20 contient : 20
Apres exchange
Le registre 10 contient : 20
Et le registre 20 contient : 10
coruscant:~/Langages/asmparrot chris$
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.
set I0, 33 # Mettre la valeur 33 dans le registre I0 set I1, I0 # Transférer le contenu du registre I0 dans le registre I1.
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.
coruscant:~/Langages/asmparrot chris$ cat ptrs.pasm
set S0, «Chaine initiale\n»
set S1, S0
set S0, «Nouvelle affectation\n»
print “Contenu du registre S0 : “
print S0
print “Contenu du registre S1 : “
print S1
end
coruscant:~/Langages/asmparrot chris$ parrot ptrs.pasm
Contenu du registre S0 : Nouvelle affectation
Contenu du registre S1 : Chaine initiale
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat arith.pasm
set N1, 50
set N2, 3
add N0, N1, N2
print “La somme de “
print N1
print “ et de “
print N2
print “ est egale a :”
print N0
print “\n”
mul N0, N1, N2
print “Le produit de “
print N1
print “ et de “
print N2
print “ est egal a :”
print N0
print “\n”
div N0, N1, N2
print “Le quotient de “
print N1
print “ par “
print N2
print “ est egal a :”
print N0
print “\n”
set N15, 99
inc N15
print “Incrementer “
print N15
print “ donne :”
print N15
print «\n»
set N12, -5.28547
abs N10, N12
print “La valeur absolue de “
print N12
print “ est egale a :”
print N10
print “\n”
set I20, 10
fact I22, I20
print “La factorielle de “
print I20
print “ est egale a :”
print I22
print «\n»
set N27, 3.14159
sin N8, N27
print “Le sinus de “
print N27
print “ est egal a :”
print N8
print “\n”
end
coruscant:~/Langages/asmparrot chris$ parrot arith.pasm
La somme de 50.000000 et de 3.000000 est egale a :53.000000
Le produit de 50.000000 et de 3.000000 est egal a :150.000000
Le quotient de 50.000000 par 3.000000 est egal a :16.666667
Incrementer 100.000000 donne :100.000000
La valeur absolue de -5.285470 est egale a :5.285470
La factorielle de 10 est egale a :3628800
Le sinus de 3.141590 est egal a :0.000003
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat conc.pasm
set S1, «Bonjour»
set S2, “ la Compagnie.”
concat S0, S1, S2 # Concaténer S1 avec S2, résultat dans S0.
print S0
print «\n»
concat S1, S2 # Concaténer S1 avec S2, résultat dans S1.
print S1
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot conc.pasm
Bonjour la Compagnie.
Bonjour la Compagnie.
coruscant:~/Langages/asmparrot chris$
L’instruction substr va permettre d’extraire une sous-chaîne. Dans sa version la plus simple, elle comporte 4 arguments :
substr Sa, Sb, Ic, Id
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).
coruscant:~/Langages/asmparrot chris$ cat sst.pasm set S1, «0123456789» set I1, 2 set I2, 5 substr S10, S1, I1, I2 print S10 print «\n» end coruscant:~/Langages/asmparrot chris$ parrot sst.pasm 23456 coruscant:~/Langages/asmparrot chris$
Comme dans toutes les instructions Parrot, on peut indifféremment utiliser comme sources des références à des registres ou des valeurs explicites.
substr S10, S1, 3, 5 substr S10, «012345», 3, I3
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.
coruscant:~/Langages/asmparrot chris$ cat sst1.pasm
set S0, «abcdef»
# Remplacer les deux caractères «cd» par la chaîne ----
substr S1, S0, 2, 2, «----»
print S1
print «\n»
print S0
print «\n»
# supprimer la chaîne «----»
substr S1, S0, 2, 4, «»
print S1
print «\n»
print S0
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot sst1.pasm
cd
ab----ef
----
abef
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
set S0, «abcdef»
substr S0, 2, 2, «----»
print S0
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
ab----ef
coruscant:~/Langages/asmparrot chris$
L’instruction length nous permet de connaître la longueur d’une chaîne.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
set S2, «0123456789»
length I1, S2
print “La chaine “
print S2
print “ comporte “
print I1
print “ caracteres.\n”
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
La chaine 0123456789 comporte 10 caracteres.
coruscant:~/Langages/asmparrot chris$
Comme en Perl, il existe une instruction de multiplication de chaîne : son code est repeat.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
set S0, «x0x-»
set I1, 5
repeat S1, S0, I1
print S1
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
x0x-x0x-x0x-x0x-x0x-
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
set S0, «abcdef»
chopn S0, 2
print S0
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
abcd
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
set S0, «abcdef»
chopn S0, -3
print S0
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
abc
coruscant:~/Langages/asmparrot chris$
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é.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
set S0, «abcdef»
# Retirer les deux derniers caractères.
chopn S1, S0, 2
print S1
print «\n»
# Conserver les trois premiers caractères.
chopn S2, S0, -3
print S2
print «\n»
# La chaîne origine n’a pas été modifiée.
print S0
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
abcd
abc
abcdef
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat ind.pasm
set S0, «abcdef»
index I0, S0, «cd»
print I0
print «\n»
index I1, S0, «gh»
print I1
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot ind.pasm
2
-1
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
chr S0, 65 # 65 est le code ASCII décimal de «A»
print S0
print «\n»
ord I0, «x» # Le code ASCII de «x» est 120 décimal.
print I0
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
A
120
coruscant:~/Langages/asmparot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
ord I0, «ABCDEF», 2 # On convertit le caractère C.
print I0
print «\n»
ord I1, “ABCDEF”, -3 # On convertit le caractère D.
print I1
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
67
68
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
set S1, «5» # Caractère.
set S2, 10 # Caractère
set N1, S1 # Transfert vers un registre flottant
set N2, S2 # Transfert vers un registre flottant
mul N1, N2 # Calcul en flottant
print «Calcul en flottant :»
print N1
print «\n»
set I1, N1 # Conversion en entier
print «Conversion en entier :»
print I1
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot exo.pasm
Calcul en flottant :50.000000
Conversion en entier :50
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/perl chris$ cat ch.pl
#!/usr/bin/perl
$x = «ABCDEF»;
$z = ++$x;
print «$z\n»;
coruscant:~/Langages/perl chris$ ch.pl
ABCDEG
coruscant:~/Langages/python chris$ cat ch.py
#!/usr/bin/python
x = «ABCDEF»
z = ++x
print z, «\n»
coruscant:~/Langages/python chris$ ch.py
Traceback (most recent call last):
File «ess.py», line 4, in ?
z = ++x;
TypeError: bad operand type for unary +
coruscant:~/Langages/python chris$
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.
coruscant:~/Langages/asmparrot chris$ cat listePMC.pasm
set I0, 1
BOUCLE:
# Test si le numéro correspond à un type de PMC valide.
valid_type I1, I0
eq I1, 0, FIN
# Récupération du nom du PMC correspondant au numéro.
typeof S0, I0
# Impression des résultats.
set S1, I0
concat S1, « «, S1
substr S2, S1, -2
print «Nom du PMC numero «
print S2
print « : «
print S0
print «\n»
inc I0
branch BOUCLE
FIN:
end
coruscant:~/Langages/asmparrot chris$ parrot listePMC.pasm
Nom du PMC numero 1 : Null
Nom du PMC numero 2 : Env
Nom du PMC numero 3 : Key
Nom du PMC numero 4 : Random
* * *
* * *
Nom du PMC numero 66 : ParrotRunningThread
Nom du PMC numero 67 : PCCMETHOD_Test
Nom du PMC numero 68 : ResizableBooleanArray
Nom du PMC numero 69 : ResizableFloatArray
Nom du PMC numero 70 : ResizableIntegerArray
Nom du PMC numero 71 : ResizablePMCArray
Nom du PMC numero 72 : ResizableStringArray
* * *
Nom du PMC numero 79 : STMVar
Nom du PMC numero 80 : Super
Nom du PMC numero 81 : Undef
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat ptrs.pasm
new P1, .String
set P1, «Chaine un\n»
set P2, P1
set P2, “Chaine deux\n”
print “Le PMC P1 pointe sur la chaine : “
print P1
print “Le PMC P2 pointe sur la chaine : “
print P2
end
coruscant:~/Langages/asmparrot chris$ parrot ptrs.pasm
Le PMC P1 pointe sur la chaine : Chaine deux
Le PMC P2 pointe sur la chaine : Chaine deux
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat ptrs.pasm
new P1, .String
set P1, «Chaine un\n»
clone P2, P1
set P2, “Chaine deux\n”
print “Le PMC P1 pointe sur la chaine : “
print P1
print “Le PMC P2 pointe sur la chaine : “
print P2
end
coruscant:~/Langages/asmparrot chris$ parrot ptrs.pasm
Le PMC P1 pointe sur la chaine : Chaine un
Le PMC P2 pointe sur la chaine : Chaine deux
coruscant:~/Langages/asmparrot chris$
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 :
read S1, I1
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.
coruscant:~/Langages/asmparrot chris$ cat lec.pasm
getstdin P0
set I1, 5
read S1, I1
print S1
print «\n»
end
coruscant:~/Langages/asmparrot chris$ parrot lec.pasm
12
12
coruscant:~/Langages/asmparrot chris$ parrot lec.pasm
123456789
12345
coruscant:~/Langages/asmparrot chris$
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.
coruscant:~/Langages/asmparrot chris$ cat exo.pasm
getstdin P0
readline S1, P0
print “Chaine lue : “
print S1
end
coruscant:~/Langages/asmparot chris$ parrot exo.pasm
Bonjour
Chaine lue : Bonjour
coruscant:~/Langages/asmparot chris$
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.
coruscant:~/Langages/asmparot chris$ cat exo.pasm
getstdin P0
getstdout P1
readline S1, P0
print P1, “Chaine lue : “
print P1, S1
end
coruscant:~/Langages/asmparot chris$ parrot exo.pasm
Bonjour
Chaine lue : Bonjour
coruscant:~/Langages/asmparot chris$
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
- [Pascal] http://www.infeig.unige.ch/support/cpil/lect/mvp/
- [Lukasiewicz] Jan Lukasiewicz http://www-groups.mcs.st-and.ac.uk/history/Biographies/Lukasiewicz.html
- [HP 35] Calculatrices Reverse Polish Notation pour Linux http://www.linuxfocus.org/Francais/January2004/article319.shtml
- [Parrot] Programming Parrot http://www.perl.com/pub/a/2001/04/01/parrot.htm
- [Monty Python] The Parrot Sketch http://video.google.com/videoplay?docid=5775099474392087542
- [JWS] http://www.jwcs.net/~jonathan/perl6/parrot-win32.zip


