Jump to content

Ada Style Guide/Source Code Presentation

From Wikibooks, open books for an open world

Introduction · Readability

Introduction

[edit | edit source]

The physical layout of source text on the page or screen has a strong effect on its readability. This chapter contains source code presentation guidelines intended to make the code more readable.

In addition to the general purpose guidelines, specific recommendations are made in the "instantiation" sections. If you disagree with the specific recommendations, you may want to adopt your own set of conventions that still follow the general purpose guidelines. Above all, be consistent across your entire project.

An entirely consistent layout is hard to achieve or check manually. Therefore, you may prefer to automate layout with a tool for parameterized code formatting or incorporate the guidelines into an automatic coding template. Some of the guidelines and specific recommendations presented in this chapter cannot be enforced by a formatting tool because they are based on the semantics, not the syntax, of the Ada code. More details are given in the "automation notes" sections.

Code Formatting

[edit | edit source]

The "code formatting" of Ada source code affects how the code looks, not what the code does. Topics included here are horizontal spacing, indentation, alignment, pagination, and line length. The most important guideline is to be consistent throughout the compilation unit as well as the project.

Horizontal Spacing

[edit | edit source]

guideline

[edit | edit source]
  • Use consistent spacing around delimiters.
  • Use the same spacing as you would in regular prose.

instantiation

[edit | edit source]

Specifically, leave at least one blank space in the following places, as shown in the examples throughout this book. More spaces may be required for the vertical alignment recommended in subsequent guidelines.

  • Before and after the following delimiters and binary operators:
  + - * / &
  < = > /= <= >=
  := => | ..
  :
  <>
  • Outside of the quotes for string (") and character (' ) literals, except where prohibited.
  • Outside, but not inside, parentheses.
  • After commas (,) and semicolons (;).

Do not leave any blank spaces in the following places, even if this conflicts with the above recommendations.

  • After the plus (+) and minus (-) signs when used as unary operators.
  • After a function call.
  • Inside of label delimiters (<< >>).
  • Before and after the exponentiation operator (**), apostrophe ('), and period (.)
  • Between multiple consecutive opening or closing parentheses.
  • Before commas (,) and semicolons (;).

When superfluous parentheses are omitted because of operator precedence rules, spaces may optionally be removed around the highest precedence operators in that expression.

example

[edit | edit source]
Default_String : constant String :=
      "This is the long string returned by" &
      " default. It is broken into multiple" &
      " Ada source lines for convenience.";

type Signed_Whole_16 is range -2**15 .. 2**15 - 1;
type Address_Area  is array (Natural range <>) of Signed_Whole_16;

Register : Address_Area (16#7FF0# .. 16#7FFF#);
Memory   : Address_Area (       0 .. 16#7FEC#);

Register (Pc) := Register (A);

X := Signed_Whole_16 (Radius * Sin (Angle));

Register (Index) := Memory (Base_Address + Index * Element_Length);

Get (Value => Sensor);

Error_Term := 1.0 - (Cos (Theta)**2 + Sin (Theta)**2);

Z      := X**3;
Y      := C * X + B;
Volume := Length * Width * Height;

rationale

[edit | edit source]

It is a good idea to use white space around delimiters and operators because they are typically short sequences (one or two characters) that can easily get lost among the longer keywords and identifiers. Putting white space around them makes them stand out. Consistency in spacing also helps make the source code easier to scan visually.

However, many of the delimiters (commas, semicolons, parentheses, etc.) are familiar as normal punctuation marks. It is distracting to see them spaced differently in a computer program than in normal text. Therefore, use the same spacing as in text (no spaces before commas and semicolons, no spaces inside parentheses, etc.).

exceptions

[edit | edit source]

The one notable exception is the colon (:). In Ada, it is useful to use the colon as a tabulator or a column separator (see Guideline 2.1.4). In this context, it makes sense to put spaces before and after the colon rather than only after it as in normal text.

automation notes

[edit | edit source]

The guidelines in this section are easily enforced with an automatic code formatter.

Indentation

[edit | edit source]

guideline

[edit | edit source]
  • Indent and align nested control structures, continuation lines, and embedded units consistently.
  • Distinguish between indentation for nested control structures and for continuation lines.
  • Use spaces for indentation, not the tab character (Nissen and Wallis 1984, §2.2).

instantiation

[edit | edit source]

Specifically, the following indentation conventions are recommended, as shown in the examples throughout this book. Note that the minimum indentation is described. More spaces may be required for the vertical alignment recommended in subsequent guidelines.

  • Use the recommended paragraphing shown in the Ada Reference Manual (1995).
  • Use three spaces as the basic unit of indentation for nesting.
  • Use two spaces as the basic unit of indentation for continuation lines.

A label is outdented three spaces:

begin
<<label>>
   <statement>
end;
<long statement with line break>
  <trailing part of same statement>

The if statement and the plain loop:

if <condition> then
   <statements>
elsif <condition> then
   <statements>
else
   <statements>
end if;
<name>:
loop
   <statements>
   exit when <condition>;
   <statements>
end loop <name>;

Loops with the for and while iteration schemes:

<name>:
   for <scheme> loop
      <statements>
   end loop <name>;
<name>:
   while <condition> loop
      <statements>
   end loop <name>;

The block and the case statement as recommended in the Ada Reference Manual (1995):

<name>:
   declare
      <declarations>
   begin
      <statements>
   exception
      when <choice> =>
         <statements>
      when others =>
         <statements>
   end <name>;
case <expression> is
   when <choice> =>
      <statements>
   when <choice> =>
      <statements>
   when others =>
      <statements>
end case;  --<comment>

These case statements save space over the Ada Reference Manual (1995) recommendation and depend on very short statement lists, respectively. Whichever you choose, be consistent:

case <expression> is
when <choice> =>
     <statements>
when <choice> =>
     <statements>
when others =>
     <statements>
end case;
case <expression> is
   when <choice> => <statements>
                    <statements>
   when <choice> => <statements>
   when others   => <statements>
end case;

The various forms of selective accept and the timed and conditional entry calls:

select
   when <guard> =>
      <accept statement>
      <statements>
or
   <accept statement>
   <statements>
or
   when <guard> =>
      delay <interval>;
      <statements>
or
   when <guard> =>
      terminate;
else
   <statements>
end select;
select
   <entry call>;
   <statements>
or
   delay <interval>;
   <statements>
end select;

select
   <entry call>;
   <statements>
else
   <statements>
end select;

select
   <triggering alternative>
then abort
   <abortable part>
end select;

The accept statement:

accept <specification> do
   <statements>
end <name>;
separate (<parent unit>)
<proper body>

A subunit:

separate (<parent unit>)
<proper body>
end <name>;

Proper bodies of program units:

procedure <specification> is
   <declarations>
begin
   <statements>
exception
   when <choice> =>
      <statements>
end <name>;

function <specification>
  return <type name> is
   <declarations>
begin
   <statements>
exception
   when <choice> =>
      <statements>
end <name>;
package body <name> is
   <declarations>
begin
   <statements>
exception
   when <choice>=>
      <statements>
end <name>;

task body <name> is
   <declarations>
begin
   <statements>
exception
   when <choice>=>
      <statements>
end <name>;

Context clauses on compilation units are arranged as a table. Generic formal parameters do not obscure the unit itself. Function, package, and task specifications use standard indentation:

with <name>; use <name>;
with <name>;
with <name>;

<compilation unit>

generic
   <formal parameters>
<compilation unit>
function <specification>
  return <type>;

package <name> is
   <declarations>
private
   <declarations>
end <name>;

task type <name> is
   <entry declarations>
end <name>;

Instantiations of generic units and record indentation:

procedure <name> is
   new <generic name> <actuals>

function <name> is
   new <generic name> <actuals>

package <name> is
   new <generic name> <actuals>
type ... is
   record
      <component list>
      case <discriminant name> is
         when <choice> =>
            <component list>
         when <choice> =>
            <component list>
      end case;
   end record;

Indentation for record alignment:

for <name> use
   record <mod clause>
      <component clause>
   end record;

Tagged types and type extension:

type ... is tagged
   record
      <component list>
   end record;

type ... is new ... with
   record
      <component list>
   end record;

example

[edit | edit source]
Default_String : constant String :=
      "This is the long string returned by" &
      " default.  It is broken into multiple" &
      " Ada source lines for convenience.";

...

   if Input_Found then
      Count_Characters;

   else  --not Input_Found
      Reset_State;
      Character_Total :=
        First_Part_Total  * First_Part_Scale_Factor  +
        Second_Part_Total * Second_Part_Scale_Factor +
        Default_String'Length + Delimiter_Size;
   end if;

end loop;

rationale

[edit | edit source]

Indentation improves the readability of the code because it gives you a visual indicator of the program structure. The levels of nesting are clearly identified by indentation, and the first and last keywords in a construct can be matched visually.

While there is much discussion on the number of spaces to indent, the reason for indentation is code clarity. The fact that the code is indented consistently is more important than the number of spaces used for indentation.

Additionally, the Ada Reference Manual 1995, §1.1.4 [Annotated] states that the layout shown in the examples and syntax rules in the manual is the recommended code layout to be used for Ada programs: "The syntax rules describing structured constructs are presented in a form that corresponds to the recommended paragraphing . . . . Different lines are used for parts of a syntax rule if the corresponding parts of the construct described by the rule are intended to be on different lines . . . . It is recommended that all indentation be by multiples of a basic step of indentation (the number of spaces for the basic step is not defined)."

It is important to indent continuation lines differently from nested control structures to make them visually distinct. This prevents them from obscuring the structure of the code as you scan it.

Listing context clauses on individual lines allows easier maintenance; changing a context clause is less error-prone.

Indenting with spaces is more portable than indenting with tabs because tab characters are displayed differently by different terminals and printers.

exceptions

[edit | edit source]

If you are using a variable width font, tabs will align better than spaces. However, depending on your tab setting, lines of successive indentation may leave you with a very short line length.

automation notes

[edit | edit source]

The guidelines in this section are easily enforced with an automatic code formatter.

Alignment of Operators

[edit | edit source]

guideline

[edit | edit source]
  • Align operators vertically to emphasize local program structure and semantics.

example

[edit | edit source]
    if Slot_A >= Slot_B then
       Temporary := Slot_A;
       Slot_A    := Slot_B;
       Slot_B    := Temporary;
    end if;

    ----------------------------------------------------------------
    Numerator   := B**2 - 4.0 * A * C;
    Denominator := 2.0 * A;
    Solution_1 := (B + Square_Root(Numerator)) / Denominator;
    Solution_2 := (B - Square_Root(Numerator)) / Denominator;
    ----------------------------------------------------------------

    X := A * B +
         C * D +
         E * F;

    Y := (A * B + C) +  (2.0 * D - E) -  -- basic equation
         3.5;                            -- account for error factor

rationale

[edit | edit source]

Alignment makes it easier to see the position of the operators and, therefore, puts visual emphasis on what the code is doing.

The use of lines and spacing on long expressions can emphasize terms, precedence of operators, and other semantics. It can also leave room for highlighting comments within an expression.

exceptions

[edit | edit source]

If vertical alignment of operators forces a statement to be broken over two lines, especially if the break is at an inappropriate spot, it may be preferable to relax the alignment guideline.

automation notes

[edit | edit source]

The last example above shows a kind of "semantic alignment" that is not typically enforced or even preserved by automatic code formatters. If you break expressions into semantic parts and put each on a separate line, beware of using a code formatter later. It is likely to move the entire expression to a single line and accumulate all the comments at the end. However, there are some formatters that are intelligent enough to leave a line break intact when the line contains a comment. A good formatter will recognize that the last example above does not violate the guidelines and would, therefore, preserve it as written.

Alignment of Declarations

[edit | edit source]

guideline

[edit | edit source]
  • Use vertical alignment to enhance the readability of declarations.
  • Provide, at most, one declaration per line.
  • Indent all declarations in a single declarative part at the same level.

instantiation

[edit | edit source]

For declarations not separated by blank lines, follow these alignment rules:

  • Align the colon delimiters.
  • Align the := initialization delimiter.
  • When trailing comments are used, align the comment delimiter.
  • When the declaration overflows a line, break the line and add an indentation level for those lines that wrap. The preferred places to break, in order, are: (1) the comment delimiter; (2) the initialization delimiter; (3) the colon delimiter.
  • For enumeration type declarations that do not fit on a single line, put each literal on a separate line, using the next level of indentation. When appropriate, semantically related literals can be arranged by row or column to form a table.

example

[edit | edit source]

Variable and constant declarations can be laid out in a tabular format with columns separated by the symbols :, :=, and --

    Prompt_Column : constant        := 40;
    Question_Mark : constant String := " ? "; -- prompt on error input
    Prompt_String : constant String := " ==> ";

If this results in lines that are too long, they can be laid out with each part on a separate line with its unique indentation level:

    subtype User_Response_Text_Frame is String (1 .. 72);
    -- If the declaration needed a comment, it would fit here.
    Input_Line_Buffer : User_Response_Text_Frame
           := Prompt_String &
              String'(1 .. User_Response_Text_Frame'Length -
                           Prompt_String'Length => ' ');

Declarations of enumeration literals can be listed in one or more columns as:

  type Op_Codes_In_Column is
        (Push,
         Pop,
         Add,
         Subtract,
         Multiply,
         Divide,
         Subroutine_Call,
         Subroutine_Return,
         Branch,
         Branch_On_Zero,
         Branch_On_Negative);

or, to save space:

    type Op_Codes_Multiple_Columns is
          (Push,            Pop,                Add,
           Subtract,        Multiply,           Divide,
           Subroutine_Call, Subroutine_Return,  Branch,
           Branch_On_Zero,  Branch_On_Negative);

or, to emphasize related groups of values:

    type Op_Codes_In_Table is
          (Push,            Pop,
           Add,             Subtract,          Multiply,    Divide,
           Subroutine_Call, Subroutine_Return,
           Branch,          Branch_On_Zero,    Branch_On_Negative);

rationale

[edit | edit source]

Many programming standards documents require tabular repetition of names, types, initial values, and meaning in unit header comments. These comments are redundant and can become inconsistent with the code. Aligning the declarations themselves in tabular fashion (see the examples above) provides identical information to both compiler and reader; enforces, at most, one declaration per line; and eases maintenance by providing space for initializations and necessary comments. A tabular layout enhances readability, thus preventing names from "hiding" in a mass of declarations. This applies to all declarations: types, subtypes, objects, exceptions, named numbers, and so forth.

automation notes

[edit | edit source]

Most of the guidelines in this section are easily enforced with an automatic code formatter. The one exception is the last enumerated type example, which is laid out in rows based on the semantics of the enumeration literals. An automatic code formatter will not be able to do this and will likely move the enumeration literals to different lines. However, tools that are checking only for violations of the guidelines should accept the tabular form of an enumeration type declaration.

More on Alignment

[edit | edit source]

guideline

[edit | edit source]
  • Align parameter modes and parentheses vertically.

instantiation

[edit | edit source]

Specifically, it is recommended that you:

  • Place one formal parameter specification per line.
  • Vertically align parameter names, colons, the reserved word in, the reserved word out, and parameter subtypes.
  • Place the first parameter specification on the same line as the subprogram or entry name. If any parameter subtypes are forced beyond the line length limit, place the first parameter specification on a new line indented the same as a continuation line.

example

[edit | edit source]
    procedure Display_Menu (Title   : in     String;
                            Options : in     Menus;
                            Choice  :    out Alpha_Numerics);

The following two examples show alternate instantiations of this guideline:

    procedure Display_Menu_On_Primary_Window
          (Title   : in     String;
           Options : in     Menus;
           Choice  :    out Alpha_Numerics);

or:

    procedure Display_Menu_On_Screen (
          Title   : in     String;
          Options : in     Menus;
          Choice  :    out Alpha_Numerics
        );

Aligning parentheses makes complicated relational expressions more clear:

    if not (First_Character in Alpha_Numerics and then
            Valid_Option(First_Character))        then

rationale

[edit | edit source]

This alignment facilitates readability and understandability, and it is easy to achieve given automated support. Aligning parameter modes provides the effect of a table with columns for parameter name, mode, subtype, and, if necessary, parameter-specific comments. Vertical alignment of parameters across subprograms within a compilation unit increases the readability even more.

notes

[edit | edit source]

Various options are available for subprogram layout. The second example above aligns all of the subprogram names and parameter names in a program. This has the disadvantage of occupying an unnecessary line where subprogram names are short and looking awkward if there is only one parameter.

The third example is a format commonly used to reduce the amount of editing required when parameter lines are added, deleted, or reordered. The parentheses do not have to be moved from line to line. However, the last parameter line is the only one without a semicolon.

exceptions

[edit | edit source]

When an operator function has two or more formal parameters of the same type, it is more readable to declare the parameters in a single one-line list rather than to separate the formal parameter list into multiple formal parameter specifications.

    type Color_Scheme is (Red, Purple, Blue, Green, Yellow, White, Black, Brown, Gray, Pink);

    function "&" (Left, Right : Color_Scheme) return Color_Scheme;

automation notes

[edit | edit source]

Most of the guidelines in this section are easily enforced with an automatic code formatter. The one exception is the last example, which shows vertical alignment of parentheses to emphasize terms of an expression. This is difficult to achieve with an automatic code formatter unless the relevant terms of the expression can be determined strictly through operator precedence.

Blank Lines

[edit | edit source]

guideline

[edit | edit source]
  • Use blank lines to group logically related lines of text (NASA 1987).

example

[edit | edit source]
    if ... then

       for ... loop
          ...
       end loop;

    end if;

This example separates different kinds of declarations with blank lines:

    type Employee_Record is
       record
          Legal_Name    : Name;
          Date_Of_Birth : Date;
          Date_Of_Hire  : Date;
          Salary        : Money;
       end record;

    type Day is
          (Monday,    Tuesday,   Wednesday, Thursday,  Friday,
           Saturday,  Sunday);

    subtype Weekday is Day range Monday   .. Friday;
    subtype Weekend is Day range Saturday .. Sunday;

rationale

[edit | edit source]

When blank lines are used in a thoughtful and consistent manner, sections of related code are more visible to readers.

automation notes

[edit | edit source]

Automatic formatters do not enforce this guideline well because the decision on where to insert blank lines is a semantic one. However, many formatters have the ability to leave existing blank lines intact. Thus, you can manually insert the lines and not lose the effect when you run such a formatter.

Pagination

[edit | edit source]

guideline

[edit | edit source]
  • Highlight the top of each package or task specification, the top of each program unit body, and the end statement of each program unit.

instantiation

[edit | edit source]

Specifically, it is recommended that you:

  • Use file prologues, specification headers, and body headers to highlight those structures as recommended in Guideline 3.3.
  • Use a line of dashes, beginning at the same column as the current indentation to highlight the definition of nested units embedded in a declarative part. Insert the line of dashes immediately before and immediately after the definition.
  • If two dashed lines are adjacent, omit the longer of the two.

example

[edit | edit source]
    with Basic_Types;
    package body SPC_Numeric_Types is
       ---------------------------------------------------------------------
       function Max
             (Left  : in     Basic_Types.Tiny_Integer;
              Right : in     Basic_Types.Tiny_Integer)
             return Basic_Types.Tiny_Integer is
       begin
          if Right < Left then
             return Left;
          else
             return Right;
          end if;
       end Max;
       ---------------------------------------------------------------------
       function Min
             (Left  : in     Basic_Types.Tiny_Integer;
              Right : in     Basic_Types.Tiny_Integer)
             return Basic_Types.Tiny_Integer is
       begin
          if Left < Right then
             return Left;
          else
             return Right;
          end if;
       end Min;
       ---------------------------------------------------------------------
       use Basic_Types;
    begin  -- SPC_Numeric_Types
       Max_Tiny_Integer := Min(System_Max, Local_Max);
       Min_Tiny_Integer := Max(System_Min, Local_Min);
       -- ...
    end SPC_Numeric_Types;

rationale

[edit | edit source]

It is easy to overlook parts of program units that are not visible on the current page or screen. The page lengths of presentation hardware and software vary widely. By clearly marking the program's logical page boundaries (e.g., with a dashed line), you enable a reader to quickly check whether all of a program unit is visible. Such pagination also makes it easier to scan a large file quickly, looking for a particular program unit.

exceptions

[edit | edit source]

This guideline does not address code layout on the physical "page" because the dimensions of such pages vary widely and no single guideline is appropriate.

automation notes

[edit | edit source]

The guidelines in this section are easily enforced with an automatic code formatter.

Number of Statements Per Line

[edit | edit source]

guideline

[edit | edit source]
  • Start each statement on a new line.
  • Write no more than one simple statement per line.
  • Break compound statements over multiple lines.

example

[edit | edit source]

Use:

    if End_Of_File then
       Close_File;
    else
       Get_Next_Record;
    end if;

rather than:

    if End_Of_File then Close_File; else Get_Next_Record; end if;

exceptional case:

    Put("A=");    Natural_IO.Put(A);    New_Line;
    Put("B=");    Natural_IO.Put(B);    New_Line;
    Put("C=");    Natural_IO.Put(C);    New_Line;

rationale

[edit | edit source]

A single statement on each line enhances the reader's ability to find statements and helps prevent statements being missed. Similarly, the structure of a compound statement is clearer when its parts are on separate lines.

exceptions

[edit | edit source]

If a statement is longer than the remaining space on the line, continue it on the next line. This guideline includes declarations, context clauses, and subprogram parameters.

According to the Ada Reference Manual 1995, §1.1.4 [Annotated], "The preferred places for other line breaks are after semicolons."

automation notes

[edit | edit source]

The guidelines in this section are easily enforced with an automatic code formatter, with the single exception of the last example, which shows a semantic grouping of multiple statements onto a single line.

exceptions

[edit | edit source]

The example of Put and New_Line statements shows a legitimate exception. This grouping of closely related statements on the same line makes the structural relationship between the groups clear.

Source Code Line Length

[edit | edit source]

guideline

[edit | edit source]
  • Adhere to a maximum line length limit for source code (Nissen and Wallis 1984, §2.3).

instantiation

[edit | edit source]

Specifically, it is recommended that you:

  • Limit source code line lengths to a maximum of 72 characters.

rationale

[edit | edit source]

When Ada code is ported from one system to another, there may be restrictions on the record size of source line statements, possibly for one of the following reasons: some operating systems may not support variable length records for tape I/O, or some printers and terminals support an 80-character line width with no line-wrap. See further rationale in the note for Guideline 7.1.2.

Source code must sometimes be published for various reasons, and letter-size paper is not as forgiving as a computer listing in terms of the number of usable columns.

In addition, there are human limitations in the width of the field of view for understanding at the level required for reading source code. These limitations correspond roughly to the 70- to 80-column range.

exceptions

[edit | edit source]

An alternate instantiation is to limit source code length to 79 characters. The 79-character limit differentiates the code from the FORTRAN 72-character limit. It also avoids problems with 80-character width terminals where the character in the last column may not print correctly.

automation notes

[edit | edit source]

The guidelines in this section are easily enforced with an automatic code formatter.

Summary

[edit | edit source]

code formatting

[edit | edit source]
  • Use consistent spacing around delimiters.
  • Use the same spacing as you would in regular prose.
  • Indent and align nested control structures, continuation lines, and embedded units consistently.
  • Distinguish between indentation for nested control structures and for continuation lines.
  • Use spaces for indentation, not the tab character (Nissen and Wallis 1984, §2.2).
  • Align operators vertically to emphasize local program structure and semantics.
  • Use vertical alignment to enhance the readability of declarations.
  • Provide, at most, one declaration per line.
  • Indent all declarations in a single declarative part at the same level.
  • Align parameter modes and parentheses vertically.
  • Use blank lines to group logically related lines of text (NASA 1987).
  • Highlight the top of each package or task specification, the top of each program unit body, and the end statement of each program unit.
  • Start each statement on a new line.
  • Write no more than one simple statement per line.
  • Break compound statements over multiple lines.
  • Adhere to a maximum line length limit for source code (Nissen and Wallis 1984, §2.3).

Readability