[Next] [Previous] [Top]

Metteur en page de sources Ada


6. Troisième application : structure


6.1. Description

Cette application permet d'effectuer la mise en page de sources Ada (pour le lecteur humain; elle ne change rien pour le compilateur). Pour ceci, une simple analyse lexicale se montrant insuffisante, on a bien besoin d'une analyse syntaxique des sources.

L'application importe des services de :

- Command_Line (pour l'analyse des arguments de l'application),
- Syntax_Analyser_G (pour l'analyse syntaxique, cf. GRAMACT),
- Text_IO (pour la manipulation des fichiers d'E/S).

Sans noms de fichiers en paramètres, comme dans les deux applications précédantes, elle travaille sur les canaux standard d'E/S.

Les erreurs de syntaxe dans le code provoquent l'arrêt de la mise en page. Dans le cas où une erreur est détectée, l'application va le signaler, en indiquant sur forme de commentaire Ada les numéros de ligne et de colonne où l'erreur se trouve, pour ensuite copier le reste du fichier d'entrée tel quel dans le fichier de sortie. Du point de vue d'un compilateur, le résultat n'est pas modifié.

En outre, dans le cas où on dépasse le nombre maximum de caractères autorisés par ligne, une nouvelle ligne contenant la partie du code qui excède cette limite va être générée.


6.2. Options

Forme générale de la commande :

structure [-i<indentation>] [-c<caractères_par_ligne>]

[in_file [out_file]]

(1) Nombre de caractères d'indentation :

-i<Positive range 1 .. 10>

Option par défaut : -i2

(2) Nombre maximum de caractères par ligne :

-c<Positive range 64 .. 80>

Option par défaut : -c64

(3) Fichier d'entrée :

Option par défaut : standard input

(4) Fichier de sortie (seulement si le fichier d'entrée a été spécifié) :

Option par défaut : standard output


6.3. Emploi de GRAMACT

Pour l'analyse syntaxique des sources, on va se servir du composant GRAMACT [STROHMEIER92].

GRAMACT est un projet de recherche du LGL. Il permet de réaliser des programmes d'application traitant des sources en insérant des actions dans une grammaire préexistante. Ces actions sont activées au moment des dérivations des règles de la grammaire.

GRAMOL est le formalisme utilisé dans GRAMACT pour décrire la grammaire d'un langage. Dans le cas qui nous concerne, on va utiliser la description en GRAMOL du langage Ada [LRM87].

Le deuxième élément qui nous intéresse dans GRAMACT est le programme d'extraction d'actions Extract. Ce programme permet d'extraire les actions qui ont été placées sur la grammaire et de produire le fichier de paramétrisation de l'analyseur syntaxique.

Enfin, le dernier élément intervenant est l'application "structure" elle-même, qui réalise la mise en page des sources Ada. Elle utilise l'analyseur syntaxique Syntax_Analyser_G fournit par GRAMACT en forme de paquetage générique. Ce paquetage implémente l'interprète qui effectue l'analyse syntaxique, et qui, durant celle-ci, signale les actions rencontrées pour permettre à l'application de les exécuter.

D'après son architecture (figure 3), on peut remarquer que l'application est facilement adaptable à plusieurs styles de mise en page. En effet, il suffit d'avoir pour chaque style un fichier avec la grammaire "décorée" du langage, qui servira à initialiser l'analyseur syntaxique. A noter que, dans tous les cas, l'application reste inchangée.

En plus d'analyser syntaxiquement le code, on tient aussi compte des commentaires, qui n'appartiennent pas à la syntaxe du langage, mais qui servent à "décorer" le code source.

On peut donc dire que cette application ne se limite pas à une analyse syntaxique pure, mais elle va plus loin. Et c'est au niveau de traitement des commentaires que l'analyseur syntaxique de GRAMACT montre quelques faiblesses, qu'on peut considérer normales si on tient compte qu'il s'agit d'éléments n'appartenant pas à la syntaxe.



Figure 3. Architecture de l'application "structure".


6.4. Critères de mise en page

Les critères pour effectuer la mise en page des sources Ada ont été établis à l'aide du document "Ada Quality and Style" [SPC92] :

(1) Laisser un espace de séparation au moins :

- avant et après les délimiteurs et opérateurs binaires suivants :
+ - * / &
< = > /= <= >=
:= => | ..
:
<>
- en dehors des quotes des littéraux chaînes (") et caractères (`),
- en dehors, mais pas à l'intérieur, des parenthèses,
- après les virgules (,) et les points-virgules (;).

(2) Ne pas laisser des espaces de séparation :

- après les signes plus (+) et moins (-) utilisés comme opérateurs unaires,
- après l'appel à une fonction,

- à l'intérieur des délimiteurs des étiquettes (<< >>),
- avant et après l'apostrophe (`) et le point (.),
- entre plusieurs parenthèses d'ouverture ou de fermeture consécutives,
- avant les virgules (,) et les points-virgules (;).

(3) Exemples d'indentation des structures du langage :

- étiquettes :
   begin
   <<étiquette>>
     <instruction>
   end;

- instructions "if" et boucles "loop" :
   if <condition> then            |   <nom>:
     <instructions>               |     loop
   elsif <condition> then         |       <instructions>
     <instructions>               |       exit when <condition>;
   else                           |       <instructions>
     <instructions>               |     end loop <nom>;
   end if;

- schémas d'itération "for" et "while" :
   <nom>:                         |   <nom>:
     for <schéma> loop            |     while <condition> loop
       <instructions>             |       <instructions>
     end loop <nom>;              |     end loop <nom>;

- instructions "block" et "case" :

   <nom>:                         |   case <expression> is
     declare                      |     when <choix> =>
       <déclarations>             |       <instructions>
     begin                        |     when <choix> =>
       <instructions>             |       <instructions>
     exception                    |     when others =>
       when <choix> =>            |       <instructions>
         <instructions>           |   end case;
       when others =>
         <instructions>
     end <nom>;

- sous-unités :

   separate (<unité père>)
   <corps>

- corps des unités de programme :

   procedure <spécification> is   |   package body <nom> is
     <déclarations>               |     <déclarations>
   begin                          |   begin
     <instructions>               |     <instructions>
   exception                      |   exception
     when <choix> =>              |     when <choix> =>
       <instructions>             |       <instructions>
   end <nom>;                     |   end <nom>;

   function <spécification>       |   task body <nom> is
     return <nom de type> is      |     <déclarations>
     <déclarations>               |   begin
   begin                          |     <instructions>
     <instructions>               |   exception
   exception                      |     when <choix> =>
     when <choix> =>              |       <instructions>
       <instructions>             |   end <nom>;
   end <nom>;

- clauses de contexte dans des unités de compilation et spécifications des unités de programme :
   with                           |   function <spécification>
     <nom>;                       |     return <type>;

   use                            |   package <nom> is
     <nom>;                       |     <déclarations>
                                  |   private
                                  |     <déclarations>;
   <unité de compilation>         |   end <nom>;

   generic                        |   task type <nom> is
     <paramètres formels>         |     <entry déclarations>
   <unité de compilation>         |   end <nom>;
- instanciations des unités génériques et indentation des déclarations de types articles :
   procedure <nom> is             |   type <nom>
     new <nom générique>          |     <discriminants> is
       <paramètres actuels>       |     record
                                  |       <composants>
   function <nom> is              |       case <discriminant> is
     new <nom générique>          |         when <choix> =>
       <paramètres actuels>       |           <composants>
                                  |         when <choix> =>
   package <nom> is               |           <composants>
     new <nom générique>          |       end case;
       <paramètres actuels>       |     end record;

- alignement des paramètres :

   function <nom>                 |   procedure <nom>
     (Paramètre_1;                |     (Paramètre_1;
      Paramètre_2;                |      Paramètre_2;
      ...                         |      ...
      Paramètre_n)                |      Paramètre_n);
     return <type>;


6.5. Définition des actions

Il s'agit ici de deux aspect primordiaux :

- d'une part, de définir les actions nécessaires à la réalisation de la mise en page envisagée dans l'application,

- d'autre part, de déterminer les endroits où ces actions doivent être placées dans les règles de la grammaire.

Les actions définies ont comme caractéristique importante leur simplicité ou atomicité : elles ne font pas plusieurs opérations en même temps, mais une opération précise et simple.

Pour produire plusieurs effets, on va donc combiner un certain nombre d'actions dans l'ordre correct.

L'emplacement des différents éléments lexicaux dans le fichier de sortie va être fait à l'aide de la variable globale Colonne et des primitives de Text_IO.

Les actions qui ont été définies sont les suivantes :

LINE

Text_IO.New_Line;

Passe le fichier de sortie à la ligne suivante.

SET

Text_IO.Set_Col (Colonne);

Positionne le fichier de sortie dans la colonne indiquée par Colonne.

SPACE

Text_IO.Put (Blank);

Ecrit un espace de séparation.

AVANCE

Colonne := Colonne + 1;

Incrémente d'une unité la valeur de Colonne.

RECULE

Colonne := Colonne - 1;

Décremente d'une unité la valeur de Colonne.

INCR

Colonne := Colonne + Positive_Count (Indentation);

Incrémente Colonne du nombre de caractères d'indentation donné en paramètre.

DECR

Colonne := Colonne - Positive_Count (Indentation);

Décremente Colonne du nombre de caractères d'indentation donné en paramètre.

AVIS_DECR

Decrementer := Decrementer + 1;

Augmente d'une unité le nombre de fois qu'on va devoir décrementer Colonne pour revenir à la position adéquate. Il s'agit d'une action qui sert par exemple à mettre à jour une variable d'état indiquant qu'on est dans le contexte d'une instruction "block" dont un nom a été donné, et par conséquence toute l'instruction doit être indentée par rapport au nom.

L'emploi d'une variable numérique au lieu d'une variable booléenne répond au fait qu'il est possible de trouver plusieurs cas "imbriqués". En effet, dans le cas où une variable booléenne serait employée, on ne tiendrait compte que d'un seul avis, en perdant le reste.

DECR_EV

if Decrementer > 0 then

Colonne := Colonne - Positive_Count (Indentation);

Decrementer := Decrementer - 1;

end if;

Décremente Colonne (du nombre de caractères d'indentation donné en paramètre) dans le cas où on se trouve dans le contexte d'une instruction "block" dont un nom a été donné. De même, la variable qui indique ce fait est mise à jour.

A l'aide des actions qu'on vient de décrire, la démarche à suivre pour effectuer la mise en page serait :

(1) "Décorer" les règles de la grammaire avec les actions;
(2) Compiler le fichier avec la grammaire décorée, afin d'obtenir le fichier de paramétrisation de l'analyseur syntaxique;
(3) Exécuter l'application de mise en page.

Ci-dessous on montre quelques exemples de règles "décorées" :

a) Règle définissant la clause de contexte "with" :

with_clause = "WITH" {+ simple_name $ "," +} ";" .

Règle décorée avec des actions :

with_clause = "WITH" <+LINE, INCR+>
              {+ <+SET+> simple_name $ "," <+LINE+> +} ";"
              <+LINE, DECR+> .

Exemple :

with Calendar, Command_Line, Text_IO;

Résultat :

with
  Calendar,
  Command_Line,
  Text_IO;

b) Règle définissant l'instruction "if" :

if_statement = "IF" condition "THEN" sequence_of_statements
               { "ELSIF" condition "THEN" sequence_of_statements }
               [ "ELSE" sequence_of_statements ] "END" "IF" ";" .

Règle décorée avec des actions :

if_statement = "IF" <+SPACE+> condition <+SPACE+> "THEN"
               sequence_of_statements <+LINE, SET+>
               { "ELSIF" <+SPACE+> condition <+SPACE+> "THEN"
                 sequence_of_statements <+LINE, SET+> }
               [ "ELSE" sequence_of_statements <+LINE, SET+> ]
               "END" <+SPACE+> "IF" ";" .

Exemple :

if    condition_1 then action_1; action_2;
elsif condition_2 then action_3;
else                   action_4; action_5;
end if;

Résultat :

if condition_1 then 
  action_1;
  action_2;
elsif condition_2 then
  action_3;
else
  action_4;
  action_5;
end if;

c) Règle définissant l'instanciation des paquetages génériques :

generic_instanciation = "PACKAGE" simple_name "IS" "NEW" package_name
                        [ generic_actual_part ] ";" .

Règle décorée avec des actions :

generic_instanciation = "PACKAGE" <+SPACE+> simple_name <+SPACE+> "IS"
                        <+INCR, LINE, SET+> "NEW" <+SPACE+> package_name
                        [ generic_actual_part ] ";" <+DECR+> .

Exemple :

package Ada_To_PostScript is new Ada_To_Foo_G
( Put_Page, Put_Empty, Put_Newline, Put_Line_Number, Put_Blank,
  Put_Reserved, Put_Identifier, Put_Numeric, Put_String, Put_Comment,
  Put_Character, Put_Delimiter, Put_Substitute );

Résultat :

package Ada_To_PostScript is
  new Ada_To_Foo_G
    (Put_Page,
     Put_Empty,
     Put_Newline,
     Put_Line_Number,
     Put_Blank,
     Put_Reserved,
     Put_Identifier,
     Put_Numeric,
     Put_String,
     Put_Comment,
     Put_Character,
     Put_Delimiter,
     Put_Substitute);


6.6. Exemples

Etant donné le fichier comparer.a :

---------------------------------------------------------
function Comparer ( Element_1, Element_2 : Type_Element )
  return Integer is
begin
  if    Element_1 > Element_2 then return  1;
  elsif Element_1 = Element_2 then return  0;
  else                             return -1;
  end if;
end Comparer;
---------------------------------------------------------

(1) structure comparer.a :

----------------------------------
function Comparer
  (Element_1,
   Element_2 : Type_Element)
  return Integer is
begin
  if Element_1 > Element_2 then
    return 1;
  elsif Element_1 = Element_2 then
    return 0;
  else
    return -1;
  end if;
end Comparer;
----------------------------------

(2) structure comparer.a | apparence -iu :

----------------------------------
function COMPARER
  (ELEMENT_1,
   ELEMENT_2 : TYPE_ELEMENT)
  return INTEGER is
begin
  if ELEMENT_1 > ELEMENT_2 then
    return 1;
  elsif ELEMENT_1 = ELEMENT_2 then
    return 0;
  else
    return -1;
  end if;
end COMPARER;
----------------------------------

(3) structure -i4 comparer.a | ada_to_postscript :

     1   ------------------------------------
     2   function Comparer
     3       (Element_1,
     4        Element_2 : Type_Element)
     5       return Integer is
     6   begin
     7       if Element_1 > Element_2 then
     8           return 1;
     9       elsif Element_1 = Element_2 then
    10           return 0;
    11       else
    12           return -1;
    13       end if;
    14   end Comparer;
    15   ------------------------------------

Exemple contenant une erreur de syntaxe ("elseif" au lieu de "elsif") :

---------------------------------------------------------
function Comparer ( Element_1, Element_2 : Type_Element )
  return Integer is
begin
  if     Element_1 > Element_2 then return  1;
  elseif Element_1 = Element_2 then return  0;
  else                              return -1;
  end if;
end Comparer;
---------------------------------------------------------

(4) structure comparer.a :

----------------------------------
function Comparer
  (Element_1,
   Element_2 : Type_Element)
  return Integer is
begin
  if Element_1 > Element_2 then
    return 1;

  -- ***** SYNTAX ERROR at line : 5, column : 3

  elseif Element_1 = Element_2 then return  0;
  else                              return -1;
  end if;
end Comparer;
-----------------------------------------------


Metteur en page de sources Ada - 29 MAR 95
[Next] [Previous] [Top]

Generated with CERN WebMaker