Retrouvez cet article dans : Linux Magazine 99
Lors des deux premiers articles, nous avons détaillé le fonctionnement des instructions de base de la machine virtuelle Parrot. Nous allons maintenant nous intéresser davantage aux particularités du langage d’assemblage et découvrir que l’on peut faire de la programmation orientée objet en assembleur.
Les macro-instructions
Qu’est-ce qu’une macro ?
Une macro-instruction [Macro] est un moyen de définir des opérations non répertoriées par le langage de base de la machine à partir du jeu d’instructions réellement disponible. Plus spécifiquement, elle peut être vue comme la création de toutes pièces par le programmeur d’un nouveau code opération dont l’exécution entraînera celle de plusieurs commandes de base. Le comportement est toutefois fondamentalement différent de celui d’un sous-programme, une macro-instruction doit être considérée comme l’association d’un texte de substitution au code qui l’identifie, et ceci, chaque fois que nécessaire. De plus, une macro-instruction pourra s’adapter à son environnement au moyen des paramètres syntaxiques qui lui seront transmis. Il faut par ailleurs noter que cette technique de programmation n’est pas l’apanage de l’assembleur. En langage C,#define abs(x) ((x) < 0 ? - (x) : (x))Ensuite, chaque fois que le programme y fera référence sous la forme
((exp) < 0 ? - (exp) : (exp))
Définition d’une macro en Parrot
La définition d’une macro-instruction commence par le mot clé .macro suivi du nom qui lui servira de référence, puis, éventuellement, d’une liste de paramètres. Commençons par définir une opération simple. À l’image de l’instruction readline qui lit une chaîne de caractères terminée par un \n, nous allons créer une macro-instruction printline qui affiche une valeur suivie d’un \n.
chris$ cat macro.pasm
.macro printline (A)
print .A
print “\n”
.endm
# Affichage d’un entier.
set I1, 12513
.printline (I1)
# Affichage d’un réel.
set N1, 3.14159
.printline (N1)
# Affichage d’une chaîne.
set S1, "Bonjour tout le monde."
.printline (S1)
end
chris$ parrot macro.pasm
12513
3.141590
Bonjour tout le monde..
chris$
Cette séquence amène quelques commentaires.
La première ligne nous permet de définir le nom et la liste d’appel de notre macro-instruction. Cette définition débute par la directive .macro suivie du nom par lequel elle sera référencée, ici printline. Le paramètre qui lui est passé, A, représente la valeur que l’on désire afficher. Comme partout dans Parrot, ce peut être une référence à un registre ou une valeur explicite.
Après cette ligne, il est nécessaire de définir le corps de la macro-instruction. Ici, il se compose de deux lignes, l’affichage de la donnée qui lui a été transmise suivi d’un \n.
La séquence doit se terminer par la directive .endm.
Dans le programme, on y fait référence par l’intermédiaire de son nom précédé d’un point : .printline, suivi du paramètre.
Si on examinait un peu plus en détail ce qui a été fait, on constaterait qu’un prétraitement syntaxique aurait substitué à chaque appel les deux lignes qu’il représente. Le programme final soumis au système est, en définitive, le suivant :
# Impression d’un entier.
set I1, 12513
print I1
print “\n”
# Impression d’un réel.
set N1, 3.14159
print N1
print “\n”
# Impression d’une chaîne.
set S1, "Bonjour tout le monde."
print S1
print "\n"
end
Les paramètres
Un paramètre peut représenter pratiquement n’importe quel objet, un registre ( chris$ cat macro.pasm
.macro lire (PMC)
LIRE:
readline S0, .PMC
unless S0, FIN
print P1, S0
branch LIRE
FIN:
.endm
getstdout P1
open P3, "dial.txt", "<"
.lire (P3)
end
chris$ parrot macro.pasm
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!
chris$
ou lorsqu’une macro doit gérer plusieurs sorties :
chris$ cat macro.pasm
.macro printline (PMC, A)
print .PMC, .A
print “\n”
.endm
.macro quot (DIVID, DIVIS, BON, MAUVAIS)
eq .DIVIS, 0, .MAUVAIS
div I2, .DIVID, .DIVIS
branch .BON
.endm
getstdin P0
getstdout P1
readline S1, P0
set I0, S1
readline S1, P0
set I1, S1
.quot (I0, I1, OK, PASOK)
OK:
print P1, “Le quotient de “
print P1, I0
print P1, “ par “
print P1, I1
print P1, " est egal a "
.printline (P1, I2)
branch SUITE
PASOK:
.printline (P1, “Erreur, division par zero.”)
SUITE:
end
coruscant:~/Langages/asmparrot chris$ parrot macro.pasm
25
5
Le quotient de 25 par 5 est egal a 5
chris $parrot macro.pasm
25
0
Erreur, division par zero.
chris$
Explication. La macro-instruction .quot comporte quatre paramètres, le dividende, le diviseur, l’adresse où aller si tout se passe bien et l’adresse concernée en cas d’erreur.
Lors de la première exécution, le diviseur étant différent de zéro, il n’y a aucun problème, l’opération est réalisable. La sortie se fait vers l’adresse transmise par l’intermédiaire du paramètre Les références locales
Lorsque nous avons écrit la macro chris$ cat macro.pasm
.macro lire (PMC)
LIRE:
readline S0, .PMC
unless S0, FIN
print P1, S0
branch LIRE
FIN:
.endm
getstdout P1
open P3, "dial1.txt", "<"
.lire (P3)
open P4, “dial2.txt”, “<”
.lire (P4)
end
chris$ parrot macro.pasm
error:imcc:Label ‘LIRE’ already defined
in macro ‘.lire’ line 2
included from ‘st.pasm’ line 1
chris$
L’interpréteur détecte une erreur syntaxique. En effet, l’opération de substitution a produit le résultat suivant
getstdout P1
open P3, "dial1.txt", "<"
LIRE:
readline S0, P3
unless S0, FIN
print P1, S0
branch LIRE
FIN:
open P4, "dial2.txt", "<"
LIRE:
readline S0, P4
unless S0, FIN
print P1, S0
branch LIRE
FIN:
end
qui met bien en évidence la duplication des labels ainsi que nous le précise le message d’erreur.
Pour parer à ce problème, chaque fois qu’une macro qui contient une référence est susceptible d’être développée plusieurs fois, il est nécessaire de déclarer tous ses labels comme étant locaux au moyen de la directive chris$ cat macro.pasm
.macro lire (PMC)
.local $LIRE:
readline S0, .PMC
unless S0, .$FIN
print P1, S0
branch .$LIRE
.local $FIN:
close .PMC
.endm
getstdout P1
print P1, “** Fichier 1 **\n”
open P3, “dial1.txt”, “<”
.lire (P3)
print P1, “** Fichier 2 **\n”
open P4, "dial2.txt", "<"
.lire (P4)
end
chris$ parrot macro.pasm
** Fichier 1 **
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/.
** Fichier 2 **
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!
chris$
Il est maintenant possible de faire référence à cette macro autant de fois que nécessaire sans que la moindre erreur soit générée.
Les macros prédéfinies
Parrot prédéfinit un certain nombre de macros. Nous en avons utilisé quelques-unes. Par exemple chris$ cat cons.pasm
.macro printline (PMC, A)
print .PMC, .A
print "\n"
.endm
.constant Registre_Entier1 I1
.constant Registre_Reel1 N1
.constant Registre_Chaine1 S1
.constant Registre_PMC1 P1
.constant Dix 10
.constant Cent 100
.constant Hello “Bonjour tout le monde\n”
set .Registre_Entier1, .Dix
set .Registre_Reel1, .Cent
set .Registre_Chaine1, .Hello
getstdout .Registre_PMC1
.printline (.Registre_PMC1, .Registre_Entier1)
.printline (.Registre_PMC1, .Registre_Reel1)
.printline (.Registre_PMC1, .Registre_Chaine1)
end
chris$ parrot cons.pasm
10
100.000000
Bonjour tout le monde
chris$
Ainsi, rien n’empêche de créer un fichier qui va contenir toutes les déclarations de constantes possibles et imaginables qui sera inséré dans le programme au moyen de la directive chris$ cat constantes.txt
.constant Zero 0
.constant Euler 0.57721
.constant Un 1
.constant Apery 1.20205
.constant Racine_Deux 1.41421
.constant Mills 1.30637
.constant Nombre_d_or 1.61803
.constant Niven 1.70521
.constant Racine_Trois 1.73205
.constant Feigenbaum 2.50290
.constant Sierpinski 2.58498
.constant e 2.71828
.constant pi 3.14159
chris$ cat cons.pasm
.macro printline (PMC, A)
print .PMC, .A
print “\n”
.endm
.include “constantes.txt”
getstdout P1
print P1, “Racine de deux : “
.printline (P1, .Racine_Deux)
print P1, “Racine de trois : “
.printline (P1, .Racine_Trois)
print P1, “Valeur de e : “
.printline (P1, .e)
print P1, „Valeur de pi : „
.printline (P1, .pi)
end
chris$ parrot cons.pasm
Racine de deux : 1.414210
Racine de trois : 1.732050
Valeur de e : 2.718280
Valeur de pi : 3.141590
chris$
Application sur un programme
Le programme suivant permet de convertir un nombre décimal en numération romaine [Romain]. chris$ cat romains.pasm
# Declaration des macros.
.macro set_liste (PMC, CHAINE)
new .PMC, .ResizablePMCArray
split .PMC, “,”, .CHAINE
.endm
.macro print_line (LIGNE)
print P1, .LIGNE
print P1, “\n”
.endm
.macro lire (REGISTRE)
.local $LIRE:
.print_line (“Nombre de depart?”)
readline S0, P0
set .REGISTRE, S0
lt I0, 4000, .$SORTIE
.print_line (“Le nombre doit etre inferieur a 4000.”)
branch .$LIRE
.local $SORTIE:
.endm
.macro convertir (A, R)
set I1, 0 # Indice d’exploration des listes.
.local $CALCUL:
set I2, P3[I1] # Récupération diviseur.
div I3, .A, I2 # Occurrences du chiffre romain.
set S2, P4[I1] # Récupération de chiffre romain.
repeat S2, S2, I3
concat .R, S2 # Construction du nombre romain.
mod .A, .A, I2 # Reste de la division.
inc I1
if .A, .$CALCUL
.endm
.macro question (OUI)
.print_line (“Un autre calcul (o/n) ?”)
readline S0, P0
eq S0, "o\n", .OUI
.endm
# Déclaration et initialisation liste chiffres arabes.
.set_liste (P3, "1000,900,500,400,100,90,50,40,10,9,5,4,1")
# Déclaration et initialisation liste chiffres romains
.set_liste (P4, "M,CM,D,CD,C,XC,L,XL,X,IX,V,IV,I")
getstdin P0 # Entrée standard.
getstdout P1 # Sortie standard.
LEC:
.lire (I0)
CONV:
set S1, ""
print P1, “Le nombre “
print P1, I0
print " s’ecrit : "
.convertir (I0, S1)
.print_line (S1)
.question (LEC)
end
chris$ parrot romains.pasm
Nombre de depart?
666
Le nombre 666 s’ecrit : DCLXVI
Un autre calcul (o/n) ?
o
Nombre de depart?
3333
Le nombre 3333 s’ecrit : MMMCCCXXXIII
Un autre calcul (o/n) ?
n
chris$
La première macro, La programmation orientée objet
Il peut sembler surprenant de penser que l’on puisse écrire des applications orientées objet [poo] en assembleur. En fait, il existe dans Parrot des fonctions permettant ce type d’approche. La longueur des programmes ne me permettra pas d’écrire comme je l’ai fait jusqu’à présent des ensembles complets. Je me contenterais de définir les diverses opérations afin que ceux qui désireraient approfondir le sujet puissent le faire sans trop de difficulté.Les classes
Définition d’une classe
C’est bien entendu par l’intermédiaire d’un PMC que sera déclarée une classe. La directivenewclass P0, "Humain"Un nouvel objet référencé
chris$ parrot romains.pasm
print "Avant la creation de l’objet.\n"
bsr LISTEPMC
newclass P10, "Humain"
print “\n---------------------------\n\n”
print “Apres la creation de l’objet.\n”
bsr LISTEPMC
end
LISTEPMC:
set I0, 1
BOUCLE:
# Le numéro correspond à un type de PMC valide.
valid_type I1, I0
eq I1, 0, FIN
# Récupération du nom du PMC.
typeof S0, I0
# Edition 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:
ret
chris$
Avant la creation de l’objet.
Nom du PMC numero 1 : Null
Nom du PMC numero 2 : Env
* * * * *
Nom du PMC numero 80 : Super
Nom du PMC numero 81 : Undef
---------------------------
Apres la creation de l’objet.
Nom du PMC numero 1 : Null
Nom du PMC numero 2 : Env
* * * * *
Nom du PMC numero 80 : Super
Nom du PMC numero 81 : Undef
Nom du PMC numero 82 : ParrotClass
chris$
Les attributs de classe
La classe qui vient d’être créée est vierge de toute caractéristique. Il est possible de définir des attributs qui lui seront rattachés et desquels hériteront tous ses descendants (sous-classes ou objets). Un attribut est défini par son nom et par la classe à laquelle il est rattaché.addattribute P0, "Prenom" addattribute P0, "Nom" addattribute P0, "Age"Nous dirons par exemple qu’un être humain, quel qu’il soit, comporte trois caractéristiques, son prénom, son nom et son âge. Les trois instructions que nous venons d’écrire spécifient cet état de chose. Les attributs
Définition d’une sous-classe
L’instructionsubclass Pi, Pj, "Nom_Classe"permet de définir une sous-classe appelée
subclass P1, P0, "Homme" subclass P2, P0, "Femme" subclass P3, P0, "Enfant"qui nous permettent de créer dans la classe principale
classname SO, P1va nous retourner dans le registre
Les objets
Définition d’un objet
Pour instancier un objet dans une classe donnée, il faut, au préalable, déterminer quel est le numéro qui a été affecté à la classe en question lors de sa déclaration. Le code opération pour réaliser cette fonction est :find_type Ix, "Nom_de_la_classe"L’entier correspondant à la référence de la classe se trouve dans le registre spécifié. C’est lui qui va nous permettre d’instancier un objet.
new Px, IxSi nous reprenons l’exemple que nous avons décrit jusqu’à présent, nous pouvons créer un objet Homme au moyen de la séquence.
find_type I0, "Homme" new P10, I0La référence de l’objet qui vient d’être créé se trouve dans le PMC
Les attributs
L’objet vient d’hériter des caractéristiques de la classe dans laquelle il a été créé. Nous pouvons donc maintenant leur affecter des valeurs. C’est l’instruction setattribute qui réalise cette opération.setattribute Px, A, PzElle a pour effet de positionner l’attribut
- par son nom, s’il n’y a pas d’ambiguïté :
"Prenom"; - par sa hiérarchie
"Humain\00Prenom"; - par son numéro d’ordre dans la déclaration
Ix.
new P5, .String set P5, "Christian" setattribute P10, “Prenom”, P5 new P5, .String set P5, "Christian" setattribute P10, “Humain\00Prenom”, P5 new P5, .String set P5, "Christian" classoffset I1, P10, "Humain" setattribute P10, I1, P5Dans la troisième méthode, l’instruction
classoffset I1, P10, "Humain"va permettre de récupérer le numéro d’ordre du premier attribut que la classe
getattribute Px, Py, Aqui a pour effet de récupérer la valeur de l’attribut
Une application
Le programme suivant reprend les divers points qui ont été vus. Il permet de créer plusieurs objets et d’en stocker les définitions dans une liste de PMC ( .macro print_line (A1, A2, A3)
print P1, .A1
print P1, .A2
print P1, .A3
.endm
.macro lecture (DESTINATION)
readline .DESTINATION, P0
chopn .DESTINATION, 1
.endm
On déclare maintenant les PMC. C’est dans une liste de PMC référencée par P4, que seront mémorisés les divers objets au fur et à mesure de leur création.
getstdin P0
getstdout P1
new P4, .ResizablePMCArray
Comme vu précédemment, on déclare la classe de base # Déclaration de la classe Humain
newclass P10, "Humain"
# Attributs de l’humain.
addattribute P10, “Prenom”
addattribute P10, “Nom”
addattribute P10, "Age"
# Déclaration de la sous-classe Homme
subclass P11, P10, "Homme"
À partir de maintenant, le programme entre dans un mode conversationnel. Un nouvel objet possédant ses propres attributs sera créé chaque fois que nécessaire et sa référence sera conservée dans la liste créée pour la circonstance.
# Récupération du type de "Homme"
find_type I0, "Homme"
# Lecture informations.
print P1, “Prenom,Nom,Age ou ligne vide pour terminer\n”
ENTREE:
.lecture (S1)
unless S1, EDITION
new P2, .ResizableStringArray
# Découpage de la chaîne lue en ses composantes.
split P2, ",", S1
# Nouvel objet de la sous-classe "Homme"
new P20, I0
# PMC intermédiaire pour affecter les attributs.
new P30, .String
set P30, P2[0]
setattribute P20, “Humain\x00Prenom”, P30
set P30, P2[1]
setattribute P20, “Humain\x00Nom”, P30
set P30, P2[2]
setattribute P20, "Humain\x00Age", P30
push P4, P20
branch ENTREE
À la fin des créations, il ne reste plus qu’à explorer la liste pour récupérer l’une après l’autre les références mémorisées afin de les éditer.
EDITION:
set I0, P4
.print_line ("Nombre de creations : ", I0, "\n")
set I1, 0
SUIVANT:
eq I1, I0, FINI
.print_line ("Homme numero ", I1, " : ")
set P25, P4[I1]
classname S1, P11
getattribute P9, P25, “Humain\x00Prenom”
.print_line (“Prenom : “, P9, “ - “)
getattribute P9, P25, “Humain\x00Nom”
.print_line ("Nom : ", P9, " - ")
getattribute P9, P25, "Humain\x00Age"
.print_line ("Age : ", P9, "\n")
inc I1
branch SUIVANT
FINI:
end
Lorsque l’ensemble des morceaux du programme sont mis bout à bout, l’exécution donne le résultat suivant :
chris$ parrot homme.pasm Prenom,Nom,Age ou ligne vide pour terminer Christian,Aperghis-Tramoni,59 Sebastien,Aperghis-Tramoni,29 Nombre de creations : 2 Homme numero 0 : Prenom : Christian - Nom : Aperghis-Tramoni - Age : 59 Homme numero 1 : Prenom : Sebastien - Nom : Aperghis-Tramoni - Age : 29 chris$
Les méthodes
Les méthodes prédéfinies
Tous les objets héritent d’un ensemble de fonctions prédéfinies par le PMC chris$ cat init.pasm
# Déclaration de la classe Humain
newclass P10, "Humain"
# Attributs de l’humain.
addattribute P10, “Prenom”
# Déclaration de la sous-classe Homme
subclass P11, P10, "Homme"
# Recuperation du type de "Homme"
find_type I0, "Homme"
# Nouvel objet de la sous-classe “Homme”
new P20, I0
end
.namespace ["Humain"]
.pcc_sub __init:
get_params "(0)", P31
classname S0, P31
print “Un nouvel objet de type “
print S0
print “ vient de se creer.\n”
returncc
chris$ parrot init.pasm
Un nouvel objet de type Homme vient de se creer.
chris$
Voyons un autre exemple qui va lui aussi faire référence à une méthode prédéfinie. Soient les instructions suivantes :
newclass P10, "Arithmetique"
addattribute P10, “Entier”
subclass P11, P10, “Valeur”
find_type I0, "Valeur"
# Objet de la sous-classe “Valeur”
new P20, I0
set P20, 30
end
Son exécution va produire le message d’erreur :
Can’t find method ‘__set_integer_native’ for object ‘Valeur’ current instr.: ‘(null)’ pc 16 (prog.pasm:7)Nous faisant savoir que l’instruction numéro 7 a fait appel à la méthode
set Px, Iy : __set_integer_native get Ix, Py : __get_integer add Px, Py, Pz : __add inc Px : __increment print Px : __get_stringVoyons ceci à partir d’un exemple. Nous allons déclarer deux objets composés d’un entier.
newclass P1, "Nombre" addattribute P1, "Entier" find_type I1, "Nombre" # Création d’un premier objet nombre. new P3, I1 # Affectation d’une valeur à l’objet. set P3, 300 # Création d’un second objet nombre. new P4, I1 # Affectation d’une valeur à l’objet. set P4, 400 get_results "(0)", P5 # Addition des deux objets. add P5, P4, P3 # Affichage des résultats. print "Resultat de l’addition : " print P5 print "\n" endOn désire au moment où ils sont créés leur affecter comme attribut un entier. Dans l’espace de nom
.namespace ["Nombre"]
.pcc_sub __init:
get_params "(0)", P31
new P10, .Integer
setattribute P31, “Entier”, P10
returncc
Comme il s’agit d’objets, le code opération .pcc_sub __set_integer_native:
get_params "(0,0)", P31, I31
new P30, .Integer
set P30, I31
returncc
L’opération que nous désirons effectuer est l’addition des valeurs entières correspondant aux attributs des deux objets qui viennent d’être créés. Le résultat de cette opération sera récupéré dans un PMC de type .pcc_sub __add:
get_params "(0,0,0)", P25, P24, P23
getattribute P15, P25, “Entier”
getattribute P14, P24, “Entier”
new P13, .Integer
add P13, P14, P15
set_returns "(0)", P13
returncc
Notre programme est maintenant terminé, il ne nous reste plus qu’à l’exécuter.
chris$ parrot add.pasm Resultat de l’addition : 700 chris$
Les méthodes définies par le programmeur
Une méthode attachée à une classe doit avoir un nom qui sera défini par l’intermédiaire d’un registre " string ". set S0, "methode"
***
callmethodcc P0, S0
***
.namespace [“Objet”]
.pcc_sub methode:
***
returncc
Une fois déclarée, la méthode devra être rattachée à l’espace concerné au moyen de la fonction chris$ cat obj.pasm
# Declaration de la classe Humain
newclass P0, "Humain"
# Declaration de la methode Identite
set S0, "Identite"
# Appel de la methode Identite
callmethodcc P0, S0
end
# Definition de la methode
.namespace [“Humain”]
.pcc_sub Identite:
print “Bonjour, je suis un etre humain.\n”
returncc
chris$ parrot obj.pasm
Bonjour, je suis un etre humain.
chris$
Test de l’existence d’une méthode
Avant l’appel d’une méthode, il est possible de tester son existence. va tester si la méthode chris$ cat obj.pasm
.macro existe (A, B, OUI, NON)
print "La methode "
print .A
unless .B, .$FAUX
print “ existe, on l’appelle.\n”
branch .OUI
.local $FAUX:
print “ n’existe pas, on passe a la suite.\n”
branch .NON
.endm
newclass P2, "Chris"
set S0, "Bonjour"
# Test de l’existence de la methode Bonjour.
set S1, "Bonjour"
can I0, P2, S1
.existe (S1, I0, E1, SUITE)
E1:
callmethodcc P2, S0
SUITE:
# Test de l’existence de la methode Salut.
set S1, "Salut"
can I0, P2, S1
.existe (S1, I0, E2, FIN)
E2:
callmethodcc P2, S0
FIN:
end
# Definition de la methode
.namespace [“Chris”]
.pcc_sub Bonjour:
print “ Chris vous souhaite le bonjour.\n”
returncc
chris$ parrot obj.pasm
La methode Bonjour existe, on l’appelle.
Chris vous souhaite le bonjour.
La methode Salut n’existe pas, on passe a la suite.
chris$
Quelques particularités notables
Évaluation d’une chaîne
Le langage d’assemblage nous permet, comme cela est possible en Perl, d’évaluer une chaîne de caractères, contenue dans un registre, comme un programme. Prenons par exemple l’instruction suivante :set S0, "set S1, 10\nprint \"Valeur de S1 : \"\nprint S1\nprint \"\\n\"\nreturncc\n"L’impression du contenu du registre S0 donnera le résultat suivant :
set S1, 10 print "Valeur de S1 : " print S1 print “\n” returnccSoit un sous-programme Parrot parfaitement constitué. Cette chaîne peut donc être soumise à un évaluateur qui la considérera comme une suite d’instructions à exécuter.
chris$ cat evaluer.pasm
print " Debut du programme.\n"
compreg P1, "PASM"
set S0, "set S1, 10\nprint \"Valeur de S1 : \"\nprint S1\nprint \"\\n\"\nreturncc\n"
set_args "(0)", S0
get_results "(0)", P0
print “ Evaluation du programme contenu dans S0.\n”
invokecc P1
invokecc P0
print “ Retour au programme principal.\n”
end
chris$ parrot evaluer.pasm
Debut du programme.
Evaluation du programme contenu dans S0.
Valeur de S1 : 10
Retour au programme principal.
chris$
Le registre S0 contient la donnée représentative du programme qui doit être évalué. L’instruction Les coroutines
Une coroutine est un sous-programme dont l’exécution peut être suspendue et qui, au moment de l’appel suivant, reprendra son exécution au point où elle aura été interrompue. Tout sous-programme qui contient le code opération chris$ cat evaluer.pasm
.include "interpinfo.pasm"
.pcc_sub _main:
.const .Sub P0 = “_japh”
new P2, .String
set P2, ""
store_global "car", P2
find_global P10, "car"
IMP:
invokecc P0
unless P10, FIN
print P10
branch IMP
FIN:
end
.pcc_sub _japh:
set S1, "Just Another Perl Hacker.\n"
set I1, 0
SUITE:
find_global P10, "car"
substr S10, S1, I1, 1
set P10, S10
inc I1
yield
branch SUITE
chris$ parrot evaluer.pasm
Just Another Perl Hacker.
chris$
Application des coroutines
Le mécanisme se révèle être d’une grande utilité lorsqu’une fonction doit mémoriser une valeur au fur et à mesure de ses appels successifs. Un des premiers exemples qui vient à l’esprit est la génération d’une série de valeurs pseudo-aléatoires [Aléat]. L’algorithme standard part avec une valeur initiale (la racine) qui sert pour produire la première valeur. Cette dernière sera alors utilisée pour en générer une nouvelle, et ainsi de suite. Prenons un algorithme classique de génération de suite pseudo-aléatoire, qui, en Perl, se présente comme suit : sub rand {
$x = int(($x * 1103515245 + 12345) / 65536) % 32768;
return $x;
}
Et réalisons-le en Parrot au moyen d’une coroutine.
chris$ cat rand.pasm
.const .Sub P1 = "_rand"
set I1, 10
AUTRE:
get_results "(0)", I0
invokecc P1
print I0
print " "
dec I1
if I1 , AUTRE
print "\n"
end
.pcc_sub _rand:
new P0, .Float
set P0, 1
store_global "racine", P0
find_global P0, "racine"
NOUVEAU:
mul P0, 1103515245
add P0, 12345
div P0, 65536
mod P0, 32768
set I31, P0
set_returns "(0)", I31
yield
branch NOUVEAU
chris$ parrot rand.pasm
16838 22996 7307 3007 20531 15468 29688 23554 1052 20045
chris$
Récupération des valeurs de la ligne de commandes
Les informations entrées sur la ligne de commandes sont les premiers résultats ( chris$ cat cmd.pasm
# Récupération des paramètres de la ligne de commande.
# P0 est de type .ResizableStringArray.
get_params "(0)", P0
set I1, P0
print I1
print " parametres : "
set I0, 0
AUTRE:
set S0, P0[I0]
print S0
print " - "
inc I0
lt I0, I1 , AUTRE
print “\n”
end
chris$ parrot cmd.pasm Bonjour tout le monde.
5 parametres : cmd.pasm - Bonjour - tout - le - monde. -
chris$
Suivi du déroulement
On dispose d’instructions spécifiques permettant de suivre pas à pas le déroulement du programme. Le code opération chris$ cat info.pasm
.macro set_liste (PMC, CHAINE)
new .PMC, .ResizablePMCArray
split .PMC, “,”, .CHAINE
.endm
.macro info (FICHIER, LIGNE)
print “Fichier : “
print .FICHIER
print “, Ligne : “
print .LIGNE
print "\n"
.endm
getfile S0
.set_liste (P3, "1,2,3,4,5")
set I1, P3
getline I30
.info (S0, I30)
set I0, 0
SUITE:
set S1, P3[I0]
print S1
print "\n"
inc I0
getline I30
.info (S0, I30)
lt I0, I1 , SUITE
end
chris$ parrot info.pasm
Fichier : info.pasm, Ligne : 17
1
Fichier : info.pasm, Ligne : 25
2
Fichier : info.pasm, Ligne : 25
3
Fichier : info.pasm, Ligne : 25
4
Fichier : info.pasm, Ligne : 25
5
Fichier : info.pasm, Ligne : 25
chris$
Dans le même ordre d’idée, il est possible de procéder au tracé des instructions. L’instruction chris$ cat trace.pasm
.macro set_liste (PMC, CHAINE)
new .PMC, .ResizablePMCArray
split .PMC, ",", .CHAINE
.endm
.set_liste (P3, "1,2,3,4,5")
trace 1
set I1, P3
set I0, 0
trace 0
SUITE:
set S1, P3[I0]
print S1
print "\n"
trace 1
inc I0
trace 0
lt I0, I1, SUITE
end
chris$ parrot trace.pasm
9 set I1, P3 I1=-888 P3=ResizableStringArray=PMC(0xf9bf7c)
12 set I0, 0 I0=-888
15 trace 0 1
27 inc I0 I0=0
29 trace 0 2
27 inc I0 I0=1
29 trace 0 3
27 inc I0 I0=2
29 trace 0 4
27 inc I0 I0=3
29 trace 0 5
27 inc I0 I0=4
29 trace 0
chris$
Pour chaque instruction tracée, on voit apparaître le numéro de la ligne considérée, l’instruction correspondante et, si cette dernière est une affectation, le registre de destination et sa valeur avant l’exécution de l’opération demandée. C’est ainsi que, lors de la première exécution des lignes 9 et 12, les contenus sont égaux à -888, valeur qui correspond à un registre qui n’a reçu aucune affectation.
Gestion du temps
Comme en Perl, l’instruction.constant TM_SEC 0 .constant TM_MIN 1 .constant TM_HOUR 2 .constant TM_MDAY 3 .constant TM_MON 4 .constant TM_YEAR 5 .constant TM_WDAY 6 .constant TM_YDAY 7 .constant TM_ISDST 8
.TM_SECSeconde (0-60).TM_MINMinute (0-59).TM_HOURHeure (0-23).TM_MDAYJour dans le mois (1-31).TM_MonMois dans l’année (1-12).TM_YEARAnnée réelle, 2007 est bien 2007, pas 107.TM_WDAYJour dans la semaine (0-6), 0 représentant le dimanche..TM_YDAYJour dans l’année (0-365).TM_ISDSTHeure d’été (Vrai ou Faux)
chris$ cat time.pasm
.macro set_liste (PMC, CHAINE)
new .PMC, .ResizablePMCArray
split .PMC, ",", .CHAINE
.endm
.macro print2 (V1, V2)
print .V1
print .V2
.endm
.const .Sub P1 = "_date"
# Initialisations données de la coroutine.
invokecc P1
# Affichage du jour.
invokecc P1
# IAffichage de l’heure
invokecc P1
# Affichage du numéro du jour.
invokecc P1
# Affichage heure d’été.
invokecc P1
end
.pcc_sub _date:
.set_liste (P10, “Dim,Lun,Mar,Mer,Jeu,Ven,Sam”)
.set_liste (P11, “*,Jan,Fev,Mar,Avr,Mai,Jun,Jui,Aou,Sep,Oct,Nov,Dec”)
.set_liste (P12, “hiver,ete”)
time I0
decodelocaltime P2, I0
yield
.include "tm.pasm"
.print2 (“Nous sommes le”, “ “)
set I1, P2[.TM_WDAY]
set S1, P10[I1]
.print2 (S1, " ")
set I1, P2[.TM_MDAY]
.print2 (I1, " ")
set I1, P2[.TM_MON]
set S1, P11[I1]
.print2 (S1, " ")
set I1, P2[.TM_YEAR]
.print2 (I1, "\n")
yield
.print2 (“Il est”, “ “)
set I1, P2[.TM_HOUR]
.print2 (I1, " heures ")
set I1, P2[.TM_MIN]
.print2 (I1, " minutes ")
set I1, P2[.TM_SEC]
.print2 (I1, “ secondes\n”)
yield
print “Ce jour est le “
set I1, P2[.TM_YDAY]
.print2 (I1, “eme de l’annee\n”)
yield
print “Nous sommes en heure d’”
set I1, P2[.TM_ISDST]
set S1, P12[I1]
.print2 (S1, ".\n")
yield
chris$ parrot time.pasm
Nous sommes le Mar 10 Avr 2007
Il est 11 heures 20 minutes 34 secondes
Ce jour est le 99eme de l’annee
Nous sommes en heure d’ete.
chris$





Donnez votre avis
Vous devez avoir ouvert une session pour écrire un commentaire.