[Ada Information Clearinghouse]

Ada '83 Quality and Style:

Guidelines for Professional Programmers

Copyright 1989, 1991,1992 Software Productivity Consortium, Inc., Herndon, Virginia.


CHAPTER 2: Source Code Presentation

2.1 Code Formatting

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.
In this section...
2.1.1 Horizontal Spacing
2.1.2 Indentation
2.1.3 Alignment of Operators
2.1.4 Alignment of Declarations
2.1.5 More on Alignment
2.1.6 Blank Lines
2.1.7 Pagination
2.1.8 Number of Statements Per Line
2.1.9 Source Code Line Length
Summary of Guidelines from this section


2.1.1 Horizontal Spacing

guideline

instantiation

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. Do not leave any blank spaces in the following places, even if this conflicts with the above recommendation. When superfluous parentheses are omitted because of operator precedence rules, spaces may optionally be removed around the highest precedence operators in that expression.

example

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

It is a good idea to use white space around delimiters and operators because they are typically short (one or two character) sequences 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, they should be spaced the same (no spaces before commas and semicolons, no spaces inside of parentheses, etc.).

exception

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 as in normal text.

automation notes

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

Language Ref Manual references: 2.2 Lexical Elements, Separators, and Delimiters, 2.6 String Literals, 3.6.3 The Type String, 4.4 Expressions, 4.5 Operators and Expression Evaluation, 4.5.2 Relational Operators and Membership Tests, 4.5.3 Binary Adding Operators, 6.4 Subprogram Calls, 6.5 Function Subprograms


2.1.2 Indentation

guideline

instantiation

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. A label is outdented three spaces. A continuation line is indented two spaces:
begin 
<<label>>                            | <long statement with line break> 
   <statement>                       |   <trailing part of same statement> 
end; 

The if statement and the plain loop:
   if <condition> then               | <name>: 
      <statements>                   |    loop 
   elsif <condition> then            |       <statements> 
      <statements>                   |       exit when <condition>; 
   else                              |       <statements> 
      <statements>                   |    end loop <name>; 
   end if;                           | 

Loops with the for and while iteration schemes:
   <name>:                           | <name>: 
      for <scheme> loop              |    while <condition> loop 
         <statements>                |       <statements> 
      end loop <name>;               |    end loop <name>; 

The block and the case statement as recommended in the Ada Language Reference Manual (Department of Defense 1983):
   <name>:                           | case <expression> is 
      declare                        |    when <choice> => 
         <declarations>              |       <statements> 
      begin                          |    when <choice> => 
         <statements>                |       <statements> 
      exception                      |    when others => 
         when <choice> =>            |       <statements> 
            <statements>             | end case;  --<comment> 
         when others =>              | 
            <statements>             | 
      end <name>;                    |

These case statements save space over the the Ada Language Reference Manual (Department of Defense 1983) recommendation and depend on very short statement lists, respectively. Whichever you choose, be consistent.
   case <expression> is              | case <expression> is 
   when <choice> =>                  |    when <choice> => <statements> 
        <statements>                 |                     <statements> 
   when <choice> =>                  |    when <choice> => <statements> 
        <statements>                 |    when others   => <statements> 
   when others =>                    | end case; 
        <statements>                 | 
   end case;                         |

The various forms of selective wait and the timed and conditional entry calls:
   select                            | select 
      when <guard> =>                |    <entry call>; 
         <accept statement>          |    <statements> 
         <statements>                | or 
   or                                |    delay <interval>; 
      <accept statement>             |    <statements> 
      <statements>                   | end select; 
   or                                | 
      when <guard> =>                | 
         delay <interval>;           | 
         <statements>                | 
   or                                | select 
      when <guard> =>                |    <entry call>; 
         terminate;                  |    <statements> 
   else                              | else 
      <statements>                   |    <statements> 
   end select;                       | end select;

The accept statement and a subunit:
   accept <specification> do         | separate (<parent unit>) 
      <statements>                   | <proper body> 
   end <name>;                       |

Proper bodies of program units:
   procedure <specification> is      | package body <name> is 
      <declarations>                 |    <declarations> 
   begin                             | begin 
      <statements>                   |    <statements> 
   exception                         | exception 
      when <choice> =>               |    when <choice> => 
         <statements>                |       <statements> 
   end <name>;                       | end <name>; 
                                     | 
   function  <specification>         | task body <name> is 
     return  <type name> is          |    <declarations> 
      <declarations>                 | begin 
   begin                             |    <statements> 
      <statements>                   | exception 
   exception                         |    when <choice> => 
      when <choice> =>               |       <statements> 
         <statements>                | end <name>; 
   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>;                      | function  <specification> 
   with <name>;                      |   return <type>; 
   with <name>;                      | 
                                     | package <name> is 
   use  <name>;                      |    <declarations> 
                                     | private 
   <compilation unit>                |    <declarations> 
                                     | end <name>; 
                                     | 
   generic                           | task type <name> is 
      <formal parameters>            |    <entry declarations> 
   <compilation unit>                | end <name>;

Instantiations of generic units and record indentation:
   procedure <name> is               |type ... is 
      new <generic name> <actuals>   |   record 
                                     |      <component list> 
   function <name> is                |      case <discriminant name> is 
      new <generic name> <actuals>|            when <choice> => 
                                     |            <component list> 
   package <name> is                 |         when <choice> => 
      new <generic name> <actuals>   |            <component list> 
                                     |      end case; 
                                     |   end record;

Indentation for record alignment:
   for <name> use 
      record <alignment clause> 
         <component clause> 
      end record;

example

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

loop
   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

Indentation improves the readability of the code because it gives the reader 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, in Section 1.5, the Ada Language Reference Manual says that the layout shown in the examples and syntax rules in the Ada Language Reference 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 the user scans it.

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

automation notes

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

Language Ref Manual references: 1.5 Method of Description and Syntax Notation, 2.6 String Literals, 3.7 Record Types, 5 Statements, 6.1 Subprogram Declarations, 7 Packages, 9.7 Select Statements, 10.1.1 Context Clauses - With Clauses, 11.2 Exception Handlers, 12.1 Generic Declarations, 12.3 Generic Instantiation, 13.4 Record Representation Clauses


2.1.3 Alignment of Operators

guideline

example

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) +  -- basic equation 
     (2.0 * D - E) -  -- 
     3.5;             -- account for error factor

rationale

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

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

automation notes

The last example above shows a type of "semantic alignment" which 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 which 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.

Language Ref Manual references: 4.4 Expressions, 4.5 Operators and Expression Evaluation, 5.2 Assignment Statement


2.1.4 Alignment of Declarations

guideline


instantiation

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

example

Variable and constant declarations can be laid out in a table 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

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 type declarations as well as object declarations.

automation notes

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 only checking for violations of the guidelines should accept the tabular form of an enumeration type declaration.

Language Ref Manual references: 2.6 String Literals, 2.7 Comments, 3.5.1 Enumeration Types, 3.9 Declarative Parts, 3 Declarations and Types


2.1.5 More on Alignment

guideline

instantiation

Specifically it is recommended that you:

example

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

or
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

This facilitates readability and understandability. Aligning parameter modes provides the effect of a table with columns for parameter name, mode, type, and, if necessary, parameter-specific comments. Vertical alignment of parameters across subprograms within a compilation unit increases the readability even more.

note

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 don't have to be moved from line to line. However, the last parameter line is the only one without a semicolon.

automation notes

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. Language Ref Manual references: 4.4 Expressions, 5.3 If Statements, 6.2 Formal Parameter Modes


2.1.6 Blank Lines

guideline

example

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

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

automation notes

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.

Language Ref Manual references: 3 Declarations and Types, 5 Statements


2.1.7 Pagination

guideline

instantiation

Specifically, it is recommended that you:

example

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

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.

note

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

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

Language Ref Manual references: 2.7 Comments, 6.3 Subprogram Bodies, 7.2 Package Specifications and Declarations, 7.3 Package Bodies, 9.1 Abort Statements


2.1.8 Number of Statements Per Line

guideline

example

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

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.

note

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 Language Reference Manual (Department of Defense 1983), "The preferred places for other line breaks are after semicolons."

exceptions

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

automation notes

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.

Language Ref Manual references: 1.5 Method of Description and Syntax Notation, 3 Declarations and Types, 5 Statements


2.1.9 Source Code Line Length

guideline

instantiation

Specifically, it is recommended that you:

rationale

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.1.

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.

automation notes

The guidelines in this section are easily enforced with an automatic code formatter.
Back to document index