afficher >><< masquer ]
SAMPI - Editeur structuré
1. Le Problème et la Proposition
2. Le Langage Primitif de Représentation Textuelle
2.1. Présentation de la Syntaxe Concrète
2.2. Notations
2.3. Exemple de structuration des données
2.4. Exemple de structuration des traitements
2.5. Exemple de structurations connexes
2.5.1. Présentation
2.5.2. La macro-génération
2.5.22.1. Les données
2.5.22.2. Les traitements
2.5.3. Les symétries du programme
2.5.4. Annexe 1 : Construction du champ CHP_PRESENT
2.5.5. Annexe 2 : Les fonctions de traitement en Lisp
3. Le Langage Complété pour la Structuration des Textes
3.1. Présentation de la Syntaxe Complétée
3.2. Etude quantitative de l'évolution des programmes
3.3. L'édition syntaxique
3.4. étude de cas : le langage LTR3 et l'atelier ENTREPRISE
4. L'Enrichissement du Langage par de Nouveaux Concepts
4.1. Présentation de la Syntaxe Abstraite
4.2. Les difficultés
4.3. Compléter la Syntaxe
5. La Formalisation des Solutions Techniques
5.1. L'évaluation fonctionnelle
5.2. La structuration par les objets
5.3. Modèle sémantique comparé de l'évaluateur
5.4. Comparaison critique
5.5. Construction de la Syntaxe Abstraite
6. Les Comparaisons avec d'autres Approches
7. Les Perspectives
8. Les Editeurs
8.0. brisé sur la barrière de la complexité (une fois de plus)
8.1. L'éditeur ligne : Manuel de l'utilisateur
8.2. L'éditeur page : Guide de l'utilisateur
9. Les Aspects d'Implantation
9.1. Contexte d'évaluation
9.2. La Syntaxe Abstraite : Manuel du concepteur
9.3. L'éditeur page : Guide de l'implanteur
Références
Rubrique Perl-Javascript

La macro-génération

La macro-génération est une technique « vieille comme le monde », à la fois largement utilisée et largement critiquée. On l'utilise dans deux optiques :

Le gros avantage de la macro-génération en est sa simplicité. Mais cette simplicité ne doit pas masquer certains dangers qui lui sont liés :

On trouve dans [KoW 87] une syntaxe pour les macro-définitions en Lisp qui cherche à en faciliter la compréhension : au lieu de proposer classiquement une fonction qui permette de tester sur quelques exemples la macro-génération, on propose un moyen d'expression qui fait ressembler la macro-définition à sa forme macro-générée. Dans [TrY 80] on cherche à réduire l'initiative laissée au programmeur à l'utilisation d'une macro-définition (COBOL) : c'est la macro-définition syntaxique, où les paramètres effectifs doivent répondre à certaines contraintes d'ordre syntaxique qu'on impose à la définition.

1. Les données

2. Les traitements

1. Les données

On a présenté précédemment trois types abstraits de données identifiés :

- PHYL : phylum
- OPER : opérateur
- DRAP : drapeau phylum × opérateur

1.1. la référence uniforme

On a vu que pour un phylum :
- les quatre premiers champs sont aussi des propriétés du type,
- le cinquième champ est d'accès plus complexe.

Dans une implantation efficace du type, on voudra accéder directement aux premiers champs et utiliser des fonctions pour les propriétés du dernier. Il se pose alors la question de référence uniforme : on aimerait nommer de la même façon des concepts qui ont une sémantique semblable mais une implantation différente.

Par exemple :
(1) PHYLNOMOPER
(2) NOMOPER(PHYL)
devraient s'interpréter identiquement. On utiliserait alors systématiquement la syntaxe (2), parce qu'elle permet de ne pas distinguer – sur le plan des choix d'implantation – le champ NOMOPER du champ PARENT :
NOMOPER(PHYL) deviendrait PHYLNOMOPER
PARENT(PHYL) resterait un appel fonctionnel

Dans un langage comme Alphard, la chose est vraie.

Dans un langage comme Ada, ceci est faux ; mais les outils développés autour du langage le rendent vrai – un optimiseur global, placé en aval du compilateur, reconnaît dans NOMOPER une "fausse fonction" qu'il génère par du code en ligne.

Dans un langage comme Lisp, ceci sera vrai à l'initiative du programmeur : il peut définir la fonction NOMOPER par une macro-définition qui remplace alors les appels de la fonction par du code en ligne (Note : on pourrait se placer dans cette situation en Ada avec l'utilisation d'un pragma – le pragma INLINE.

Dans d'autres langages, on utilise une définition « textuelle » : la propriété de référence uniforme, mal supportée par ces langages, est introduite en amont du compilateur (c'est-à-dire qu'on fournit au compilateur un texte macro-généré).

1.2. les similitudes de traitements

On s'intéresse ici aux propriétés qui définissent de "vraies fonctions".

La définition d'un Type Abstrait de données n'est jamais très éloignée de celle de Type Abstrait Algébrique (TAA) : on aimerait alors exprimer dans le texte les propriétés de consistance et de complétude qu'on peut vérifier dans le formalisme des TAA.

On regarde le type des drapeaux, pour lequel aucune propriété n'est une "fausse fonction". Le type est déclaré en Ada :

type TYPE_DRAP is record
                     CHP_PRESENT   : BOOLEAN;
                     CHP_VISIB     : INTEGER;
                     CHP_CHGTVISIB : BOOLEAN;
                  end record;

Les sous-programmes qui opèrent sur le champ CHP_PRESENT sont :

function PRESENT (DRAP:in TYPE_DRAP) return BOOLEAN is
begin
   return DRAP.CHP_PRESENT;
end;

procedure SETPRESENT (DRAP:in out TYPE_DRAP) is
begin
   return DRAP.CHP_PRESENT := TRUE;
end;

procedure RESETPRESENT (DRAP:in out TYPE_DRAP) is
begin
   return DRAP.CHP_PRESENT := FALSE;
end;

Les grandes similitudes entre ces trois sous-programmes sont la traduction dans le langage de l'effort du programmeur visant à obtenir la consistance et la complétude dans la définition du type. Une fois encore on ne va pas présenter un moyen de s'assurer ces propriétés mais seulement de les exprimer et de les conserver dans le programme.

Au vu des trois sous-programmes, on "devine" les étapes de construction.

Par exemple, ma procédure SETPRESENT se construit :

-- schéma général des sous-programmes

schéma général des sous-programmes

-- particularisation des procédures

particularisation des procédures

-- particularisation de la procédure SETPRESENT

particularisation de la procédure SETPRESENT

On donne en annexe l'arbre de construction des sous-programmes relatifs au champ CHP_PRESENT ainsi que sa représentation « textuelle ».

On pourrait définir des « textes » à un niveau de détail plus fin :

On reconnaît dans les autres propriétés du type TYPE_DRAP les mêmes similitudes, relatives cette fois aux champs CHP_VISIB et CHP_CHGTVISIB.

1.3. la généricité

On introduit la structure générique des listes :

le paramètre de généricité est :

nom : fonction d'accès au nom d'un élément de la liste

les propriétés sont :

recherche : recherche d'un élément dans la liste d'après son nom
ins : insertion d'un élément dans la liste
sup : suppression d'un élément de la liste

L'implantation intuitive est :

La représentation qui s'impose de manière évidente est celle d'une liste chaînée.

Comment alors traduire cette structure sous la forme s'un paquetage générique ?

Première réponse

La première approche consiste à considérer la liste comme un type de structure ("record") dont les champs sont les propriétés de la liste :

informellement :

type LISTE is record
                 RECHERCHE : function ...;
                 INS       : procedure ...;
                 SUP       : procedure ...;
              end record;

en Ada :

package LISTE is
   function RECHERCHE ...;
   procedure INS ...;
   procedure SUP ...;
end LISTE;

On doit alors paramétrer le paquetage par :

les "vrais" paramètres :

TYPE_ELT : le type des éléments de la liste
NOM : la fonction de nommage des éléments

les "faux" paramètres :

TYPE_REFELT : le type qui construit une liste chaînée d'éléments
VAL, NXT, NULNXT, SETNXT, RESETNXT :
  les sous-programmes de manipulation des listes chaînées : accès à l'élément, accès au suivant, test (vrai si l'élément suivant est nul – null), création d'un nouvel élément suivant, suppression de l'élément suivant.

Le grand nombre de paramètres de généricité soulève deux problèmes :

Deuxième réponse

Une deuxième possibilité est de réaliser un type générique dans le paquetage générique de la liste. C'est le type construit dans le paquetage qui sera utilisé pour les instances.

en Ada :

generic
   type TYPE_ELT;
   with function NOM (X:in TYPE_ELT) return STRING;
package LISTE is
   type TYPE_LISTE is ...;
   function RECHERCHE ...;
   procedure INS ...;
   procedure SUP ...;
end LISTE;

Les paramètres du paquetage se limitent alors uniquement aux "vrais" paramètres de généricité : TYPE_ELT et la fonction NOM. On retrouve dans cette approche le traditionnel exemple de la pile paramétrée par le type des éléments.

Dans le cas présent ce choix ne convient pas. En effet, les types de données manipulés sont :

type TYPE_PHYL;
type TYPE_OPER;
type TYPE_DRAP;

type TYPE_REFPHYL is access TYPE_PHYL;
type TYPE_REFOPER is access TYPE_OPER;
type TYPE_REFDRAP is access TYPE_DRAP;

type TYPE_PHYL is record
                     CHP_NOMPHYL  : TYPE_NOM;
                     CHP_ATTPHYL  : TYPE_ATTPHYL;
                     CHP_OPERPHYL : TYPE_REFDRAP;
                     CHP_PEREPHYL : TYPE_REFPHYL;
                     CHP_PARENT   : INTEGER;
                  end record;

type TYPE_OPER is record
                     CHP_NOMOPER  : TYPE_NOM;
                     CHP_ATTOPER  : TYPE_ATTOPER;
                     CHP_PHYLOPER : TYPE_REFDRAP;
                  end record;

type TYPE_DRAP is record
                     CHP_PRESENT   : BOOLEAN;
                     CHP_VISIB     : INTEGER;
                     CHP_CHGTVISIB : BOOLEAN;
                  end record;

Le type TYPE_LISTE des éléments chaînés se retrouve ici :

- pour le type TYPE_PHYL : dans le type TYPE_REFPHYL,
- pour le type TYPE_OPER : dans le type TYPE_REFOPER,
- pour le type TYPE_DRAP : dans le type TYPE_REFDRAP,

Il se trouve (par hasard) que la définition récursive du type TYPE_PHYL, par le champ CHP_PEREPHYL qui est une référence sur le type, interdit de définir le type TYPE_PHYL puis d'instancier le type des listes dans le paquetage LISTE.

Plus généralement on peut trouver l'approche peu naturelle : en effet elle demande de définir le cas général d'abord, puis de réaliser les cas particuliers à partir de ce cas général – on définit le type générique, puis on l'instancie. La démarche naturelle de paramétrisation est plutôt inverse : on réalise plusieurs objets, on les reconnaît bâtis sur un même modèle, on définit alors le modèle dont chaque réalisation devient une instance. C'est du reste la démarche qu'on a adoptée ici, et qui a conduit à se placer dans une situation qu'on ne peut pas inverser.