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.
Contents
- Your first Ada program
- Basic Ada program structures
- Time in Ada
- Creating and using tasks
- Protected types
Continue here
- The Ada 95 Reference Manual
- The Ada 95 predefined language environment, i.e. the standard Ada libraries
- The Ada Programming Book on Wikibooks
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:
- Rows starting with '--' are comments
- The name of the file must be the same as the name of the main procedure, i.e. Hello_World
- Ada is case-insensitive, so Hello_World is the same as hello_WORLD
- We load the package Ada.Text_IO (by using with), because it defines the procedure Put_Line(Item : in String);
- By writing use Ada.Text_IO we avoid having to write Ada.Text_IO.Put_Line(Item => Message);.
- Named parameters is a feature of the language: instead of writing Put_Line(Item => Message); we could have written Put_Line(Message);
- 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:
- Empty statement: null;
- Conditional statement, e.g. if
- Loop statement
- Procedure
- Function
- Simple type declaration
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:
- The function keyword is used instead of procedure.
- The return keyword, followed by a data-type, specifies the return type of the function.
- 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.