Statements

Simple and Compound Statements

We can classify statements as either simple or compound. Simple statements don't contain other statements; think of them as "atomic units" that cannot be further divided. Compound statements, on the other hand, may contain other — simple or compound — statements.

Here are some examples from each category:

Category

Examples

Simple statements

Null statement, assignment, subprogram call, etc.

Compound statements

If statement, case statement, loop statement, block statement

Labels

We can use labels to identify statements in the code. They have the following format: <<Some_Label>>. We write them right before the statement we want to apply it to. Let's see an example of labels with simple statements:

with Ada.Text_IO; use Ada.Text_IO; procedure Show_Statement_Identifier is pragma Warnings (Off, "is not referenced"); begin <<Show_Hello>> Put_Line ("Hello World!"); <<Show_Test>> Put_Line ("This is a test."); <<Show_Separator>> <<Show_Block_Separator>> Put_Line ("===================="); end Show_Statement_Identifier;

Here, we're labeling each statement. For example, we use the Show_Hello label to identify the Put_Line ("Hello World!"); statement. Note that we can use multiple labels a single statement. In this code example, we use the Show_Separator and Show_Block_Separator labels for the same statement.

Labels and goto statements

Labels are mainly used in combination with goto statements. (Although pretty much uncommon, we could potentially use labels to indicate important statements in the code.) Let's see an example where we use a goto label; statement to jump to a specific label:

procedure Show_Cleanup is pragma Warnings (Off, "always false"); Some_Error : Boolean; begin Some_Error := False; if Some_Error then goto Cleanup; end if; <<Cleanup>> null; end Show_Cleanup;

Here, we transfer the control to the cleanup statement as soon as an error is detected.

Use-case: Continue

Another use-case is that of a Continue label in a loop. Consider a loop where we want to skip further processing depending on a condition:

procedure Show_Continue is function Is_Further_Processing_Needed (Dummy : Integer) return Boolean is begin -- Dummy implementation return False; end Is_Further_Processing_Needed; A : constant array (1 .. 10) of Integer := (others => 0); begin for E of A loop -- Some stuff here... if Is_Further_Processing_Needed (E) then -- Do more stuff... null; end if; end loop; end Show_Continue;

In this example, we call the Is_Further_Processing_Needed (E) function to check whether further processing is needed or not. If it's needed, we continue processing in the if statement. We could simplify this code by just using a Continue label at the end of the loop and a goto statement:

procedure Show_Continue is function Is_Further_Processing_Needed (Dummy : Integer) return Boolean is begin -- Dummy implementation return False; end Is_Further_Processing_Needed; A : constant array (1 .. 10) of Integer := (others => 0); begin for E of A loop -- Some stuff here... if not Is_Further_Processing_Needed (E) then goto Continue; end if; -- Do more stuff... <<Continue>> end loop; end Show_Continue;

Here, we use a Continue label at the end of the loop and jump to it in the case that no further processing is needed. Note that, in this example, we don't have a statement after the Continue label because the label itself is at the end of a statement — to be more specific, at the end of the loop statement. In such cases, there's an implicit null statement.

Historically

Since Ada 2012, we can simply write:

loop
   --  Some statements...

   <<Continue>>
end loop;

If a label is used at the end of a sequence of statements, a null statement is implied. In previous versions of Ada, however, that is not the case. Therefore, when using those versions of the language, we must write at least a null statement:

loop
   --  Some statements...

   <<Continue>> null;
end loop;

Labels and compound statements

We can use labels with compound statements as well. For example, we can label a for loop:

with Ada.Text_IO; use Ada.Text_IO; procedure Show_Statement_Identifier is pragma Warnings (Off, "is not referenced"); Arr : constant array (1 .. 5) of Integer := (1, 4, 6, 42, 49); Found : Boolean := False; begin <<Find_42>> for E of Arr loop if E = 42 then Found := True; exit; end if; end loop; Put_Line ("Found: " & Found'Image); end Show_Statement_Identifier;

Relevant topics

In addition to labels, loops and block statements allow us to use a statement identifier. In simple terms, instead of writing <<Some_Label>>, we write Some_Label :.

We could rewrite the previous code example using a loop statement identifier:

with Ada.Text_IO; use Ada.Text_IO; procedure Show_Statement_Identifier is Arr : constant array (1 .. 5) of Integer := (1, 4, 6, 42, 49); Found : Boolean := False; begin Find_42 : for E of Arr loop if E = 42 then Found := True; exit Find_42; end if; end loop Find_42; Put_Line ("Found: " & Found'Image); end Show_Statement_Identifier;

Loop statement and block statement identifiers are generally preferred over labels. Later in this chapter, we discuss this topic in more detail.

Exit loop statement

We've introduced bare loops back in the Introduction to Ada course. In this section, we'll briefly discuss loop names and exit loop statements.

A bare loop has this form:

loop
    exit when Some_Condition;
end loop;

We can name a loop by using a loop statement identifier:

Loop_Name:
   loop
      exit Loop_Name when Some_Condition;
   end loop Loop_Name;

In this case, we have to use the loop's name after end loop. Also, having a name for a loop allows us to indicate which loop we're exiting from: exit Loop_Name when.

Let's see a complete example:

with Ada.Text_IO; use Ada.Text_IO; with Ada.Containers.Vectors; procedure Show_Vector_Cursor_Iteration is package Integer_Vectors is new Ada.Containers.Vectors (Index_Type => Positive, Element_Type => Integer); use Integer_Vectors; V : constant Vector := 20 & 10 & 0 & 13; C : Cursor; begin C := V.First; Put_Line ("Vector elements are: "); Show_Elements : loop exit Show_Elements when C = No_Element; Put_Line ("Element: " & Integer'Image (V (C))); C := Next (C); end loop Show_Elements; end Show_Vector_Cursor_Iteration;

Naming a loop is particularly useful when we have nested loops and we want to exit directly from the inner loop:

procedure Show_Inner_Loop_Exit is pragma Warnings (Off); Cond : Boolean := True; begin Outer_Processing : loop Inner_Processing : loop exit Outer_Processing when Cond; end loop Inner_Processing; end loop Outer_Processing; end Show_Inner_Loop_Exit;

Here, we indicate that we exit from the Outer_Processing loop in case a condition Cond is met, even if we're actually within the inner loop.

In the Ada Reference Manual

Block Statements

We've introduced block statements back in the Introduction to Ada course. They have this simple form:

procedure Show_Block_Statement is pragma Warnings (Off); begin -- BLOCK STARTS HERE: declare I : Integer; begin I := 0; end; end Show_Block_Statement;

We can use an identifier when writing a block statement. (This is similar to loop statement identifiers that we discussed in the previous section.) In this example, we implement a block called Simple_Block:

procedure Show_Block_Statement is pragma Warnings (Off); begin Simple_Block : declare I : Integer; begin I := 0; end Simple_Block; end Show_Block_Statement;

Note that we must write end Simple_Block; when we use the Simple_Block identifier.

Block statement identifiers are useful:

  • to indicate the begin and the end of a block — as some blocks might be long or nested in other blocks;

  • to indicate the purpose of the block (i.e. as code documentation).

In the Ada Reference Manual

Extended return statement

A common idiom in Ada is to build up a function result in a local object, and then return that object:

procedure Show_Return is type Array_Of_Natural is array (Positive range <>) of Natural; function Sum (A : Array_Of_Natural) return Natural is Result : Natural := 0; begin for Index in A'Range loop Result := Result + A (Index); end loop; return Result; end Sum; begin null; end Show_Return;

Since Ada 2005, a notation called the extended_return_statement, which allows you to declare the result object and return it as part of one statement, is available. It looks like this:

procedure Show_Extended_Return is type Array_Of_Natural is array (Positive range <>) of Natural; function Sum (A : Array_Of_Natural) return Natural is begin return Result : Natural := 0 do for Index in A'Range loop Result := Result + A (Index); end loop; end return; end Sum; begin null; end Show_Extended_Return;

The return statement here creates Result, initializes it to 0, and executes the code between do and end return. When end return is reached, Result is automatically returned as the function result.

In a later section, we'll see how extended return statements can be almost essential for limited types.

In the Ada Reference Manual