Hoppa till huvudinnehållet
Institutionen för informationsteknologi

Your first Ada program

The program below prints Hello World.

   -- File: Hello_World.adb
   with Ada.Text_IO;
   use Ada.Text_IO;

   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");
      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");
         if i = 10 then
            exit loop Outer_Loop;
         end if;
      end loop;
      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.

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;

Simple type declaration

Two useful forms of type declarations are:

   subtype Die is Integer range 1 .. 6; -- One die
   type Dice is array (1 .. 2) of Die;  -- Two dice

The first declares Die as an integer subtype containing all values in the interval [1, 6].
The second declares Dice as an array type with two elements of the type Die.

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;


Using packages

The interface of an Ada package is declared in an Ada Specification file, with the extension .ads
The body of that same package is defined an Ada Body file, with the extension .adb

The package introduces a separate namespace for all entities declared in its specification, and data hiding for anything defined in the body but not declared in the specification. The name of the package must be the same as the base name of the package specification and body files.

with package gives access to all entities declared in package.
use package imports all names in the package into the global namespace.


Creating and using tasks

Declaring a task type

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

Defining a task body

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

Defining a task instance

This is done by defining a varaiable of a task type:

   -- When defined in a static structure, the task
   -- will start executing when application is started.
   My_Task : My_Task_Type;

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;


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: This is the preferred way to create a periodic loop, since using delay will accumulate an error in the execution period.


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;

Uppdaterad  2008-10-02 10:31:28 av Simon Tschirner.