Skip to main content
Department of Information Technology

Ada Quick Start

This is a short on-line introduction to get you started with the ADA programming language. It is very simplistic, for a more complete description see the reference guide.

Your first Ada program

The program below prints Hello World.

  -- File: hello_world. adb
  with Ada.Text_IO ; -- Use package Ada.Text_IO
  use Ada.Text_IO ;   -- Integrate its namespace

  procedure hello_world is
      Message : constant String := " Hello World " ;
  begin
      Put_Line(Item => Message) ;
  end hello_world ;

Some things to note:

  1. Rows starting with '--' are comments
  2. The name of the file must be the same as the name of the main procedure, i.e. Hello_World
  3. Ada is case-insensitive, so Hello_World is the same as hello_WORLD
  4. We load the package Ada.Text_IO (by using with), because it defines the procedure Put_Line(Item : in String);
  5. By writing use Ada.Text_IO we avoid having to write Ada.Text_IO.Put_Line(Item => Message);.
  6. Named parameters is a feature of the language: instead of writing Put_Line(Item => Message); we could have written Put_Line(Message);
  7. The assignment operator is ':=', and the operators testing for equality and inequality are '=' and '/=', respectively

The named parameters are actually useful for calls with multiple arguments, because then the arguments are bound to the parameter with the corresponding name, instead of using the order in which they are passed. For example:

-- Procedure declaration:
procedure Add_Item(Index : in Integer; Item : in DataItem);

-- Two equivalent calls to the procedure:
Add_Item(i, Data);
Add_Item(Item => Data, Index => i);


Basic Ada program structures

How to write a:

Conditional statement

The following if statement prints a different string depending on wether the value of the variable x is zero.

   if x = 0 then
      Put_Line("Value of x is zero.");
   else
      Put_Line("Value of x is non-zero.");
   end if;

Note: The else-part is optional: The following only prints a string if the value of x is zero.

   if x = 0 then
      Put_Line("Value of x is zero.");
   end if;

This is equivalent to:

   if x = 0 then
      Put_Line("Value of x is zero.");
   else
      null;
   end if;

Loop statement

The following piece of code prints Hello World 10 times, assuming that we have declared an integer variable i.

   i := 1;
   loop
      Put_Line(Item => "Hello World");
      i := i + 1;
      exit when i = 10;
   end loop;

This code does the same thing (explanation below):

   j := 1
Outer_Loop:
   loop
      i := 1;
      loop
         Put_Line(Item => "Hello World");
         i := i + 1;
         if i = 10 then
            exit loop Outer_Loop;
         end if;
      end loop;
      j := j + 1;
      exit when j = 10;
   end loop;

It looks almost like it would print a hundred times, but the statement exit loop Outer_Loop; aborts the outer loop after ten iterations in the inner loop. This is known as a named loop, in that we have the name Outer_Loop identifying a loop. Using this name we can exit any loop in a hierarchy of loops.

Loops can be annotated with "for" or "while" to control the number of iterations.

Types

In Ada you can use different built-in basic types like Boolean, Integer, Float, Character, String. Further, you can make up own types.

Three useful forms of type declarations are:

  subtype Die is Integer range 1 .. 6; -- One die
  type Dice is array (0 .. 22) of Die ;  -- 23 dice
  type Ring is mod 23; -- Modular type , range 0 .. 22

  • The first declares Die as an integer subtype containing all values in the interval [1, 6]. Operations like + and * leading to results outside the interval will cause an exception.
  • The second declares Dice as an array type with 23 elements of the type Die.
  • The third declades Ring as a modular type with all values in the interval [0, 22]. Operations like + and * on such types are always implicitly followed by a modulo operation, such that the result will again be within the interval.

Another useful type declaration is the recod, e.g.:

   -- Parameters to a PID controller is a triple
   type Pid_Parameters is
      record
         P : Float; -- Proportional part
         I : Float; -- Integral part
         D : Float; -- Derivative part
      end record;

Procedure

This is a declaration of a procedure:

   procedure Print_On_Equal(a, b : in Integer; Message in out String);

The declaration makes the procedure Print_On_Equal available for use by specifying its name and type. The definition of a procedure also attaches code to this specification:

   procedure Print_On_Equal(a, b : in Integer; Message : in out String) is
      i : Integer := a - b;
   begin
      if i = 0 then
         Put_Line(Message);
      else
         Message := "";
      end if;
   end Print_On_Equal;

The definition begins same as a declaration, but before the semicolon we have the is keyword, variable declarations, the begin keyword, the procedure body, and then end Print_On_Equal

Function

Functions are declared and defined similarly to procedures, with a few exceptions:

  1. The function keyword is used instead of procedure.
  2. The return keyword, followed by a data-type, specifies the return type of the function.
  3. The body of the function should be exited by a return statement.

Example:

   function Multiply(Term1, Term2 : Float) return Float is
   begin
      return Term1 * Term2;
   end Multiply;


Time in Ada

To delay execution for a certain amount of time, say 10 seconds, write:

   delay 10.0;

To delay execution until after a certain time t, write:

   delay until t;

The package Ada.Calendar defines the variable Clock, containing current system time.
This can be used in the following way to create a periodic loop:

   with Ada.Calendar;
   use Ada.Calendar;

   -- Prints a message periodically
   procedure Periodic_Put(Message : in String; Period : in Duration) is
         Next_Time : Time := Clock + Period;
      begin
         loop
            Ada.Text_IO.Put_Line(Message);
            delay until Next_Time;
            Next_Time := Next_Time + Period;
         end loop;
      end Periodic_Put;

Note the different semantics of Duration and Time: Variables of type Time store absolute time information, like (absolute) time points; Variables of type Duration store relative time information, like time intervals, i.e., the difference between two time points. The statements delay and delay until expect differently typed parameters.

Advice: The above is the preferred way to create a periodic loop, since just using delay will accumulate an error in the execution period.


Creating and using tasks

Declaring a task

   task type My_Task is
      entry Init;
      entry Get(Item : out DataItem);
      entry Put(Item : in DataItem);
   end My_Task;

Defining a task body

   task body My_Task is
      -- Declarations needed by task code go here.
   begin
      -- Task code goes here.
   end My_Task;

The select statement

   -- This task maintains a stack of data items.
   accept Init; -- Wait for client to call 'Init' before proceeding.
   select
      -- Guarded entries:

      when not Is_Empty(Container) =>
         -- Will only accept 'Get' when non-empty.
         accept Get(Item : out DataItem) do
            Item := Get_Head(Container);
         end Get;
         Pop_Head(Container);
   or
      when not Is_Full(Container) =>
         -- Will only accept 'Put' when not full.
         accept Put(Item : in DataItem) do
            Put_Tail(Container, Item);
         end Put;
   or
      -- Will continue after 'delay' only if
      -- 'Get' and 'Put' can not be accepted for 120s.
      delay 120.0;
      Put_Line("Timeout!");
   end select;


Protected types

Protected types provide mutually exclusive (i.e. thread safe) access to its internal data.

   -- Declaration of an array data-type used internally.
   type Buffer_Type is array (1 .. 1000) of Data_Item;

   -- Declaration of a thread safe buffer type.
   protected type Shared_Buffer is
      procedure Get(Item : out Data_Item);
      procedure Put(Item : in Data_Item);
   private
      -- The internal data of the shared buffer
      Length : Integer := 0;
      Buffer : Buffer_Type;
   end Shared_Buffer;

   -- Definition of body of protected type
   protected body Shared_Buffer is
      procedure Get(Item : out Data_Item) is
      begin
         if Length > 0 then
            Item := Buffer(Length);
         end if;
      end Get;

      procedure Put(Item : in Data_Item) is
      begin
         If Length < 1000 then
            Length := Length + 1;
            Buffer(Length) := Item;
         end if;
      end Put;
   end Shared_Buffer;

Protected types can also use entries. The same code would look like this:

   -- Declaration of an array data-type used internally.
   type Buffer_Type is array (1 .. 1000) of Data_Item;

   -- Declaration of a thread safe buffer type.
   protected type Shared_Buffer is
      entry Get(Item : out Data_Item);
      entry Put(Item : in Data_Item);
   private
      -- The internal data of the shared buffer
      Length : Integer := 0;
      Buffer : Buffer_Type;
   end Shared_Buffer;

   -- Definition of body of protected type
   protected body Shared_Buffer is
      entry Get(Item : out Data_Item) when (Length > 0) is
      begin      
            Item := Buffer(Length);
      end Get;

      entry Put(Item : in Data_Item) when (Length < 1000) is
      begin
            Length := Length + 1;
            Buffer(Length) := Item;
      end Put;
   end Shared_Buffer;

Note that this entry implementation is not equivalent to the previous procedure implementation. In the entry implementation, Get entry and Put entry have guards while the if statements in the Get and Put procedures are not guards.

Updated  2014-08-27 18:05:43 by Philipp Rümmer.