AdaSubst User Guide V1.4r1


Next: , Previous: (dir), Up: (dir)

AdaSubst User Guide

Last edited: 8 February 2008

This is the AdaSubst user guide. It describes how to install and use AdaSubst.

This software is © Adalog, 2002-2008. AdaSubst is free software; you can redistribute it and/or modify it under terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License distributed with this program; see file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As a special exception, if other files instantiate generics from the units of this program, or if you link units provided with this program with other files to produce an executable, these units do not by themselves cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Public License.

If for any reason your local lawyer expresses some concern about the terms of this license, please get in touch with us. Our intent is to allow anybody to do whatever they please with this code, and we'll be happy to clarify things if necessary.

This program is offered to the community with this very liberal license as a demonstration of Adalog's know-how; please do not remove the reference to Adalog in the header comment of each unit.


Next: , Previous: Top, Up: Top

1 Introduction

AdaSubst is a tool for making semantic substitutions. This means that it does not replace text, but Ada elements (identifiers) in an Ada program by other identifiers. This substitution can therefore account for visibility rules, to make sure that only the intended element is replaced. But AdaSubst goes far beyond that: it takes all Ada rules into consideration. Therefore, if you substitute a name defined in a generic, the same substitution will be applied for all instantiations (unless there is an explicit substitution for the instantiated element, of course). Similarly, if you substitute the name of a primitive operation of a type, the same substitution will be applied to the corresponding operation of all derived types (see Known bugs for a limitation on this however).

AdaSubst can also perform other kinds of semantic substitution; for the moment, it can also remove all representation clauses from a program. Other forms of substitutions may be added in the future; we are looking forward to user suggestions about what could be useful.

AdaSubst can be used for a variety of usages. See AdaSubst in practice to give you an idea about what it can do for you. It has already been used in an industrial context, and has processed succesfully many thousands of lines. It is another example of the nice things that can be made using ASIS.

Please send bug reports, remarks, notes, good ideas, improvements to the documentation, etc. to J-P. Rosen.


Next: , Previous: Introduction, Up: Top

2 Installing AdaSubst

This version uses ASIS-for-GNAT. As a consequence, you must have GNAT installed before you install AdaSubst. If you are using another compiler that provides an ASIS interface, you may recompile AdaSubst with this compiler. GNAT will not be necessary any more (but your compiler will, of course). See AdaSubst and ASIS.

If you downloaded an executable distribution of AdaSubst, please note that it has been compiled with GNAT GPL2007, and will not work with any other version of GNAT. If you have an older version of GNAT, either upgrade your GNAT to GPL2007 (recommended!), or recompile AdaSubst from the source distribution.

If you downloaded the source version of AdaSubst, you must make sure that ASIS is installed on your system, then simply issue the following command:

     gnat make -P build.gpr

If this fails (presumably because ASIS is not installed in a standard location), you can also gnatmake the file adasubst.adb, including appropriate options to access the ASIS library.

AdaSubst needs no setup, no special rights. Simply copy the executable where you please.

This version has been tested under Windows/XP and Linux.


Next: , Previous: Installing AdaSubst, Up: Top

3 Principle of operation

AdaSubst reads one or more Ada units, and produces output files obtained by performing a number of substitutions to the text of the corresponding source files. A dictionary file defines the substitutions to be performed. Note that source comments, character literals, character strings, and attribute designators are never substituted.

The dictionary file is a regular text file defining substitutions, one per line. Each line has one of the following formats:(elements between quotes must be typed as is):

     <Original> "=>" <substitution>
     "not" <Original>

<Original> defines what kind of element is to be substituted (the original). Since the original is an Ada element, case is irrelevant, and you can even put spaces where they are allowed by Ada syntax.. Only one substitution is allowed for a given <Original>.

In the first form, <Substitution> describes how you want the Original to be modified. Note that the replacement is always taken "as is", i.e. the replacement uses the same casing as given in the substitution string.

The second form is an indication that the corresponding <Original> is not to be substituted. This is useful to make exceptions to a more general substitution rule. See Substitution with exceptions for examples of this.

In addition, empty lines and lines starting with “#” or “–” (comment lines) are ignored.


Next: , Previous: Principle of operation, Up: Principle of operation

3.1 Specifying the original name

The syntax of the <Original> is as follows:

     <Original> ::= <Full_Name> | "all" <Simple_Name>

<Full_Name> is the full name of the element to be substituted, using normal Ada dot notation (with some extension, see Overloaded names). Full name means that you give the full expanded name, starting from a compilation unit. This name must be the actual full name, i.e. it must not include any renaming. For example, the usual Put_Line must be given as Ada.Text_IO.Put_Line, not as Text_IO.Put_Line. Predefined elements (Integer, Constraint_Error) must be given in the form Standard.Integer or Standard.Constraint_Error, since they are logically declared in the package Standard.

<Simple_Name> is a single identifier, possibly followed by overloading information, see Overloaded names. No qualification is allowed.

The first form designates a single element from an Ada program, and is therefore called "element substitution". Only this element will be substituted, i.e. if you want to replace the variable "V" declared in package "P", the original should be "P.V" , and no other "V" in the program will be substituted. The second form designates all identifiers with the given name in the program, irrespectively of where they appears, i.e. if you use the form "all V", all elements named "V" will be substituted. This form is called "identifier substitution".


Next: , Previous: Specifying the original name, Up: Specifying the original name

3.1.1 Overloaded names

In Ada, names can be overloaded. This means that you can have several procedures P in package Pack, if they differ by the types of the parameters. If you just give the name Pack.P as the <Ada_Entity_Name>, the corresponding rule will be applied to all elements named P from package Pack. If you want to distinguish between overloaded names, you can specify a profile after the element's name. A profile has the syntax:

     "{" [ ["access"] <type-name>
          { ";" ["access"] <type-name> } ]
          ["return" <type-name>] "}"

You must specify the type name, even if the <Ada_Entity_Name> declaration uses a subtype of the type; this is because Ada uses types for overloading resolution, not subtypes. Anonymous access parameters are specified by putting access in front of the type name. An overloaded name for a procedure without parameters uses just a pair of empty brackets. If the subprogram is a function, you must provide the return <type-name> part for the return type of the function. The types must also be given as a unique name, i.e. including the full path: if the type is T declared in package Pack, you must specify it as Pack.T. As a convenience, the Standard. is optional for predefined types, so you can write Standard.Integer as Integer. There is no ambiguity, since a type is always declared within some construct. Note that omitting Standard works only for types that are part of the profile used to distinguish between overloaded Ada entities but that the Ada entity name must always contain Standard if it is a predefined element.

Overloaded names can be also be used with the all <Simple_Name> form of the <Ada_Entity_Name>. In this case, the rule will be applied to all names that are subprograms with the given identifier and matching the given profile, irrespectively of where they appear.

Note that if you use an overloaded name, all overloadable names that are part of the <Ada_Entity_Name>, including those of the profile, must use the overloaded syntax. For example, given the following program

     procedure P is
        procedure Q (I : Integer) is
           ...
        end Q;
        procedure Q (F : Float) is
           ...
        end Q;
     begin
        ...
     end P;

If you want to distinguish between the two procedures Q, you must specify them as P{}.Q{Integer} and P{}.Q{Float} (note the P{} which specifies an overloaded name for a procedure P without parameters).

The names of entities which can not be overloaded (like package, exception, ...) must not be suffixed by braces (e.g. Ada.Text_IO.Put_Line{Standard.String}).


Next: , Previous: Overloaded names, Up: Specifying the original name

3.1.2 Enumeration literals

Following normal Ada rules, an enumeration literal is considered a parameterless function. If you want to distinguish between overloaded enumeration literals, you can use overloaded names for them. For example, given:

     package Pack is
        type T1 is (A, B);
        type T2 is (B, C);
     end Pack;

Ada entities names are:


Next: , Previous: Enumeration literals, Up: Specifying the original name

3.1.3 Anonymous constructs

There is a special case for elements that are defined (directly or indirectly) within unnamed loops or block statements. Everything happens as if the unnamed construct was named _anonymous_. So if you have the following program:

     procedure P is
     begin
        for I in 1..10 loop
           declare
              J : Integer;
           begin
              ...
           end;
        end loop;
     end P;

You can refer to I as P._anonymous_.I, and to J as P._anonymous_._anonymous_.J.


Next: , Previous: Anonymous constructs, Up: Specifying the original name

3.1.4 Record and protected types components

You can designate the name of a record or protected type component (a “field” name), but to identify it uniquely, you must precede its name by the name of the type. This is a small extension to Ada syntax, but it is the simplest and most natural way to deal with this case. For example, given:

     procedure P is
        type T is
           record
              Name : Integer;
           end record;
        ...

The Ada entity name is P.T.Name.


Previous: Record and protected types components, Up: Specifying the original name

3.1.5 Operators

AdaSubst handles operators (i.e. functions like "+") correctly. Of course, you must specify such operations using normal Ada syntax: if you define the integer type T in package Pack, an overloaded name for the addition would be Pack."+"{Pack.T; Pack.T return Pack.T}.

There is no problem in changing a regular function name into an operator, but the substitution will continue to use the prefixed call notation. If you do the opposite, i.e. changing an operator into a regular function, AdaSubst will automatically transform any infixed call into a prefixed call. In short, if your dictionary says:

     Plus => "+"
     "-"  => Minus

the following sequence:

     X := Plus (X, Y);
     X := X - Y;
     X := "-" (X, Y);

will become:

     X := "+" (X, Y);
     X := Minus (X, Y);
     X := Minus (X, Y);

Note finally that "and then", "or else", "in" and "not in" are not operators (although they are operations) and cannot therefore be substituted. Similarly, operators between universal types have specifications that cannot be expressed in Ada, and therefore cannot be substituted.


Next: , Previous: Specifying the original name, Up: Principle of operation

3.2 Specifying the substitution

The substitution can take one of the following forms:

      ["abs"] <Name>
      [<Name>] "in" <Package_Name>
      <Name> { [","] <Name> }

<Name> is the new name(s) of the entity, <Package_Name> is the place where it is now (if it has been moved). The idea is that the substitution describes how the element has been changed. If you simply changed the name of an entity, just give the new name. If you moved an entity into another package, just give "in" followed by the new package name. Of course, if you moved an entity and renamed it, you can give both.

If the substitution starts with an "abs", the element is replaced exactly by the <Name> (we call this an absolute substitution). Note that the element is replaced, irrespectively of how it was originally named (i.e. using a short name or a qualified name). This allows you to change short names into long names, i.e. if the element is "Pack.V" and the substitution is "abs Pack.V", then every occurrence of the former (even in short notation) will be changed into the full notation.

As a special case, you may provide several substitions for an element in the dictionary, separated by spaces or a comma (the third form). Such substitutions are always absolute. This is intended for the case when one element has been replaced by several ones, typically when a package has been split into several packages. For the purpose of the explanation, assume that package Pack has been split into Pack1 and Pack2. The dictionary will contain a line like "Pack => Pack1, Pack2". Normally, you should specify explicitely in the dictionary where each element of Pack has been moved (see Using AdaSubst in preparation mode for a mode that helps you do that). When AdaSubst encounters a reference to Pack for which there is no explicit substitution, two things can happen:

  1. If Pack appears by itself in a with or use clause, it is replaced by the list of substitutions (i.e., "with Pack" becomes "with Pack1, Pack2"). This may create unnecessary references, but causes no harm.
  2. If Pack appears as the prefix of some elements, it is replaced by the list of substitutions, with "?" as the separator (i.e. "Pack.V" becomes "Pack1? Pack2.V"), and a warning message is issued. Since there is no case in Ada where a "?" is legal, you will be noticed at the first compilation of the problem, and it will be easy for you to fix it manually, or to adjust the dictionary as appropriate.

For example, given a dictionary file containing the following lines:

     Pack      => Parent, Parent.Child
     Pack.X    => New_X in Parent.Child
     Pack.V    => abs Variable
     Pack.Z    => Child.Z in Parent

Here is how the substitutions will happen:

Original Substituted
     with Pack;
     procedure Example is
        A : Integer := Pack.X
        B : Integer := Pack.Y;
        use Pack;
     begin
        A := X + Y + V;
        B := Pack.V;
        B := Pack.Z + Z
     end Example;

     with Parent, Parent.Child;
     procedure Example is
        A : Integer := Parent.Child.New_X
        B : Integer := Parent? Parent.Child.Y;
        use Parent, Parent.Child;
     begin
        A := New_X + Y + Variable;
        B := Variable;
        B := Parent.Child.Z + Child.Z;
     end Example;


Notes:


Next: , Previous: Specifying the substitution, Up: Principle of operation

3.3 Preference rules

It may happen that several substitutions are possible for a given element. In this case, the rule is to use the most specific interpretation. This means that substitutions are considered in the following order:

The exact rule for substitution is that if an element has a substitution, then that substitution is applied; otherwise, if the element's name was given using a qualified notation, a substitution is attempted on the prefix (i.e. everything before the last dot), using this same rule.

For example, you may specify to replace "Pack" with "My_Package", and "Pack.X" with "Other_Package.X". In this case, the most specific substitution is applied (the later), but if you have a "Pack.Y" (no specific substitution specified), it will be replaced by "My_Package.Y".


Previous: Preference rules, Up: Principle of operation

3.4 Rigor and efficiency

In the design of AdaSubst, we chose the most rigorous approach, hence the choice of ASIS. This means that we do not believe that AdaSubst would generate a code that compiles and is wrong (provided the dictionary is correct, of course). If in doubt, it will produce code that does not compile, so you can fix it manually.

However, using ASIS has its drawback: the code must be compilable, and running AdaSubst on it can take as long as it would need to fully compile it. Since AdaSubst is used normally only once on any source file, it seems a sensible compromise.


Next: , Previous: Principle of operation, Up: Top

4 Using AdaSubst in normal mode

In normal mode, AdaSubst is started by a command line of the form:

     AdaSubst [-T] [-bcdhrsw]
              <dictionary-file> | <substitution>
              [-oO <output-prefix>]
              [-p <project-file>]
              [-l <line-length>]
              <unit-name>{+|-<unit-name>} | @<list-file>
              [-- <ASIS-options>]

Options:

As usual, options can be grouped or provided separately (i.e. "-b -s" is the same as "-bs"). Options can appear at any place on the command line.

-b
process body of unit
-c
leave substituted lines as comments in output file
-d
debug
-h
help. Print usage message and exit
-l
limits output line length to the given value. See below.
-o
specify a prefix for output files. See below.
-O
same as -o, but keep output files that have no changes.
-p
use source directory indications from provided project file. See below.
-r
recursive. Process the unit and (recursively) all user units it depends on (including parent units if the unit is a child or a subunit). Predefined Ada units and units belonging to the compiler's run-time library are never processed.
-s
process specification of unit
-w
overwrite output file if already existing

The -T option means "translate", but it's the default mode for AdaSubst and is therefore optional.

If the -h option is given, AdaSubst just prints a brief usage summary and exits. If neither -s or -b is given, -sb is assumed (i.e. both the specification and body are processed). The -d option adds extra messages for debugging purposes; there is no reason to use it, unles you encounter a bug in AdaSubst.

<dictionary-file> is the name of the file to be used as the dictionary. If it is given as the single character "-", the dictionary is read from the standard input. Alternatively, you can give one substitution as a quoted string instead of a dictionary file; this avoids having to write a dictionary file for simple substitutions. For example:

     AdaSubst "pack.x => A_Better_Name" My_Package

<unit-name> is the name of the Ada unit to translate; note that it is a unit name, not a file name: case is not significant, and there should be no extension! Of course, child units and subunits are allowed following normal Ada naming rules: "Parent.Child". Note that when a unit is processed, all its subunits are processed at the same time. Alternatively, you can give an "@" followed by the name of a file instead of a unit name. This file should contain a list of unit names, one on each line. All units whose names are given in the file will be processed. If a name in the file starts with "@", it will also be treated as an indirect file (i.e. the same process will be invoked recursively).

If no "-o <output-prefix>" option is given, the result of the substitutions is simply written to the standard output. This is useful to debug your dictionary, or if you want to have all the output in a single file. With Gnat, this file could be gnatchopped to rebuild the correct source file names. Otherwise, AdaSubst will output the result of processing each unit to a file whose name is obtained by prefixing the <output-prefix> to the original name of the corresponding source file. The <output-prefix> can be any string, and is not analyzed by AdaSubst. A prefix like "result/" will result in all the output going to the directory "result", with the same name as the original. Alternatively, a prefix like "new-" will result in all output files being in the same directory, with a "new-" prepended to the name. AdaSubst will refuse to overwrite an existing file on output, unless the "-w" option is given. If no substitution were performed on a unit, the output file is not kept, unless you specified the <output-prefix> with the "-O" (capital O) option.

It may happen that a modified line is longer than the original, and therefore exceeds the maximum line length accepted by the compiler. The "-l" option sets an upper bound to the length of the output lines. If a line becomes longer than that, it is folded at an appropriate point. By convention, if the given <line-length> is zero (or if the "-l" option appears without a value), it means that lines should never be folded. If no "-l" option is given, the maximum line length is 200 (the minimum required by the Ada standard). A line length less than 80 is not accepted.

If the "-c" option is given, every changed line is kept in the output file, as a comment line starting with "–CHANGED: ". This makes it handy to retrieve changed lines with an editor, and to check the substitutions.

Everything that appears after "--" will be treated as an ASIS option, as described in the ASIS user manual. Normally, you don't have to care about this, except for the issue discussed below.

If the units that you are processing reference other units whose source is not in the same directory, AdaSubst needs to know how to access these units (as GNAT would). You can do this in two ways (not exclusive):

  1. You can specify a project file (the file with a ".adp" extension used by the Ada mode of Emacs) with the "-p" option. AdaSubst will automatically consider all the directories mentionned in "src_dir" lines from the project file. Currently, AdaSubst does not accept GPS project files
  2. you can include one or several "-I" options to reference other directories where sources can be found. The syntax is the same as the "-I" option for Gnat. Note that since "-I" is actually an ASIS option, it must appear after a "--".


Next: , Previous: Using AdaSubst in normal mode, Up: Top

5 Using AdaSubst in preparation mode

Under control of special options, AdaSubst helps you prepare the dictionary or list files.


Next: , Previous: Using AdaSubst in preparation mode, Up: Using AdaSubst in preparation mode

5.1 Preparing a dictionary

This mode is intended to help you prepare the dictionary file when you want to split elements exported by a package into several other packages. This happens quite often when you start writing a package, then later decide to split it into child units.

     AdaSubst -P [-bdrsw]
              [-o <output-file>]
              <unit-name>{+|-<unit-name>} | @<list-file>
              [-- <ASIS-options>]

Options are the same as in normal mode, except "-c" and "-l" which would mean nothing and are not accepted. The "-o <output-file>" option specifies the name of the file to which output is to be written (standard output if there is no "-o" option). Note that if an <output-file> is specified that already exists, new data is appended to it, unless the "-w" option is given (in which case it is overridden).

AdaSubst will analyze the visible part (not the private part) of the units given by <unit-specification> (or the names of packages given in <list-file>, possibly recursively if a "-r" option is given), and produce a dictionary consisting of the full (overloaded) name of all elements declared within the visible parts of (generic) package units (others are ignored), followed by the simple identifier of the same element. Of course, if the visible part includes package, task or protected declarations, only their visible parts will be processed. In short, given the package:

     package Pack is
        type T is range 0..100;
        E : exception;
        V : T;
        function F (X : Integer) return Float;
        task A_Task is
           entry Visible;
        private
           entry Not_Visible;
        end A_Task;
     private
        Hidden : Integer;
     end Pack;

AdaSubst will produce a file containing:

     #
     # Elements declared in package PACK
     #
     Pack.T                            => T
     Pack.E                            => E
     Pack.V                            => V
     Pack.F{Standard.Integer return Standard.Float} => F
     Pack.F{Standard.Integer return Standard.Float}.X => X
     Pack.A_Task                       => A_Task
     Pack.A_Task.Visible{}             => Visible

Assuming that you have moved elements declared in Pack to other packages, all you have to do is take your favorite text editor and add after each identifier from the second column an "in" plus the name of the package where it is now. You can then use this dictionary to update all units that depended on Pack.

Note that the generated dictionary is a "no-op", i.e. each element is substituted by itself. AdaSubst recognizes no-ops, so that lines that contain identical substitutions are not considered modifications (no "–CHANGED:" line is generated if you put the "-c" option). Therefore, if you just want to change some names of elements declared in a package, you can use the -P option to generate the dictionary, then modify only the lines for the identifiers you want to change. You can then run AdaSubst in normal mode on the package itself as well as on any unit that uses the package to perform the substitutions.

Note also that if the original element does not use the same casing as the substitution, substitution does occur. This means that the "raw" dictionary can be used to change the casing of all identifiers to the one of the original declaration in the package specification.


Previous: Preparing a dictionary, Up: Using AdaSubst in preparation mode

5.2 Preparing a list of files

This mode is intended to help you prepare the list of files you want to process by finding all the dependents of a set of units.

     AdaSubst -D [-bdrsw]
              [<-o output-file>]
              <unit-name>{+|-<unit-name>} | @<list-file>
              [-- <ASIS-options>]

Options are the same as in normal mode, except "-c" and "-l" which would mean nothing and are not accepted. The "-r" option is assumed by default; it is accepted on the command line, but has no effect. The "-o <output-file>" option specifies the name of the file to which output is to be written (standard output if no "-o" option is given). Note that if an <output-file> is specified that already exists, new data is appended to it, unless the -w option is given (in which case it is overridden).

AdaSubst will scan all units given by <unit-name> (or the names of packages given in <list-file>), and produce a list of the unit names, and (transitively) all units that are withed by these units, except predefined units. Especially, if you give it the name of your main program, this will build the list of all units that make up the program.


Next: , Previous: Using AdaSubst in preparation mode, Up: Top

6 Using AdaSubst to remove representation clauses

In addition, a special mode of AdaSubst allows you to remove (or comment out) all representation clauses from a set of units. This can be useful if you want to assess the impact of representation clauses on your program, especially efficiency. Thanks to AdaSubst, you can easily compare two versions of your program, with or without representation clauses.

     AdaSubst -R [-bdrsw]
              [<-o output-file>]
              <unit-name>{+|-<unit-name>} | @<list-file>
              [-- <ASIS-options>]

Options are the same as in normal mode.

AdaSubst will operate like in normal mode, except that instead of translating identifiers (there is no dictionary), the output files will have all representation clauses removed (or commented out if the "-c" option is given)..


Next: , Previous: Using AdaSubst to remove representation clauses, Up: Top

7 AdaSubst in practice

AdaSubst can be used for a number of purposes. The logic behind the format of the dictionary is:

Here are some typical usages.


Next: , Previous: AdaSubst in practice, Up: AdaSubst in practice

7.1 Changing identifiers

You simply want to change every occurrence of an identifier, irrespectively of where it appears. For example, you misspelled an identifer. Just make a dictionary like this :

     Wrong_Spelling => Right_Spelling

and run AdaSubst on your program.

If you don't want to create a dictionary file, you can also use the following command:

     echo Wrong_Spelling => Right_Spelling | AdaSubst - main_program -r

Note that here, we run AdaSubst on the main program with the "-r" option to make sure that every occurrence of "Wrong_Spelling" in the whole program is replaced by "Right_Spelling". You can also avoid the "echo" command, and just type the dictionary line on the keyboard, don't forget to input the end-of-file in that case.

The following case illustrates another use of absolute substitutions. Imagine you had the following package, from an Ada83 project:

     package Missing_Ada83_Functions is
        function Min (L, R : Integer) return Integer;
        function Max (L, R : Integer) return Integer;
        -- etc.
     end Missing_Ada83_Functions;

Now, you'd rather use the Ada95 'Min and 'Max attributes. Make a dictionary like this:

     Missing_Ada83_Functions.Min    =>  abs Integer'Min
     Missing_Ada83_Functions.Min.L  =>  Left
     Missing_Ada83_Functions.Min.R  =>  Right
     Missing_Ada83_Functions.Max    =>  abs Integer'Max
     Missing_Ada83_Functions.Max.L  =>  Left
     Missing_Ada83_Functions.Max.R  =>  Right

Note the use of "abs". Therefore, even if the original was written as "Missing_Ada83_Functions.Min", it will be replaced by "Integer'Min", not "Missing_Ada83_Functions.Integer'Min". Note also the translation for the formal parameter names, to ensure correct translation of calls using named notation.


Next: , Previous: Changing identifiers, Up: AdaSubst in practice

7.2 Substitution with exceptions

As another example, suppose you defined several "Put" operations for types defined in various packages, but someone decides that they should rather be called "Print". Of course, the substitution should not be applied to the "Put" defined in Ada.Text_IO. The following dictionary will do the trick:

     all Put => Print
     not Ada.Text_IO.Put

Overloaded names can be used with all forms of substitution. If you decide in addition that all "Put" on type My_Int should be called "Print_Int" and that all "Put" on type My_Float should stay the same, the dictionary would become (assuming that the types are defined in package "My_Data"):

     all Put => Print
     not Ada.Text_IO.Put
     all Put {My_Data.My_Int} => Print_Int
     not all Put {My_Data.My_Float}


Previous: Substitution with exceptions, Up: AdaSubst in practice

7.3 Splitting a package into child units

Here you have a big package (possibly from an old Ada83 project) and you want to take advantage of the hierarchical units feature of Ada95 to split it into several child units.

  1. run AdaSubst with the "-P" option on the package. This will prepare an identity dictionnary file.
  2. Rearrange the package at your convenience. Each time you move an element from the visible part of the old package, adjust the corresponding line of the dictionary to reflect the new name. Note that you can change the name of the element at this time. Imagine for example the following package:
              package Old is
                 Ident1 : Integer;
                 Ident2 : Integer;
              end Old;
         

    Running AdaSubst -P on this package will produce:

              Old.Ident1   =>  Ident1
              Old.Ident2   =>  Ident2
         

    If you want to change this package into the following:

              package Parent is
                 Good_Variable_Name : Integer;
              end Parent;
              
              package Parent.Child is
                 Another_Good_Name : Integer;
              end Parent.Child;
         

    change the dictionary to:

              Old         =>  Parent, Parent.Child
              Old.Ident1  =>  Good_Variable_Name in Parent
              Old.Ident2  =>  Another_Good_Name in Parent.Child
         

    The first line expresses that package "Old" has been replaced by "Parent" and "Parent.Child". Any "with" or "use" for "Old" will be replaced with the corresponding clause for both packages. Alternatively, you could use the following dictionary:

              Old         =>  Parent
              Old.Ident1  =>  Good_Variable_Name in Parent
              Old.Ident2  =>  Child.Another_Good_Name in Parent
         

    Here, "Old" would be systematically replaced by "Parent" and any use of "Ident2" would be replaced by "Child.Another_Good_Name", therefore producing also a correct code.

    Run AdaSubst on all units that used the old package with this dictionary. You can also run AdaSubst with the same dictionary on the body of the packages you have modified, all required modifications will be performed automatically.

NB: if you plan to split a package into child units, it is often useful to know which parts of the package are used by various modules. Adadep, another utility provided with AdaSubst, can be instrumental for that.


Next: , Previous: AdaSubst in practice, Up: Top

8 AdaSubst and ASIS

AdaSubst has been tested only with ASIS-for-gnat; however, the only (known) dependency is for the implementation-dependent parameters used to open an ASIS context. These parameters are defined as strings in the package Implementation_Options (file implementation_options.ads), so all you have to do if you want to port AdaSubst to another implementation is adjust these definitions. If you ever do so, please keep us informed by sending a note to rosen@adalog.fr.


Next: , Previous: AdaSubst and ASIS, Up: Top

9 Troubleshooting

9.1 Message: Inconsistent tree file for ..., please remove .adt file

It is a consequence of the way ASIS works that tree files (with a .adt extension) are created as part of the process. Currently, these file are not erased by AdaSubst after execution. This speeds up execution if you run AdaSubst several times, as is often the case when you are designing the dictionary file. However, if you change your source files, the trees are no more consistent with the sources. Simply remove the adt files and rerun AdaSubst; new tree files will be created.

9.2 AdaSubst prints "!! Internal error, continuing", or halts with an error

There are many subtile special cases when analysing Ada code, so it may happen that you find a context that we didn't think about. There are also some cases where the error comes from a bug in ASIS-for-Gnat. Normally, AdaSubst will print "!! internal error, continuing" and will try to proceed, without processing the failing element. It will output a " ???? " string in the output file (something that will certainly not compile), for you to check manually if a substitution has been missed. Since such a case is very rare in practice, a bug will not prevent you from using AdaSubst. Note however that if you run AdaSubst with the "-d" (debug) option, it will halt on the failing element and print the full ASIS context.

If you encounter a bug while using AdaSubst , please rerun it with the "-d" option and save the output to a file, then send this file together with the dictionary and the file you are processing to rosen@adalog.fr. We will do our best to fix it. Of course, you are welcome to try and fix it yourself; we made every attempt to make the source of AdaSubst quite readable, but be aware that you will need a good understanding of ASIS and Ada syntax to understand how it works.

9.3 AdaSubst is slow when run on many files

There is one slow operation in AdaSubst, known as "opening the context" in ASIS. This happens only once per run - but happens in every run. This means that if you use a shell script to run AdaSubst multiple times, with one file at a time, it will take you quite a while. On the other hand, if you provide a list of files, or provide several files at the same time on the command line, or just provide a main program with the "-r" option, AdaSubst will be much faster (some users have reported going from several hours to a couple of minutes!), since the context is opened only once.

9.4 AdaSubst complains that all identifiers are doubly defined

Look at your dictionary: they are actually doubly defined. Remember that if you run AdaSubst in preparation mode and the output dictionary exists, new elements are appended to it (unless you use the "-w" option). Therefore, if you run by mistake twice the same "AdaSubst -P" command, all definitions will appear twice in the dictionary.


Next: , Previous: Troubleshooting, Up: Top

10 Known bugs

10.1 Derived primitive operations

In the case of cascaded derivations with several substitutions, there is a case where AdaSubst does not behave as expected. Consider:

     package Pack is
        type T is ...;
        procedure Primitive (X : T);
     
        type D1 is new T;
     
        type D2 is new D1;
     end Pack;

with the following dictionary:

     Pack.Primitive{Pack.T}  => T_Primitive
     Pack.Primitive{Pack.D1} => D1_Primitive

We give different substitutions for "Primitive" on "T" and "D1", but we do not specify the substitution for "D2". Since D2 derives from D1, any occurrence of a call to "Primitive" with a "D2" parameter should be substituted as a "D1_Primitive", however it will be substituted as a "T_Primitive". In other words, the implicit substitution uses the ultimate ancestor, not the direct ancestor. This is due to a bug in Asis-for-Gnat, so there is not much we can do until the bug is fixed.

10.2 Terminating identifiers

There is one very special case of a terminating identifier which is not handled properly by AdaSubst:

THEN the closing name will not be substituted at all. In short, if you write:

     procedure Parent.Child is -- Never a problem here
        ...
     end Parent  -- Crazy comment
         .       -- and crazy way of writing
         Child   -- an identifier
     ;

substitution will not occur on the final name. It is unlikely that we fix this bug in any foreseeable future since:


Previous: Known bugs, Up: Top

11 Future of AdaSubst

The structure of this programs allows it to be extended very easily. Here is a brief overview of our to-do list:

Send good ideas, bug reports, suggestions of improvements to the program or to the documentation, etc. to J-P. Rosen).

Commercial support is available for this product; please refer to the file support.txt in the doc directory. For more information, or if you need any support or assistance for your Ada projects, please visit our Web site or send us a mail.