by Jim Rogers

This is part two (of five) of this article. Click here to return to part one, or here to return to the contents.

Basic Ada Syntax

Every program must have a starting place. In the C family of languages this starting place is always a function called main. Ada has no rule about the name of the program starting point. The program starting point is a compilation unit containing only a procedure with no parameters. The simplest Ada programs can be written entirely in one procedure. The following "hello world" example demonstrates how to write a very simple Ada program.

Hello World

-----------------------------------------------------------
-- Hello World program
-----------------------------------------------------------
with Ada.Text_Io;
use Ada.Text_Io;

procedure Hello is
begin
   Put_Line("Hello World!");
end Hello;

The first three lines are comments. Ada comments start with a pair of hyphens and continue to the end of the line. The with clause declares that this program requires access to a package named Ada.Text_IO. This makes the package visible to the program, but does not make its contents directly visible. The use clause makes the contents of the Ada.Text_IO package visible to this program.

Ada identifiers are not case sensitive. Thus procedure and Procedure are equivalent.

Hello is the name of this procedure, which is the starting point for our little program.
Put_Line is a procedure from the Ada.Text_IO package that takes a string as a parameter and outputs to standard output.
Put_Line appends a newline character to the end of the output.

The combination of begin and end defines the body of the procedure.

Ada does not provide a preprocessor. Preprocessors were left out of the language on purpose. The view of the Ada language developers was that preprocessor macros are unsafe. Interestingly, many C++ developers also believe that preprocessor macros are unsafe and should be avoided whenever possible. The Ada with clause establishes a compilation dependency. It does not cause the build tools to modify the source code like a preprocessor does. This means that you can with the same module as many times as you want without causing compilation or linking problems.

Ada Types

Ada provides a large number of kinds of data types.

Ada does not have a predefined inheritance hierarchy like many object oriented programming languages. Ada allows you to define your own data types, including numeric data types. Defining your own type in Ada creates a new type. In C or C++, the typedef command simply creates an alias for a type.

Elementary Types
Access Types Used to reference objects. Ada access types are similar to reference types in C++ and Java.
Scalar Types
Discrete Types
Enum- eration Types Ada provides some predefined enumeration types. The Boolean type is an enumeration type with two values: True and False. The Character type is another enumeration type. You are allowed to define your own enumeration types.
type Colors is
   (Red, Orange, Yellow, Green,
	     Blue, Indigo, Violet);
Enumeration types are not numeric types.
Integer Types
Signed Integer Types The predefined type Integer is a signed integer type. You are allowed to define your own signed integer types.
type My_Int is range 1..100;
Even though the type definition above does not allow negative values, the type My_Int is implemented as a signed integer. All arithmetic on signed integers must deal with overflow and underflow issues. Ada will raise the exception Constraint_Error if an attempt is made to assign an out of range value to a signed integer object.
Modular Types You must define your own modular types.
type byte is mod 2**8;
The above definition defines an unsigned integer (modular) type named byte. The range of values for byte is 0 through 255, or the cycle of values derived from the modulus of 256. The expression 2**8 is read as 2 to the 8th power.

All arithmetic on modular types results in a value within the modular type’s valid range. For instance, if you define an instance of the byte type described above:

A : byte := 0;
This instance, A, is explicitly initialized to 0. Subtracting 1 from A will result in a value of 255. Modular types will not generate overflow or underflow errors.
Real Types
Floating Point Types Ada provides a predefined floating point type called float. You are allowed to define your own floating point types.
type Sin_Values is digits 10
   range -1.0..1.0;
This type definition defines a type named Sin_Values with 10 decimal digits of precision and a valid range of values from -1.0 through 1.0.
type Double is digits 15;
This type definition defines a floating point type named Double with 15 decimal digits of precision. This type would correspond exactly to the double primitive type in Java. The range restriction shown in the Sin_Values type definition is optional.
Fixed Point Types Fixed point types are real types with a fixed number of digits to the right of the radix character. These types are very much like decimal types in Cobol. Fixed point types come in two kinds. Fixed point types always have a predefined distance between neighboring values.
Ordinary The distance between values is implemented as a power of 2.
type Batting_Averages is
    delta 0.001
       range 0.0..1.0;
Batting_Averages is a fixed point type whose values are evenly spaced real numbers in the range from 0 through 1. The distance between the values is no more than 1/1000. The actual distance between values may be 1/1024, which is 2-10.
Decimal The distance between values is implemented as a power of 10.
type Balances is
   delta 0.01 digits 9 
      range
         0.0..9_999_999.99;
The important different in the definition of a decimal fixed point type from an ordinary fixed point type is the specification of the number of digits of precision. This example also shows how Ada allows you to specify numeric literals with underscores separating groups of digits. The underscore is used to improve readability.
Composite Types
array All arrays in Ada are instances of some type. There are some predefined array types in Ada. The string type is defined as an array of characters. Array types may be constrained or unconstrained types. Constrained types have a predefined size. Unconstrained types may have instances of different sizes.
Constrained type definition
type Int_Buffer is array (1..10)
   of Integer;
Int_Buffer is an array type. The valid range of index values is 1 through 10. Each element of the array is an Integer.
Unconstrained type definition
type String is array (Positive range <>)
   of Character;
This is the actual definition of a string in Ada. A string is an array of characters. The array may be any length, but the index will be a subset of the predefined integer subtype named Positive. Positive is defined as all Integer values starting at 1 through the highest value of Integer. Each instance of an indefinite array type must have its size declared when the instance is defined.
Last_Name : String(1..20);
Last_Name is defined as a 20 character string.
Array Index Values
Ada array types must specify the range of values for their array index. Any discrete type can be used as an array index. The purpose of this freedom is to allow the programmer to cleanly model the real world in code. For instance, if you want to collect the frequency of occurrence of numbers spaced around a normalized value you can define an array type as follows:
type Normalized_Distribution is array(-100..100)
   of Natural;
Natural is a predefined subtype of Integer with a minimum value of 0 and a maximum value of the highest value of Integer . If your program attempts to access an element outside the range of index values specified for your array type the exception Constraint_Error will be raised.

If you want to collect the sales of a store for each day of the week you could provide the following declarations.

type Days is
  (Monday, Tuesday, Wednesday, Thursday,
       Friday, Saturday, Sunday);

type Daily_Sales is array(Days) of Float;
This defines an enumeration type for days of the week. It then defines an array type indexed by that enumeration type. How could we deal with such an array? After all, enumeration types are not numeric types. Iteration would seem to be an issue. Ada solves this problem with the syntax of its for loop.
function Weekly_Total(Shop_Sales : Daily_Sales)
   return Float is
   Sum : Float := 0.0;
begin
   for index in Shop_Sales'Range loop
      Sum := Sum + Shop_Sales(index);
   end loop;
   return Sum;
end Weekly_Total;
This function cleanly iterates through all values in the Daily_Sales object passed through the function parameter. ‘Range is an attribute of an array that returns the range of values for the index. The for loop iterates through that range.
record A record in Ada is equivalent to a struct in C, or a record in Pascal. A record is a grouping of possibly dissimilar data elements into a single type. Ada defines two basic kinds of records, ordinary records and tagged records. Tagged records are extensible through inheritance. Ordinary records are not extensible.
Ordinary Record Type
type Inventory_Item is record
   UPC_Code    : String(1..20);
   Description : String(1..256);
   Quantity    : Natural;
   Unit_Cost   : Float;
end record;
Every instance of Inventory_Item contains four fields: UPC_Code, Description, Quantity, and Unit_Cost.
Tagged Record Type
type Person is tagged record
   Name   : String(1..20);
   Gender : Gender_Type;
   Age    : Natural;
end record;
Like an ordinary record, a tagged record may contain many fields. The difference is that a tagged record becomes the root of an inheritance hierarchy.
type Employee is new Person with record
   Id : Integer;
   Salary : Float;
end record;
Employee extends Person and add two additional fields, Id and Salary.
protected Protected types are designed to safely implement data sharing between tasks. Protected types are protected from inappropriate simultaneous access by multiple tasks. The definition of a protected type is split into two parts, the interface and private definitions, and the implementation of the operations associated with the protected type.
protected type Counting_Semaphore is
   procedure Release;
   entry Acquire;
   function Lock_Count return Natural;
private
   Count : Natural := 0;
end Counting_Semaphore;
The code snippet above defines the public interface to a Counting_Semaphore. It also defines the private data member of that type. This code snippet is called the specification of the protected type. It provides a calling contract with any code module that needs to interface with an instance of Counting_Semaphore.
protected body Counting_Semaphore is
   procedure Release is
   begin
      if Count > 0 then
         Count := Count - 1;
      end if;
   end Release;

   entry Acquire when Count < 11 is
   begin
      Count := Count + 1;
   end Acquire;

   function Lock_Count return Natural is
   begin
      return Count;
   end Lock_Count;
end Counting_Semaphore;
There are three kinds of operations allowed on a protected type. Procedures are used to modify the state of the protected type. Procedures get exclusive access to the instance of the protected type. Entries also may update the state of the protected type, but they have a boundary condition that must be true before they can be executed. Entries get exclusive access to the instance of the protected type. Functions must not change the state of the protected type. They can only report state. Functions get shared access to the instance of the protected type.
task Task types are used to define tasks with identical behavior. A task is a concurrent thread of execution in an Ada program. Tasks correspond to Threads in Java. Task definitions are split into two parts. The task specification defines the public interface to a task. This interface consists of some set of entry calls by which this task may be called from another task.
task type Producer is
   entry Start(Num_Items : in Positive);
end Producer;
Producer is a task type with a single entry. The Start entry requires a calling task to pass in a parameter with the formal name of Num_Items. The actual behavior of a task is defined in the task body.
task body Producer is
   Quantity : Positive;
begin
   accept Start (Num_Items : in Positive) do
      Quantity := Num_Items;
   end Start;
   for Iteration in 1..Quantity loop
      Put_Line("Produced Item" &
         Positive'Image(Iteration));
   end loop;
end Producer;
The instance of Producer will suspend at the accept call until some other task calls this task’s Start entry. When the call happens the value in the formal parameter Num_Items is copied into the local variable Quantity . The Producer will then execute the loop for Quantity iterations. In this example, when the loop completes the task completes.

At Home on the Range

Ranges are used by Ada for many purposes.

  • You specify a range when you define your own scalar type. The range of the type defines the set of valid values for the type.
    type Rankings is new Integer range 1..10;
    type Voltages is digits 12 range -12.0..12.0;
  • Ranges are used to specify subsets of types.
    type Days is (Monday, Tuesday, Wednesday, Thursday,
       Friday, Saturday, Sunday);
    subtype Weekends is Days range Saturday..Sunday;
  • Ranges are used to specify iteration limits in a for loop.
    for Num in 1..10 loop
       Put_Line(Integer'Image(Num));
    end loop;

A range is specified with the notation

low_value .. high_value

You need to specify a range when you define an array. Ada array indices may begin at any integer or enumeration value. The set of index values used for an array is determined by a range.

type Buffer is array (1..10) of Float;
type Normalized_Counts is array (-10..10) of Integer;
type Weekday_Sales is array(Days range Monday..Friday) of Float;

Ada provides a set of special functions called attributes to help in dealing with ranges. Attributes are evaluated by calling them with a special notation sometimes called a tick notation.

Attribute Name Meaning Example
First Evaluates to the first or lowest value in a range. For scalar data types First evaluates to the lowest valid value for the type. For arrays First evaluates to the first or lowest index value.
Days'First
Normalized_Counts'First
Last Evaluates to the last or highest value in a range. For scalar data types Last evaluates to the highest valid value for the type. For arrays Last evaluates to the last or highest index value.
Days'Last
Normalized_Counts'Last
Range Evaluates to the range of values for a range. For an array the Range attribute corresponds to Array’First..Array’Last.
Buffer'Range
Voltages'Range


This ends part two of this article. Click here to continue with part three, Ada Operators.

Share and Enjoy:
  • email
  • LinkedIn
  • Twitter
  • Facebook
  • Digg
  • RSS