Le langage PIR, quatrième partie

Article publié dans Linux Magazine 126, avril 2010.

Copyright © 2010 Christian Aperghis-Tramoni

[+ del.icio.us] [+ Developers Zone] [+ Bookmarks.fr] [Digg this] [+ My Yahoo!]

Chapeau

Nous avons vu jusqu'à maintenant comment réaliser des programmes classiques en PIR, le chapitre que nous abordons va être consacré à l'étude des structures et à la gestion des applications orientées objet.

Les programmes peuvent être téléchargé sur mon site : http://www.dil.univ-mrs.fr/~chris/Documents/progs04.pod

Introduction.

La quatrième et dernière partie de la série consacrée à PIR que nous allons présenter va nous permettre de détailler les possibilités offertes par le langage en terme de programmation orientée objet [poo]. Grâce à de nombreux exemples nous allons pouvoir expliquer comment faire pour déclarer des classes, ou bien procéder à la création de nouveaux objets. Nous verrons aussi comment définir les méthodes qui leur sont attachées.

La programmation orientée objet.

La programmation par objets est un paradigme de programmation informatique. Sa base est de définir et d'assembler de unités logicielles que l'on appelle objets.

Un objet est une structure de données correspondant à un ensemble de valeurs qui vont permettre de définir son état ainsi qu'un ensemble de messages spécifiant son comportement. Il est donc représentatif d'un concept auquel seront attachés des attributs et les méthodes permettant d'y accéder.

Les attributs définissent la structure interne alors que l'ensemble des méthodes d'accès qui décrivent comment seront traités les messages formant son interface. Ce n'est qu'à travers elles qu'il sera possible d'accéder aux données de l'objet concerné.

Toutes ces informations sont cachées, on dira encapsulées. Le principal avantage étant la capacité de modifier la structure interne des objets ou des méthodes sans que ces actions aient un quelconque impact sur les usagers.

Le langage PIR est prévu pour proposer une syntaxe qui permet de simplifier l'ensemble des opérations dans le cadre d'une programmation de ce type, en particulier, en ce qui concerne les méthodes et les appels qui respectent scrupuleusement les conventions définies par la machine virtuelle [Parrot].

Les classes.

La classe est représentative de la structure de l'objet, c'est-à-dire la définition de l'ensemble des entités qui le composent.

Tout objet doit donc être donc issu d'une classe, on peut le considérer comme le produit brut qui sort d'un moule et qui devra donc être affiné.

Dans la réalité un objet est l'instanciation d'une classe. On pourra donc parler indifféremment d'objet ou d'instance (éventuellement d'occurrence).

Une classe est composée d'attributs et de méthodes.

Plusieurs instanciations de classes pourront avoir leurs attributs égaux sans pour autant représenter un seul et même objet, c'est ainsi qu'on fera la différence entre état et identité.

Nous avons évoqué l'encapsulation comme l'un des concepts du paradigme objet. C'est un mécanisme qui permet de rassembler l'ensemble des informations dans une structure permettant de dissimuler l'implémentation de l'objet. De cette manière, on pourra empêcher l'accès aux données par tout autre moyen que les services définis lors de sa création.

C'est l'encapsulation qui va garantir l'intégrité des données.

Les espaces de nom.

Un espace de noms permet de contrôler la visibilité des attributs et des méthodes définis lors de la création des objets [Noms].

Le fonctionnement est simple, il faut savoir que le nom d'une propriété ou d'une méthode est constitué de deux parties, l'identifiant et l'espace de noms lui même.

Les espaces de noms proposent un mécanisme dans lequel les noms peuvent être réutilisés. Ceci peut ne pas paraître fondamental, mais dans le cas d'un système complexe ou d'une application qui nécessite l'utilisation d'un grand nombre de bibliothèques, cet état de choses peut être extrêmement utile.

Chaque espace de noms définissant sa propre zone pour les noms de fonction et les noms de variables, il nous sera possible de disposer de plusieurs fonctions portant le même nom, d'en créer de nouvelles ou d'en convertir, d'autres sans avoir à mettre en œuvre la Multi-Method Dispatch (MMD) qui a été présentée au chapitre précédent.

Les espaces de noms sont aussi indispensables pour définir des classes dont nous parlerons plus tard.

Un espace de noms est spécifié au moyen de la directive .namespace ["Nom_Espace"]. Entre les crochets qui sont obligatoires, une chaîne de caractères identifiera l'espace en question.

La racine des espaces de noms peut être représentée sans clé interne .namespace [ ]. Dans ce cas, l'espace de noms sera par défaut parrot.

Si on désire créer un espace de noms référencé, cette référence apparaîtra comme clé lors de la déclaration .namespace ["EtreHumain"] pour créer un espace EtreHumain ou bien .namespace ["Vivant" ; "Homme"] pour créer l'espace de noms Vivant::Homme. Ainsi, en utilisant des points virgule (;) il est possible d'imbriquer les espaces de noms à une profondeur quelconque.

L'apparition de l'instruction .namespace sans les crochets provoquera une erreur.

  coruscant chris$ cat erreur.pir
  .namespace
  .sub "Programme en erreur." :main
    .local pmc nom
    nom = new "String"
  .end

  coruscant chris$ parrot erreur.pir
  error:imcc:syntax error, unexpected '\n', expecting '['
	in file 'test.pir' line 1
  coruscant chris$

Les espaces de noms sont des PMC d'un type spécifique. On les manipule donc exactement de la même manière que tous les autres PMC.

Il est toujours possible de récupérer le PMC de l'espace de noms situé à la racine au moyen du code opération get_root_namespace. Il est aussi possible de connaître nom de l'espace de noms courant, qui peut être différent du nom de la racine de l'espace de noms. C'est l'instruction get_namespace qui réalise cette opération.

  coruscant chris$ cat espace.pir
    .namespace []
    .sub "Recuperation de l'espace de noms" :main
    .local pmc nom
    nom = new "String"
    nom = get_namespace
    print "Nous sommes dans l'espace de noms : "
    say nom
  .end
  coruscant chris$ parrot espace.pir
  Nous sommes dans l'espace de noms : parrot
  coruscant chris$ cat espace1.pir
    .namespace ["Mon_espace"]
    .sub "Recuperation de l'espace de noms" :main
    .local pmc nom
    nom = new "String"
    nom = get_namespace
    print "Nous sommes dans l'espace de noms : "
    say nom
  .end

  coruscant chris$ parrot espace.pir
  Nous sommes dans l'espace de noms : Mon_espace
  coruscant chris$

En spécifiant comme clé un nom d'espace, il est possible d'obtenir le PMC représentatif de l'espace de noms en question.

  .local pmc reference
  reference = get_namespace ["EtreHumain"]

Une fois récupéré, un PMC espace de noms servira à retrouver les variables globales ou celles appartenant à un autre espace.

Il sera aussi possible de retrouver l'ensemble des variables globales de l'espace courant :

  coruscant chris$ cat espace.pir
  .sub main
   .local pmc espace, nom
   espace = get_global "MonEspace"
   nom = espace["ma_fonction"]
   say nom
   nom()
  .end

  .namespace ["MonEspace"]
  .sub ma_fonction
    say "Dans ma_fonction de l'espace de noms."
  .end

  coruscant chris$ parrot espace.pir
  ma_fonction
  Dans ma_fonction de l'espace de noms.
  coruscant chris$

Si on le désire, on peut faire porter la recherche sur les variables globales d'un espace de noms spécifique.

  coruscant chris$ parrot espace.pir
  .sub main
    .local pmc espace1, espace2
    espace1 = get_global "ma_fonction"
    say espace1
    set_global ["MonEspace"], "nouveau", espace1
    espace2 = get_global ["MonEspace"], "nouveau"
    say espace2
    espace2()
  .end

  .sub ma_fonction
    say "On est dans la fonction."
  .end
  coruscant chris$ parrot espace.pir
  ma_fonction
  ma_fonction
  On est dans la fonction.
  coruscant chris$

Ainsi que nous venons de le voir dans cet exemple, une fois trouvé à partir d'un espace de noms, un PMC sous-programme peut être référencé exactement de la même manière que n'importe quel sous-programme.

L'appel des méthodes.

Nous venons de définir ce qu'est un espace de noms, intéressons nous maintenant à l'ensemble des possibilités qui nous sont offertes par la mise en œuvre de ces outils. Il s'agit de la programmation orientée objet.

La principale caractéristique de cette technique de programmation est la possibilité qui est donnée de créer des méthodes attachées à la structure.

Une méthode peut être assimilée à un sous-programme à la différence près que l'appel doit obligatoirement être fait par l'intermédiaire d'un PMC, lequel est passé en paramètre.

La syntaxe de base pour référencer une méthode ressemble quelque peu à celle utilisée pour invoquer un sous-programme classique.

  "Nom_Objet"."nom_methode"(Liste d'arguments)

La principale différence vient du fait qu'une méthode étant rattachée à un objet particulier, il est nécessaire de spécifier en plus de son nom propre celui de l'objet auquel elle a été attachée.

Il faut aussi noter que le nom de la méthode doit être une chaîne de caractères et donc se présenter entre guillemets. Si ce n'est pas le cas, il sera traité comme un nom de variable.

  .local string Nom_Methode
  Nom_Methode = "Ma_Methode" 
  # Appel par nom explicite.
  Mon_Objet."Ma_Methode"()
  # Par l'intermédiaire d'une variable.
  Mon_Objet.Nom_Methode()

L'invoquant peut être indifféremment une variable ou un registre, et le nom de la méthode peut être un littéral chaîne de caractères, une variable chaîne de caractères ou un objet méthode PMC.

La définition des méthodes

Une méthode sera définie comme n'importe quel sous-programme, à deux différences majeures près.

Elle doit se situer dans un espace de noms du nom de la classe dont elle fait partie.

Elle doit utiliser l'indicateur spécifique :method

  .namespace ["Ma_Classe"]
    .sub "Ma_Methode" :method
    .param string valeur
    print "Valeur transmise : "
    say valeur
  .end

Nous verrons aussi plus tard sur des exemples que, à l'intérieur du corps de la méthode, l'objet invoquant peut être consulté au moyen du mot clé self.

Si on le désire, il est aussi possible de définir un nouveau nom pour l'objet qui vient d'être invoqué au moyen de la directive :invoquant.

Dans l'exemple qui suit nous définissons deux méthodes rattachées à la classe Mon_Espace. Il est fait référence à la première, Dire_Bonjour, à partir du module principal de l'unité de compilation, alors que l'autre, Dire_Aurevoir sera appelée à partir du corps de la première méthode, et ce, de deux manières différentes.

Le premier appel se fera en utilisant la chaîne de caractères spécifiant explicitement son nom (self."Dire_Aurevoir"()), l'autre par l'intermédiaire d'un registre dans lequel le nom aura été stocké ($S0 = "Dire_Aurevoir") puis self.$S0()).

  coruscant chris$ cat methode.pir
  .sub main
    .local pmc classe
    .local pmc objet
  # On crée une nouvelle classe a partir de l'espace de noms Mon_Espace.
    newclass classe, "Mon_Espace"
  # Instantiation d'un objet de la classe Mon_Espace
    new objet, "Mon_Espace"
  # Test de l'existence d'une methode.
    $I0 = can  objet, "Dire_Bonjour"
    print "Test de la methode Dire_Bonjour : "
    say $I0
    $I0 = can  objet, "Dire_Adieu"
    print "Test de la methode Dire_Adieu : "
    say $I0
  # Appel de la methode Dire_Bonjour dans l'espace de noms Mon_Espace.
    objet."Dire_Bonjour"() 
    say "Fin du programme." 
    end
  .end
  
  # Initialisation de l'espace de noms Mon_Espace
  .namespace ["Mon_Espace"]
  # Définition de la methode globale Mon_Espace::Dire_Bonjour
  .sub Dire_Bonjour :method         
    say "Bonjour."
  # Invoquation de Mon_Espace::Dire_Aurevoir
    self."Dire_Aurevoir"()
  # Positionnement du nom de la methode dans un registre.
    $S0 = "Dire_Aurevoir"
    self.$S0()
  .end
  
  # Définition de la methode globale Mon_Espace::Dire_Au_Revoir
  .sub Dire_Aurevoir :method 
    say "Au Revoir."
  .end

  coruscant chris$ parrot methode.pir
  Test de la methode Dire_Bonjour : 1
  Test de la methode Dire_Adieu : 0
  Bonjour.
  Au Revoir.
  Au Revoir.
  Fin du programme.
  coruscant chris$

Pour chaque appel de méthode le nom considéré apparaît dans l'espace de l'objet correspondant à la classe concernée.

La directive .sub crée automatiquement une entrée dans la table de symboles de l'espace de noms courant pour le sous-programme en question, mais lorsqu'une fonction .sub est étiquetée en tant que :method elle définit automatiquement une variable locale qui s'appelle self et lui assigne un objet passé en tant que paramètre. Il n'est donc pas nécessaire de déclarer de manière explicite .param pmc self pour la récupérer, cette directive vient toute seule avec la définition :method.

Il est aussi possible de passer plusieurs arguments à une méthode et de récupérer plusieurs valeurs de retour, exactement comme un appel de sous-programme.

   (Res1, Res2) = "Mon_Objet"."Ma_methode" (Arg1, Arg2)

Les v-tables

Nous avons rapidement défini dans la première partie la notion de v-table. Nous allons y revenir ici de manière plus détaillée en développant quelques applications.

Tous les PMC souscrivent à une interface commune appelée v-table. C'est cette dernière activée lors de la création du PMC qui va lui permettre de réaliser toutes les tâches standard de bas niveau qui lui sont spécifiques.

Le terme générique de v-table est l'abréviation de Virtual Function Table qui représente une structure destinée à contenir l'ensemble des éléments et des références qui la caractérise.

Les interfaces de la v-table sont, à de nombreux égards, semblables aux fonctions et aux méthodes que l'on a l'habitude de manipuler. Dans la littérature, elles sont généralement appelées fonctions de la v-table (Vtable functions), méthodes de la v-table (Vtable methods) ou bien encore entrées de la v-table (Vtable entries).

En fait, ces interfaces ne sont en aucun cas des sous-programmes ou des méthodes au sens ou on l'entend généralement.

À l'instar des méthodes relatives à un objet, les interfaces de la v-table sont définies pour une classe spécifique de PMC et peuvent être invoquées sur n'importe quel membre créé à partir de cette classe. De la même manière, dans la définition d'une v-table on va utiliser le mot clé self pour décrire l'objet invoqué.

C'est à ce niveau que vont s'arrêter les similitudes, car contrairement aux sous-programmes ordinaires, il n'est en aucun cas possible d'invoquer directement une méthode attachée à une v-table et elles n'héritent pas non plus d'une hiérarchie de classes comme le font les méthodes classiques.

Maintenant que nous avons défini la terminologie, nous allons pouvoir décrire de manière détaillée ce qu'est une v-table et de quelle manière elle sera utilisée dans le cadre de la machine virtuelle.

L'interface v-table est le seul et unique moyen pour accéder aux données du PMC si on désire les consulter ou de les modifier, ce sera aussi aussi de cette manière que sera invoqué le PMC si ce dernier est représentatif d'un sous programme. Ils ne sont jamais appelés directement à partir du code PIR mais de manière interne par la machine virtuelle en faisant référence à des codes opérations spéciaux ou en fonction de comportements appropriés.

Par exemple, le code opération invoque permet de faire référence à la v-table spécifique d'un PMC alors que le code opération inc appelle la méthode appropriée effectuant un incrément sur la valeur de l'objet défini par le PMC de référence en fonction du type représenté.

En définitive, cette interface permet au programmeur de faire évoluer la manière même dont la machine virtuelle accède aux données, et même de modifier le comportement des codes opérations, on dit surcharger, vis à vis ces données.

Il est en effet possible de définir (de surcharger) l'interface d'une v-table directement dans un programme PIR en utilisant l'indicateur :vtable dans une déclaration de sous programme.

Cette technique sera utilisée pour sous-classer un PMC existant dans du code PIR afin de créer un nouveau type de données comportant des méthodes d'accès personnalisées.

  .sub "set_integer" :vtable
      # Positionne la valeur entière du PMC
  .end

Ici, le sous-programme invoqué doit conserver le nom défini dans l'interface de la v-table qu'il est destiné à mettre en œuvre. Toutes les interfaces de la v-table ont des noms spécifiques et il est interdit d'en changer une en lui donnant un nom arbitraire.

Cependant, si on désire renommer la fonction avec un nom différent, tout en continuant à l'utiliser comme une interface de la v-table, il est possible d'ajouter un paramètre à l'indicateur.

  .sub "MySetInteger" :vtable("set_integer")
      # Positionne la valeur entière du PMC 
  .end

Les interfaces de la v-table proposent aussi souvent l'indicateur :method afin de pouvoir les utiliser directement dans du code PIR comme des méthodes en plus d'être utilisées comme des v-tables par la machine virtuelle. C'est ainsi que nous pouvons trouver :

  .namespace ["MaClasse"]
  .sub 'Chaine' :vtable("get_string") :method
      $S0 = "Bonjour!"
      .return($S0)
  .end

  .local pmc classe = new "MaClasse"
  say classe                 # Affiche Le nom de la classe.
  $S0 = classe               # Stocke Bonjour dans $S0.
  $S0 = classe.'Chaine'()    # Stocke Bonjour dans $S0.

Nous allons maintenant appliquer tout ceci en écrivant un programme.

  coruscant chris$ cat vtable.pir 
  .sub main :main
    .local pmc classe
    classe = newclass "MaClasse"
    print "Nom de la classe : "
    say classe
    $P1 = new classe
    $P2 = $P1
    print "Premier appel. Contenu du registre : "
    say $P2           
    $P2 = $P1."Chaine"()   
    print "Second appel. Contenu du registre : "
    say $P2
  .end

  .namespace ["MaClasse"]
  .sub "Chaine" :vtable("get_string")  :method
    $P0 = new "String"
    $P0 = "Bonjour !"
    .return($P0)
  .end

  coruscant chris$ parrot vtable.pir 
  Nom de la classe : MaClasse
  Premier appel. Contenu du registre : Bonjour !
  Second appel. Contenu du registre : Bonjour !
  coruscant chris$

Lors du premier appel, le nom de la classe créée a été stocké dans un PMC ($P1). Faire référence au PMC en question ($P2 = $P1) active la méthode qui lui est attachée et renvoie la chaîne de caractères. Mais il est aussi possible de préciser le nom complet de la méthode $P2 = $P1."Chaine"().

Les PMC et les classes.

Bien que PIR n'utilise pas à proprement parler la syntaxe traditionnelle des langages orientés objet, il offre toutes les fonctionnalités indispensables pour les gérer presque aussi bien que les langages spécialisés.

Les PMC ne sont pas des classes au sens où on l'entend généralement, mais des structures polymorphes qui peuvent couvrir un grand nombre de types prédéfinis. Nous avons vu, au chapitre précédent comment on les utilise pour travailler sur les agrégats (listes, hash).

Comme les PMC ont une interface standard, la v-table qui représente la liste des fonctions que tout PMC peut mettre en œuvre, un PMC donné peut, au choix, implémenter explicitement la fonction correspondante ou choisir de laisser Parrot mettre en place la fonction par défaut.

Les sous-classes.

Il est toujours possible de définir des sous-classes de PMC existants pour associer de nouvelles informations ou de nouvelles méthodes au PMC de base.

La création d'une nouvelle sous-classe à partir d'une classe de base se fait fait en utilisant le mot clé subclass.

Cette opération va nous permettre de récupérer une classe PMC qui pourra être utilisée pour créer de nouvelles classes, apporter des modifications en ajoutant des attributs et des méthodes et créer des objets dépendant de cette classe.

Dans l'exemple qui suit, nous allons créer les sous-classes MaChaine et MaSousChaine, définies à partir de la classe existante String à laquelle a été rattachée une nouvelle méthode visualise.

  coruscant chris$ cat classes.pir 
  .sub main :main
    $P0 = new "String"
    $P0.'visualise'("Appel direct de la classe 'String'.")
  # Creation d'une sous classe de la classe "String".
    $P2 = get_class "String"
    $P1 = subclass  $P2, "MaChaine"
  # Creation d'une sous classe par reference.
    $P3 = new $P1
    $P3.'visualise'("Appel 1 de la sous classe 'MaChaine'" )
  # Creation d'une sous classe par nom.
    $P4 = new "MaChaine"
    $P4.'visualise'("Appel 2 de la sous classe 'MaChaine'")
    .local pmc nomclasse
  # Creation d'une sous classe a partir de la sous classe "MaChaine"
    $P2 = get_class "MaChaine"
    $P5 = subclass $P2, "MaSousChaine"
    nomclasse = new $P5
    nomclasse.'visualise'("Appel de la sous classe 'MaSousChaine'")
  .end
  
  .namespace ["String"]
  .sub "visualise" :method
    .param string x
    print "Message : "
    say x
  .end
  coruscant chris$ parrot classes.pir 
  Message : Appel direct de la classe 'String'.
  Message : Appel 1 de la sous classe 'MaChaine'
  Message : Appel 2 de la sous classe 'MaChaine'
  Message : Appel de la sous classe 'MaSousChaine'
  coruscant chris$

Il est évident dans cet exemple que les sous-classes héritent toutes de la méthode qui a été ajoutée à la classe de base.

Utilisation des objets.

La machine virtuelle a, de manière native, la capacité de créer et de manipuler des objets. Initialement, l'objectif de Parrot a été l'implémentation de Perl6, qui possède toutes les fonctionnalités de la programmation orientée objet [Rakudo]. Cet objectif ayant été étendu, plusieurs autres langages sont susceptibles de voir leur installation concrétisée sur la plate-forme en question [Langages]. Parrot doit, de ce fait, être capable de fournir un support utilisable par eux (Python, Ruby, PHP, JavaScript, Lua, etc) lesquels sont eux aussi majoritairement orientés objet [Langages].

Création de classes..

Nous avons rapidement vu comment utiliser des classes existantes (String) pour les enrichir ou pour leur rattacher des sous-classes. Nous allons voir maintenant comment créer ses propres classes et instancier des objets.

Pour y parvenir, nous devons revenir sur la notion d'espace de noms.

Les espaces de noms

C'est cette construction qui va nous permettre de regrouper un ensemble de fonctionnalités dans une entité unique. De cette manière, plusieurs sous-routines peuvent avoir un nom identique à condition d'appartenir à des espaces de noms différents.

Par exemple, il sera possible de positionner toutes les routines traitant des individus sans l'espace de noms Personnes et toutes celles traitant de la programmation dans un espace de noms différent, Processus dans chacun des deux espaces de noms nous pouvons alors créer une fonction identifie() ayant chacune ses caractéristiques propres.

  .namespace ["Personne"]
    .sub identifie :method
      say "Dans l'espace de noms Personne."
    .end
  .namespace ["Processus"]
    .sub identifie :method
      say "Dans l'espace de noms Processus."

La directive .namespace nous permet de spécifier le nom d'espace dans lequel vont se regrouper les méthodes attachées à l'espace. On fera appel à la méthode identifie de l'espace de noms Personne sous la forme "Personne"."identifie"() et à celle de l'espace de noms Processus "Processus"."identifie"().

Un espace de noms se termine dès l'apparition d'une nouvelle directive .namespace qui va changer le nom d'espace ou bien lorsque la fin du programme est atteinte (.end).

Il faut noter que les déclarations d'espaces de noms en PIR sont, à la syntaxe près, très similaires à la déclaration newclass de Perl. Il existe cependant quelques différences sur lesquelles nous allons revenir.

Les classes en PIR.

En PIR, la création de classes est quelque chose de relativement facile grâce aux opérateurs spécifiques intégrés au langage.

Définissons pour commencer la directive newcass qui va nous permettre, comme son nom l'indique, de créer une nouvelle classe. Elle se présente sous la forme :

   $P0 = newclass "Nom_de_la_classe"

Il est alors indispensable d'instancier les objets qui vont dépendre à la classe qui vient d'être définie. Cette opération ne présente pas beaucoup plus de difficultés :

 Mon_Objet = new "Nom_de_la_classe"

En reprenant l'exemple qui a été évoqué plus haut, nous pouvons procéder aux déclarations suivantes :

  .sub "main" :main
    .local pmc Personne
    .local pmc Processus
    .local pmc Ref_Personne, Ref_Processus
    Ref_Personne = newclass "Personne"
    Ref_Processus = newclass "Processus"
    $P10 = new "Personne"
    $P11 = new "Processus"
  .end

La valeur de retour générée par la directive newclass nous sera très utile lors de développements ultérieurs.

Il nous reste, pour que le programme soit complet, à déclarer les deux espaces de nom Personne et Processus.

C'est ainsi que nous obtenons le code opérationnel suivant :

  coruscant chris$ cat objets.pir 
  .sub "main" :main
    .local pmc Personne
    .local pmc Processus
    .local pmc Ref_Personne, Ref_Processus
    Ref_Personne = newclass "Personne"
    Ref_Processus = newclass "Processus"
  .end

  .namespace ["Personne"]
  .sub individu
  .end
    
  .namespace ["Processus"]
  .sub programme
  .end

  coruscant chris$ parrot objets.pir
  coruscant chris$

Si son exécution ne génère aucune erreur, nous n'obtenons aucun résultat car il n'y a rien à afficher.

Les Méthodes.

Définition de méthodes.

Nous allons maintenant attacher des méthodes aux objets que nous savons créer.

Il a déjà été précisé que pour distinguer une méthode d'un sous-programme, on dispose dans PIR d'un marqueur syntaxique, le modificateur :method qui, ajouté après l'identification du code lors de la déclaration de la routine, met en évidence cet état de fait.

Dans l'exemple qui suit, la directive .namespace nous permet de créer une classe EtreHumain. Dans ce nouvel espace de noms définissons une méthode identite qui récupère deux paramètres. L'instruction .sub ident :method nous permet de réaliser cette opération.

Dès lors, toutes les sous-classes crées à partir de la classe EtreHumain vont hériter de la méthode qui vient de lui être attachée.

Nous créons maintenant trois références $P10, $P11 et $P12 à des sous classes Homme, Femme et Enfant dérivées de la classe EtreHumain

Il nous est maintenant possible de créer autant de nouveaux objets appartenant aux sous-classes en question, tous héritant de l'environnement de la classe d'origine.

  coruscant chris$ cat etrehumain.pir
  .sub _ :main
    $P0 = newclass "EtreHumain"
    $P1 = get_class "EtreHumain"
    $P10 = subclass $P1, "Homme"
    $P11 = subclass $P1, "Femme"
    $P12 = subclass $P1, "Enfant"
     
    .local pmc Jean
    Jean = new $P10
    
    .local pmc Julie
    Julie = new $P11
    
    .local pmc Olivier  
    Olivier = new $P12
    
    .local pmc Claude  
    Claude = new $P12
    
    .local pmc Marie  
    Marie = new $P12
    
    Jean.'identite'("Jean", "homme")
    Julie.'identite'("Julie", "femme")
    Marie.'identite'("Marie", "enfant")
    Olivier.'identite'("Olivier", "enfant")
    Claude.'identite'("Claude", "enfant")
  .end

  .namespace ["EtreHumain"]
    .sub identite :method
      .param string nom
      .param string sexe
      print "Je suis un(e) "
      print sexe
      print " et je m'appelle "
      say nom
  .end

  coruscant chris$ parrot etrehumain.pir
  Je suis un(e) homme et je m'appelle Jean
  Je suis un(e) femme et je m'appelle Julie
  Je suis un(e) enfant et je m'appelle Marie
  Je suis un(e) enfant et je m'appelle Olivier
  Je suis un(e) enfant et je m'appelle Claude
  coruscant chris$

Dans un premier temps, la classe EtreHumain a été déclarée $P0 = newclass "EtreHumain" et une méthode (identite) a été créée dans l'espace de noms correspondant .sub identite :method cette méthode récupère deux paramètres nom et sexe.

Par la suite, une fois les sous-classes définies c'est la directive new qui va permettre l'instanciation effective d'objets.

Appel des méthodes.

À ce propos, nous avons déjà noté que le nom de la méthode se présente entre quotes. Rappelons que ceci est dû au fait que, si les quotes étaient absentes, l'identificateur serait considéré comme un nom de variable qui aurait dû être déclaré comme un symbole local .local.

C'est ainsi que, au lieu d'écrire

  Jean."identite"("Jean", "homme")

On aurait pu écrire

  .local string MonIdentite
  MonIdentite = "identite"
  Jean.MonIdentite ("Jean", "homme")

Voyons un autre exemple dans lequel l'appel de la méthode se fait par l'intermédiaire d'une variable.

  coruscant chris$ cat methode.pir
  .namespace ["Message"]
    .sub Bonjour :method
     say "Bonjour a tous."
  .end

  .sub Aurevoir :method
    say "Au Revoir."
  .end

  .namespace []
  .sub "Appel de Methodes" :main
    .local pmc MaClasse
    MaClasse = newclass "Message"
    .local pmc Annonce
    Annonce = new "Message"

    .local string methode
    methode = "Bonjour"
    Annonce.methode()
    methode = "Aurevoir"
    Annonce.methode()
  .end

  coruscant chris$ parrot methode.pir
  Bonjour a tous.
  Au Revoir.
  coruscant chris$

La variable methode de type String contient la chaîne de caractères représentative du nom de la méthode que l'on souhaite activer.

Les objets.

Les caractéristiques.

Nous nous sommes contentés jusqu'à présent de créer des classes et de leur associer des méthodes. Nous n'avons pas encore vu comment associer des caractéristiques spécifiques aux nouvelles classes que nous construisons.

C'est par l'intermédiaire du PMC qui est positionné lors de l'exécution de la directive newclass que ces opérations pourront être réalisées.

  $P0 = newclass "Nom_de_la_classe"
  .local pmc MaClasse
  MaClasse = newclass "Nom_de_la_classe"

Jusqu'à présent, nous n'avons pas encore utilisé cette information. Le PMC en question contient le descripteur qui va permettre de référencer la classe et d'en décrire les attributs, et ce sont les données qui vont permettre de la caractériser.

Les instructions permettant de manipuler les attributs sont :

La seule contrainte pour pouvoir effectuer ces opérations est que toute référence aux valeurs de ces attributs doit impérativement se faire par l'intermédiaire d'un PMC.

  coruscant chris$ cat etrehumain.pir
  .sub _ :main
    $P0 = newclass "EtreHumain"
  # Attributs de l'etre humain, nom et sexe.
    addattribute $P0, "Sexe"
    addattribute $P0, "Nom"
    .local pmc etre_humain
    etre_humain = get_class "EtreHumain"
    .local pmc homme
    .local pmc femme
    homme = subclass etre_humain, "Homme"
    femme = subclass etre_humain, "Femme"

    .local pmc Jean
  # Creation de l'objet "Jean"
    Jean = new homme
    .local pmc sexe
    sexe = new "String"
    sexe = "Masculin"
    .local pmc nom
    nom = new "String"
    nom = "Jean"
  # Sexe : "Masculin"
    setattribute Jean, "Sexe", sexe
  # Nom : "Jean"
    setattribute Jean, "Nom", nom
    
    .local pmc Julie
  # Creation de l'objet "Julie"
    Julie = new femme
    .local pmc sexe
    sexe = new "String"
    sexe = "Feminin"
    .local pmc nom
    nom = new "String"
    nom = "Julie"
  # Sexe : "Feminin"
    setattribute Julie, "Sexe", sexe
  # Nom : "Julie"
    setattribute Julie, "Nom", nom
    
    .local pmc Christian
  # Creation de l'objet "Christian"
    Christian = new homme
    .local pmc sexe
    sexe = new "String"
    sexe = "Masculin"
    .local pmc nom
    nom = new "String"
    nom = "Christian"
  # Sexe : "Masculin"
    setattribute Christian, "Sexe", sexe
  # Nom : "Christian"
    setattribute Christian, "Nom", nom
    
  # Appel des methodes attachees aux objets.
    Jean.'identite'("Bonjour")
    Christian.'identite'("Salut")
    Julie.'identite'("Hello")
  .end

  .namespace ["EtreHumain"]
    .sub identite :method
      .param string message
      .local pmc sexe
      sexe = new "String"
      sexe = getattribute self, "Sexe"
      .local pmc nom
      nom = new "String"
      nom = getattribute self, "Nom"
      print message
      print " je m'appelle "
      print nom
      print " et je suis de sexe "
      say sexe
  .end

  coruscant chris$ parrot etrehumain.pir
  Bonjour je m'appelle Jean et je suis de sexe Masculin
  Salut je m'appelle Christian et je suis de sexe Masculin
  Hello je m'appelle Julie et je suis de sexe Feminin
  coruscant chris$

Étudions en détail le contenu du programme que nous venons d'écrire.

Pour commencer, on crée la classe appelée EtreHumain, cette opération renvoie un PMC qui contient le descripteur de cette nouvelle classe.

Ce descripteur sera alors utilisé pour ajouter deux attributs à cette classe. Le premier Nom sera utilisé pour stocker le nom de la personne et le second Sexe son sexe.

C'est ici qu'on voit l'avantage de l'étiquetage d'un sous-programme en tant que méthode, ce sera, pour nous, le moyen de récupérer un PMC appelé self qui référence l'objet sur lequel agit le code.

C'est cette facilité qui est utilisée par la méthode identite pour récupérer le contenu des attributs Nom et Sexe à partir du PMC self afin de les afficher.

La situation est donc la suivante :

On a créé un espace de noms EtreHumain auquel a été rattachée une méthode identite qui récupère un paramètre dans sa liste d'appel et accède aux attributs Nom et Sexe de l'objet.

On a ensuite défini dans l'espace de noms que l'on vient de créer deux sous-classes Homme et Femme dont les références seront respectivement contenues dans les PMC homme et femme qui vont hériter des caractéristiques de l'espace de noms EtreHumain, à savoir la méthode identite et les deux attributs Nom et Sexe.

Tout ce qui nous reste à faire maintenant est de créer de nouveaux humains et de positionner leurs caractéristiques.

Nous avons évoqué la contrainte de devoir utiliser un PMC pour affecter une valeur aux caractéristiques. C'est la raison d'être des déclarations des PMC de type String lors de la création des nouveaux objets.

  .local pmc sexe
  sexe = new "String"
  sexe = "****"
  .local pmc nom
  nom = new "String"
  nom = "****"
  setattribute ****, "Sexe", sexe
  setattribute ****, "Nom", nom

Une fois instanciés, les PMC sont transmis par l'intermédiaire de l'instruction setattribute.

Initialisation d'un objet.

Lors de la création d'un objet, il est possible de déclarer dans l'espace de nom des méthodes spécifiques. L'une d'elle __init sera automatiquement exécutée à chaque nouvelle instanciation. Elle pourra servir, par exemple, pour procéder à l'initialisation de certains attributs.

  coruscant chris$ cat initialisation.pir
  .sub main :main
    .local pmc perroquet, ara, parrot
    perroquet = newclass 'Conure'
    addattribute perroquet, "Nom"
    ara = new ["Conure"]
    $P2 = getattribute ara, "Nom"
    say $P2
    parrot = new ["Conure"]
    $P2 = getattribute parrot, "Nom"
    say $P2
  .end

  .namespace ["Conure"]
  .sub __init :method
    $S0 = typeof self
    $P0 = new ["String"]
    .local pmc STDIN
    STDIN = getstdin
    print "Vous creez un nouveau "
    print $S0
    print ". Quel est son nom ? "
    $S0 = readline STDIN
    chopn $S0, 1
    $P0 = $S0
    setattribute self, "Nom", $P0
  .end

  coruscant chris$ parrot initialisation.pir
  Vous creez un nouveau Conure. Quel est son nom ? Cacatoes
  Cacatoes
  Vous creez un nouveau Conure. Quel est son nom ? Half Moon
  Half Moon
  coruscant chris$

De nouveau, à l'intérieur de la définition de l'interface de la v-table, la variable locale self contient le PMC sur lequel l'interface de la v-table est invoqué, exactement comme dans la déclaration d'une méthode.

Un programme d'application.

Le code opération subclass permet à une sous-classe d'hériter des attributs et des méthodes et des méthodes d'une classe de base.

Créons maintenant une classe de base Jungle a laquelle seront rattachés les quatre caractéristiques et les quatre méthodes qui seront communes à tous les êtres de la forêt.

Nous pouvons alors déclarer des sous-classes des habitants de la jungle, les fauves, les reptiles et les conures. Chacune de ces sous-classes hérite des caractéristiques et des méthodes de la classe Jungle.

Pour faciliter les déclarations et l'édition des résultats, nous créons deux macro-instructions.

La première CreerEntree permet de créer une nouvelle entrée dans la classe Jungle en positionnant les divers attributs qui lui sont transmis par l'intermédiaire de la liste d'appel et d'en retourner la référence dans un PMC. La seconde va tout simplement éditer les données d'un objet de la classe. Elle n'a besoin que de connaître la référence de l'objet pour en extraire toutes les caractéristiques.

  coruscant chris$ cat jungle.pir
  .macro CreerEntree (Ref, Famille, Espece, Nom, Sexe, Naissance)    
    .local pmc .Ref
    .Ref = new .Famille
    $P0 = new "String"
    $P0 = .Espece
    setattribute .Ref, "Espece", $P0
    $P0 = new "String"
    $P0 = .Nom
    setattribute .Ref, "Nom", $P0
    $P0 = new "String"
    $P0 = .Sexe
    setattribute .Ref, "Sexe", $P0
    $P0 = new "Integer"
    $P0 = .Naissance
    setattribute .Ref, "Naiss", $P0
  .endm

  .macro Edite (Ref)
    $S1 = .Ref.'Espece'()
    print "  Je suis un(e) "
    say $S1
    $S1 = .Ref.'Nom'()
    print "    Je m'appelle "
    say $S1
    $S1 = .Ref.'Sexe'()
    print "    Je suis de sexe "
    say $S1
    $I1 = .Ref.'Age'()
    print "    Je suis ne(e) en "
    print $I1
    $I0 = time
    $S0 = localtime $I0
    $S0 = substr $S0, -5, 4
    $I0 = $S0
    $I0 -= $I1
    print " et j'ai "
    print $I0
    say " ans.\n"
  .endm

  .namespace ["Jungle"]
  .sub Espece :method
    $P0 = new "String"
    $P0 =  getattribute self, "Espece"
    .return ($P0)
  .end
  .sub Nom :method
    $P0 = new "String"
    $P0 =  getattribute self, "Nom"
    .return ($P0)
  .end
  .sub Sexe :method
    $P0 = new "String"
    $P0 =  getattribute self, "Sexe"
    .return ($P0)
  .end
  .sub Age :method
    $P0 = new "Integer"
    $P0 =  getattribute self, "Naiss"
    .return ($P0)
    $I0 = time
  .end
  
  .namespace []
  .sub _ :main
    $P0 = newclass "Jungle"
    addattribute $P0, "Espece"
    addattribute $P0, "Nom"
    addattribute $P0, "Sexe"
    addattribute $P0, "Naiss"
    $P0 = subclass "Jungle", "Fauve"
    $P1 = subclass "Jungle", "Conure"
    $P2 = subclass "Jungle", "Reptile"
    .local pmc bag
    .CreerEntree (bag, "Fauve", "Panthere Noire", "Bagheera", "Femelle", 1967)
    .local pmc parrot
    .CreerEntree (parrot, "Conure", "Perroquet", "Half Moon", "Male", 2008)
    .local pmc kaa
    .CreerEntree (kaa, "Reptile", "Boa", "Kaa", "Male", 2000)
    .local pmc shere
    .CreerEntree (shere, "Fauve", "Tigre", "Shere Khan", "Male", 1998)
    say "Edition des fauves."
    .Edite (bag)
    .Edite (shere)
    say "Edition des conures."
    .Edite (parrot)
    say "Edition des reptiles."
    .Edite (kaa)
  .end

  coruscant chris$ parrot jungle.pir
  Edition des fauves.
    Je suis un(e) Panthere Noire
      Je m'appelle Bagheera
      Je suis de sexe Femelle
      Je suis ne(e) en 1967 et j'ai 42 ans.
    Je suis un(e) Tigre
      Je m'appelle Shere Khan
      Je suis de sexe Male
      Je suis ne(e) en 2000 et j'ai 11 ans.
  Edition des conures.
    Je suis un(e) Perroquet
      Je m'appelle Half Moon
      Je suis de sexe Male
      Je suis ne(e) en 2008 et j'ai 1 ans.
  Edition des reptiles.
    Je suis un(e) Boa
      Je m'appelle Kaa
      Je suis de sexe Male
      Je suis ne(e) en 2000 et j'ai 9 ans.
  coruscant chris$

Mémorisation des objets dans un agrégat.

Il est possible une fois les objets créée de mémoriser leur référence dans une structure. C'est ce que nous allons voir dans le programme qui suit.

  coruscant chris$ cat repertoire.pir
  .macro Enregistre (Ref, Ag, Nom, Prof, Nais, Mail) 
  .Ref = new "Identite"   
    .Ref.'Remplis'(.Prof, .Nais, .Mail)
    .Ag[.Nom] = .Ref
  .endm

.namespace ["Identite"] .sub Remplis :method .param string prof .param int nais .param string mail $P0 = new 'String' $P0 = prof setattribute self, "Profession", $P0 $P0 = new 'Integer' $P0 = nais setattribute self, "Naissance", $P0 $P0 = new 'String' $P0 = mail setattribute self, "Mail", $P0 .end

  .sub Edite :method
    .param string nom
    print "Nom : "
    say nom
    $P0 = getattribute self, "Profession"
    print "Profession : "
    say $P0
    $P0 = getattribute self, "Naissance"
    print "Date de naissance : "
    say $P0
    $P0 = getattribute self, "Mail"
    print "Mail : "
    say $P0
  .end
  
  .namespace []
    .sub _ :main
    .local pmc Repertoire
    Repertoire = newclass "Identite"
    addattribute Repertoire, "Profession"
    addattribute Repertoire, "Naissance"
    addattribute Repertoire, "Mail"
    
    .local pmc Agenda
    Agenda = new "Hash"
    
    .local pmc chris
    $S0 = "Christian Aperghis"
    $S1 = "Enseignant"
    $I0 = 1947
    $S2 = "chris@monmail.fr"
    .Enregistre(chris, Agenda, $S0, $S1, $I0, $S2)
    
    .local pmc seb
    $S0 = "Sebastien Aperghis"
    $S1 = "Ingenieur"
    $I0 = 1977
    $S2 = "sebastien@mommail.fr"
    .Enregistre(seb, Agenda, $S0, $S1, $I0, $S2)
    
    .local pmc cles
    cles = new 'Iterator', Agenda
  EXPLORE:
    unless cles goto FIN
    $S0 = shift cles
    $P0 = Agenda[$S0]
    $P0.'Edite'($S0)
    print "\n"
    goto EXPLORE
  FIN:
  .end

  coruscant chris$ parrot repertoire.pir
  Nom : Christian Aperghis
  Profession : Enseignant
  Date de naissance : 1947
  Mail : chris@monmail.fr
  
  Nom : Sebastien Aperghis
  Profession : Ingenieur
  Date de naissance : 1977
  Mail : sebastien@monmail.fr
  coruscant chris$

Les références des divers objets qui ont été créés sont stockés dans un hash. La clé d'entrée est le nom de la personne, la valeur contient la référence de l'objet correspondant qui mémorise les informations relatives au nom en question.

Ici aussi, une macro instruction nous permet de procéder à la création du hash.

La gestion des exceptions.

On dispose dans PIR d'un mécanisme permettant de gérer au mieux les exceptions qui peuvent se présenter lors de l'exécution d'un programme.

Une exception est un événement inattendu, généralement provoqué par une erreur dans le code. Il sera en fait géré comme un objet.

Les exceptions peuvent être capturées par des gestionnaires spécifiques, que l'on appelle handlers.

C'est ce mécanisme permet à un programme de tenter de récupérer au mieux de ses intérêts l'erreur qui est apparue au lieu de provoquer un crash système.

Comme tous les objets utilisés dans Parrot, les exceptions sont gérées par l'intermédiaire des registres PMC.

Ils vont valider l'accès à un certain nombre d'indicateurs binaires permettant de déterminer le type et l'emplacement de l'erreur et le programme qui l'a provoquée.

De nombreuses exceptions sont utilisées de manière interne dans la machine virtuelle pour spécifier les conditions d'erreur.

Les codes opération die et warn par exemple, permettent de générer une exception interne qui sera récupérée par le programme. Mais une opération arithmétique telle que div peut, elle aussi, en générer une dans le cas où le diviseur serait égal à zéro.

Par ailleurs, on dispose d'une instruction throw qui permet d'en créer artificiellement.

Les instructions qui génèrent des exceptions.

Voici un exemple très simple de code qui génère une exception en utilisant l'instruction throw.

  $P0 = new "Exception"
  throw $P0

Si un gestionnaire d'exception est disponible dans le code courant, alors le contrôle lui sera passé et elle sera prise en compte. Dans le cas contraire, le programme se termine.

Prendre en compte une exception.

Les exceptions sont en définitive des objets gérés par un PMC. En tant que tel elles peuvent donc avoir un certain nombre de caractéristiques utiles.

On peut, par exemple, passer le contrôle à un morceau de code qui se contentera d'afficher un message. Dans un premier temps, le mécanisme peut se présenter comme suit :

  coruscant chris$ cat exception.pir
  .sub _ :main
    # Gestionnaire de l'exception.
    $P0 = new "Exception"
    $P1 = new "String"
    $P1 = "Ceci est le message d'erreur de l'exception."
    $P0["message"] = $P1
    say "Avant exception"
    # Declenchement de l'exception.
    throw $P0
    say "Apres exception"
  .end

  coruscant chris$ parrot exception.pir
  Avant exception
  Ceci est le message d'erreur de l'exception.
  current instr.: '_' pc 15 (test.pir:9)
  coruscant chris$

On voit bien apparaître le premier message envoyé par le programme avant la génération de l'exception throw $P0 puis, le message d'erreur généré par l'exception elle-même et l'indication que le programme s'est terminé sur l'instruction 9 current instr.: '_' pc 15 (test.pir:9). Par contre, comme l'exception termine le programme, le message Apres l'exception ne sera jamais affiché.

Autre attribut disponible, la gravité et le type de l'exception.

   $P0 ["severity"] = 10 # Une valeur entière.
   $P0 ["type"] = 5 # Une valeur entière.

Il y a aussi la possibilité de créer n'importe quelle caractéristique additionnelle

   $P0 ["Additionnelle"] = $P2 # Une référence à un PMC.

Dans ces conditions, il est nécessaire de récupérer les caractéristiques de l'exception dans un gestionnaire comme nous allons le voir.

Les gestionnaires d'exceptions.

Ce sont les codes opération push_eh et pop_eh qui manipulent les gestionnaires d'exception.

Tout contexte dispose d'une liste de gestionnaires répertoriés. L'instruction push_eh ajoute un nouveau gestionnaire à la fin de la liste alors que l'instruction pop_eh en retire un. Bien que le choix des noms choisis push et pop évoquent une structure de pile, il n'en est rien.

La création d'une structure standard de gestion d'exception est la suivante :

    EXCEPTION:
      push_eh CAPTURE
       * * *
      goto FIN

    CAPTURE:
      .get_results ($P0)
  
    FIN:
      pop_eh

Dans les lignes que nous venons d'écrire, l'étiquette EXCEPTION permet de spécifier le début du code qui gère l'exception. Dans ce bloc, on insère le label CAPTURE au moyen de l'instruction (push_eh CAPTURE), représentant l'adresse du code qui gère l'exception. C'est dans ce bloc, qu'il est possible de récupérer des paramètres envoyés par le gestionnaire (.get_results ($P0)), et lorsque la procédure se termine, on retire le gestionnaire de la liste pop_eh.

Comme tous les autres types de données, le gestionnaire d'exception est référencé par un PMC. C'est le fait d'exécuter l'instruction push_eh LABEL qui va automatiquement créer le PMC gestionnaire.

Si on le désire, il est aussi possible de procéder à une création explicite.

  $P0 = new "ExceptionHandler"
  set_addr $P0, GESTIONNAIRE
  push_eh $P0
    . . . 
  GESTIONNAIRE:

L'objet PMC qui a géré l'exception peut être récupéré dans le gestionnaire au moyen de l'instruction .get_results ()

  GESTIONNAIRE:
    .local pmc err
    .get_results (err)
    . . .

Il est possible de consulter et d'analyser l'ensemble des attributs afin d'obtenir l'ensemble des informations sur le problème qui a généré l'exception.

  coruscant chris$ cat exception.pir
    .sub _ :main
    # Gestionnaire de l'exception.
    $P0 = new "Exception"
    $P1 = new "String"
    $P2 = new "Integer"
    $P2 = 100
    $P1 = "Traitement de l'exception."
    $P0["message"] = $P1
    $P0 ["severity"] = 100
    $P0 ["type"] = 5
    push_eh GESTIONNAIRE
    say "Avant exception"
    # Declenchement de l'exception.
    throw $P0
    say "Apres exception"
  
  GESTIONNAIRE:
    .get_results($P0)
    pop_eh
    print "Type de l'exception : "
    $I1 = $P0["type"]
    say $I1
    print "Message de l'exception : "
    $S1 = $P0["message"]
    say $S1
    print "Severite de l'exception : "
    $I1 = $P0["severity"]
    say $I1
  .end

  coruscant chris$ parrot exception.pir
  Avant exception
  Type de l'exception : 5
  Message de l'exception : Traitement de l'exception.
  Severite de l'exception : 100
  coruscant chris$

Comme tous les handlers déclarés ne sont pas destinés à gérer toutes les exceptions, si on se trouve dans l'impossibilité de le faire un régisseur peut rediriger le problème vers un autre gestionnaire de la liste.

Dans ces conditions, les exceptions vont se propager à travers les éléments de la liste jusqu'à trouver le gestionnaire par défaut qui permet la sortie du programme.

  coruscant chris$ cat exception.pir
  .sub main :main
    say "Debut du programme."
    push_eh GESTIONNAIRE
    $P1 = new ['Exception']
    $P2 = new ['String']
    set $P2, "Grosse faute."
    setattribute $P1, 'message', $P2
    throw $P1
    say "Cette ligne ne sera jamais affichee."
    end
  GESTIONNAIRE:
    .local pmc mess
    .get_results (mess)
    say "Gestion de l'exception."
    typeof $S1, mess
    print "Le parametre transmis est de type : "
    say $S1
    print "Le parametre transmis contient : "
    say mess
    pop_eh
  .end

  coruscant chris$ parrot exception.pir
  Debut du programme.
  Gestion de l'exception.
  Le parametre transmis est de type : Exception
  Le parametre transmis contient : Grosse faute.
  coruscant chris$

Un gestionnaire est référencé par un label au sens PIR du terme, et le flux du programme va se brancher à l'emplacement ainsi spécifié lorsque survient l'exception considérée.

Si cette exception est due à un problème de programmation, le message qui est récupéré contient la description du problème.

Par exemple dans le cas d'une division par zéro :

  coruscant chris$ cat zerodiv.pir
  .sub main :main
    say "Debut du programme."
    push_eh GESTIONNAIRE
    $P1 = new ['Exception']
    $P2 = new ['String']
    setattribute $P1, 'message', $P2
    $I1 = 10 / 0
    end
  GESTIONNAIRE:
    .local pmc mess
    .get_results (mess)
    say "Gestion de l'exception."
    print "Le parametre transmis contient : "
    say mess
    pop_eh
  .end

  coruscant chris$ parrot zerodiv.pir
  Gestion de l'exception.
  Le parametre transmis contient : Divide by zero
  coruscant chris$

Une nouvelle fois, il est facile de constater que l'évènement ne se différencie pas des objets classiques gérés par des PMC

Les gestionnaires sont positionnés au moment voulu avec toutes leurs caractéristiques et, si une exception se présente, elle sera prise en compte.

Conclusion.

Force est de constater que le langage intermédiaire de la machine Parrot offre de nombreuses possibilités.

Bien qu'une nouvelle mise à jour de la machine virtuelle soit disponible mensuellement, PIR ne devrait pas, dans l'immédiat, voir apparaître des modifications majeures.

Parrot Intermediate Representation n'est ni un Langage évolué, ni un assembleur. C'est une représentation à base de registres typés qui sait aussi gérer les espaces de noms et qui a l'avantage de proposer à l'utilisateur un nombre illimité de registre symboliques. Sa capacité de créer de multiples gestionnaires pour prendre en compte les divers évènements qui peuvent se présenter et les outils de mise au point disponibles permettent de créer des applications avec un minimum de difficultés.

 =head1 Références

[poo] Programmation orientée objet, http://fr.wikipedia.org/wiki/Orienté_objet

[Parrot] Site de la machine virtuelle, http://www.parrot.org

[Noms] Les espaces de nom, http://fr.wikipedia.org/wiki/Espace_de_noms

[Rakudo] Le site de Rakudo, Perl6, http://www.rakudo.org

[Langages] Liste des langages, http://www.parrot.org/languages

[IE7, par Dean Edwards] [Validation du HTML] [Validation du CSS]