Auto_Text_IO automates the generation of Put and Get subprograms for user-defined types. It reads a parent package spec, and writes child packages containing Get and Put subprograms for each type in the spec.
Auto_Text_IO can generate up to four files; a public child spec and body, and a private child spec and body. The private child contains Put and Get subprograms for types declared in the private part of the parent package.
See also auto_text_io.html.
Auto_Text_IO uses a naming convention when picking names for child packages, and in general requires the user to also follow the naming convention for user-written packages.
These rules are encoded in the procedures
Auto_Text_IO.Text_IO_Child_Name and
Auto_Text_IO.Private_Text_IO_Child_Name. If you wish to use a
different naming convention, you can rewrite those procedures.
The only requirement on parent names is that generic package names
must start with Gen_.
The public child package name is the parent package name with .Text_IO
appended. In Ada 83 mode, _Text_IO is used instead.
The private child package name is the parent package name with
.Private_Text_IO appended. Private children are not supported
in Ada 83 mode.
For example, a parent package named Cards has text io children named
Cards.Text_IO and Cards.Private_Text_IO.
If the parent package is generic, the child names have .Gen_Text_IO or
.Gen_Private_Text_IO appended. Generic packages are not
supported in Ada 83 mode.
When the parent package is generic, often it has generic formal package parameters. Then the text io child will also have generic formal package parameters, for the text_io children of the parent's generic formal package parameters.
To make the generic formal package parameter visible in the child, it
must be renamed in the parent; the name must be the generic formal
parameter name, with Parent_ prepended.
The name of the generic formal parameter in the child is then the
generic formal parameter name in the parent, with _Text_IO appended.
When the parent package is itself a child of an ancestor generic
package, the text_io child may require a generic formal package
parameter for the ancestor text_io child. The name of this generic
formal parameter is the ancestor package name, with the Gen_ prefix
stripped off, and _Text_IO appended.
In general, generic formal package parameters may need instantiation
arguments (see Math_Scalar_Text_IO in the example below). In
the packages generated by Auto_Text_IO, these arguments are always
previous generic formal package parameters, and they name text_io
packages corresponding to any generic ancestors of the parent package,
and to the arguments of the corresponding generic formal package
parameters of the parent package.
In some cases, not all of these arguments are needed, for example if
no types from the formal package are used. However, it is not possible
for Auto_Text_IO to determine which packages are needed, without
compiling all previously generated packages and querying them. That
would cause the makefiles to become quite complex. So we need help
from the user; the comment -- auto_text_io: ignore must be
applied to each generic formal package parameter that does not need a
text_io child. This allows Auto_Text_IO to generate all text_io
children for a library in one pass.
A simple example:
with Ada.Numerics.Generic_Elementary_Functions;
generic
-- auto_text_io: ignore
with package Elementary is new Ada.Numerics.Generic_Elementary_Functions (Real_Type);
package SAL.Gen_Math.Gen_Scalar is
...
end SAL.Gen_Math.Gen_Scalar;
with SAL.Gen_Math.Gen_Scalar;
generic
-- auto_text_io: ignore
with package Elementary is new Ada.Numerics.Generic_Elementary_Functions (Real_Type);
with package Math_Scalar is new SAL.Gen_Math.Gen_Scalar (Elementary);
package SAL.Gen_Math.Gen_DOF_3 is
...
end SAL.Gen_Math.Gen_DOF_3;
with SAL.Gen_Math.Gen_Text_IO;
generic
with package Math_Text_IO is new SAL.Gen_Math.Gen_Text_IO;
package SAL.Gen_Math.Gen_Scalar.Gen_Text_IO is
...
end SAL.Gen_Math.Gen_Scalar.Gen_Text_IO;
with SAL.Gen_Math.Gen_Scalar.Gen_Text_IO;
with SAL.Gen_Math.Gen_Text_IO;
generic
with package Math_Text_IO is new SAL.Gen_Math.Gen_Text_IO;
with package Math_Scalar_Text_IO is new Parent_Math_Scalar.Gen_Text_IO (Math_Text_IO);
package SAL.Gen_Math.Gen_DOF_3.Gen_Text_IO is
...
end Gen_Math.Gen_DOF_3.Gen_Text_IO;
Given the declaration of SAL.Gen_Math.Gen_DOF_3, the child
SAL.Gen_Math.Gen_DOF_3.Gen_Text_IO has the following generic formal
package parameters:
Math_Text_IOMath_Scalar_Text_IOMath_Scalar takes an argument of Math_Text_IO because
Gen_Math.Gen_Scalar has a generic ancestor Gen_Math. It does
not take a parameter Elementary_Text_IO, because of the comment
auto_text_io: ignore.
However, this does not provide enough control; in some cases, we need to ignore only the instantation parameters, not an entire package. Here is a more complex example:
generic
-- Auto_Text_IO : ignore
with package Elementary is new Ada.Numerics.Generic_Elementary_Functions (Real_Type);
with package Math_Scalar is new SAL.Gen_Math.Gen_Scalar (Elementary);
-- Auto_Text_IO : ignore
with package Math_DOF_3 is new SAL.Gen_Math.Gen_DOF_3 (Elementary, Math_Scalar);
-- Auto_Text_IO : ignore
with package Math_DOF_6 is new SAL.Gen_Math.Gen_DOF_6 (Elementary, Math_Scalar, Math_DOF_3);
package SAL.Gen_Math.Gen_Den_Hart is
generic
with package Math_Text_IO is new SAL.Gen_Math.Gen_Text_IO;
with package Math_Scalar_Text_IO is new Parent_Math_Scalar.Gen_Text_IO (Math_Text_IO);
package SAL.Gen_Math.Gen_Den_Hart.Gen_Text_IO is
...
end SAL.Gen_Math.Gen_Den_Hart.Gen_Text_IO;
generic
-- Auto_Text_IO : ignore
with package Elementary is new Ada.Numerics.Generic_Elementary_Functions (Real_Type);
with package Math_Scalar is new SAL.Gen_Math.Gen_Scalar (Elementary);
with package Math_DOF_3 is new SAL.Gen_Math.Gen_Dof_3 (Elementary, Math_Scalar);
with package Math_DOF_6 is new SAL.Gen_Math.Gen_Dof_6 (Elementary, Math_Scalar, Math_DOF_3);
with package Math_Den_Hart is new SAL.Gen_Math.Gen_Den_Hart
(Elementary,
Math_Scalar,
-- Auto_Text_IO : ignore
Math_DOF_3,
-- Auto_Text_IO : ignore
Math_DOF_6);
package SAL.Gen_Math.Gen_Manipulator is
...
end SAL.Gen_Math.Gen_Manipulator;
generic
with package Math_Text_IO is new SAL.Gen_Math.Gen_Text_IO;
with package Math_Scalar_Text_IO is new Parent_Math_Scalar.Gen_Text_IO (Math_Text_IO);
with package Math_DOF_3_Text_IO is new Parent_Math_DOF_3.Gen_Text_IO (Math_Text_IO, Math_Scalar_Text_IO);
with package Math_DOF_6_Text_IO is new Parent_Math_DOF_6.Gen_Text_IO (Math_Text_IO, Math_Scalar_Text_IO, Math_DOF_3_Text_IO);
with package Math_Den_Hart_Text_IO is new Parent_Math_Den_Hart.Gen_Text_IO (Math_Text_IO, Math_Scalar_Text_IO);
package SAL.Gen_Math.Gen_Manipulator.Gen_Text_IO is
...
end SAL.Gen_Math.Gen_Manipulator.Gen_Text_IO;
The package SAL.Gen_Math.Gen_Den_Hart takes generic parameters
Gen_DOF_3 and Gen_DOF_6, but does not need their text_io children.
However, the package Gen_Manipulator does need their text_io children.
So we must ignore only the instantiation parameters in
Gen_Manipulator.Math_Den_Hart.
For some types, the user may wish to provide their own body for the
basic Put and Get subprograms, to enforce some restriction on the type
or change the text format. For example,
SAL.Gen_Math.Gen_Scalar.Trig_Pair_Type is output as a single
float value in radians, rather than as a float pair.
SAL.Gen_Math.Gen_Scalar.Limit_Type does input validation in Get.
This is handled by labeling the type with the comment --
Auto_Text_IO : separate, and providing a separate subprogram for the
basic Put and Get. This is currently supported only for non-tagged
record types. The specifications for the separate subprograms for a
type named Foo_Type are:
procedure Put_Foo
(File : in Ada.Text_IO.File_Type;
Item : in Foo_Type;
Single_Line_Record : in Boolean := False;
Named_Association_Record : in Boolean := False;
Single_Line_Component : in Boolean := True;
Named_Association_Component : in Boolean := False)
is separate;
procedure Get_Foo
(File : in Ada.Text_IO.File_Type;
Item : out Foo_Type;
Named_Association_Record : in Boolean := False;
Named_Association_Component : in Boolean := False)
is separate;
One way to write the separate subprograms is to first let Auto_Text_IO write the normal subprograms, copy them to separate subprograms, edit them, and then apply the comment to the type.
Auto_Text_IO does not currently support all the types defined in
Standard. Here is a description of how to add support for a new type;
we use the fictitious type Foo as an example.
Foo in Test/machine.ads, type Telem_Type.
Foo to the list of exceptions in Add_To_Context.Standard_Text_IO_Name.
Ada.Text_IO for Foo, in file foo_text_io.ads.
Foo
in the objects of type Telem_Type.
Auto_Text_IO generates code that uses packages from Stephe's Ada Library at run-time.
One of those packages (SAL.Text_IO_Utils) provides simple
whitepsace skipping, and one-character look-ahead. The one-character
look-ahead is provided by Ada 96 Text_IO. The functionality
provided by the Get routines generated by Auto_Text_IO is limited by
this one-character lookahead. For example, it is not possible to
determine by looking at the input whether named association is being
used; that would require looking ahead past an identifier for “=>”.
Similarly, it is not possible to support Ada comments in the input to
Get; that would require two character lookahead.
An alternate design would be to use a lexer (such as provided by OpenToken) that allows looking ahead more characters. However, a lexer requires an internal input buffer. Thus Get operations generated by Auto_Text_IO could not be mixed with plain Ada.Text_IO Get operations; the Ada.Text_IO would not use the lexer's internal buffer, and would miss input.
It would be possible to provide a replacement for all of Ada.Text_IO, that used the lexer's internal input buffer. An early version of SAL provided such a package for Ada 83 (Ada 83 Text_IO did not provide any look-ahead), to support Ada comments in input files for unit tests. But at the moment, I've decided that's not worth it, partly because I now use AUnit for unit tests, and therefore have far less need for input files.