![]() |
![]() | ||||||||||||||||||||||||||||||||||||||
|
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]()
|
Introduction and Overview Ada is a computer programming language originally designed to support the construction of long-lived, highly reliable software systems. Ada is based on Pascal, but extends and modifies it in ways which emphasize readability, avoids error-prone notation, encourages reuse and team coordination, and supports efficient implementation. A significant advantage of Ada is its reduction of debugging time. Ada tries to catch as many errors as reasonably possible, as early as possible. Many errors are caught at compile-time by Ada that are not caught or are caught much later by other computer languages. Ada programs also catch many errors at run-time if they cannot be caught at compile-time (in most implementations, this checking can be turned off to improve performance). In addition, Ada includes a problem (exception) handling mechanism so that these problems can be dealt with at run-time. Learning ObjectivesThe purpose of this lesson is to introduce you to the basic structure and syntax of Ada programs. At the end of this lesson, you should be able to:
History and Language StandardsAda is a high-level programming language developed in the late 1970s and early 1980s for the United States Department of Defense (DoD) for real-time embedded systems. Ada is the most commonly used language in U.S. weapons systems modernization. Ada was designed to be a general-purpose language for everything from business applications to rocket guidance systems. One of its principal features is that it supports real-time applications. In addition, Ada incorporates modular techniques that make it easier to build and maintain large systems. Since 1986, Ada has been the mandatory development language for most U.S. military applications. In addition, Ada is often the language of choice for large systems that require real-time processing, such as banking and air traffic control systems.Ada is named after Augusta Ada Byron (1815-52), daughter of Lord Byron, and Countess of Lovelace. She helped Charles Babbage develop programs for the analytic engine, the first mechanical computer. She is considered by many to be the world's first programmer. Ada was originally standardized by ANSI in 1983 (ISO released an equivalent standard in 1987). Ada was recently revised to add some new capabilities; this revision is called "Ada 9X" or "Ada 95." Ada compiler vendors have generally updated their compilers to add the new Ada 95 features. This lesson covers the capabilities of Ada 95, but will note which features are new to Ada 95. Ada is officially defined in its reference manual (RM). The complete Ada 95 RM is available on-line as a hypertext document. However, the RM is not intended to be a tutorial and can be hard to understand if you're not already somewhat familiar with Ada. Ada was not designed by a committee. The original Ada design was the winner of a language design competition; the winning team was headed by Jean Ichbiah (Ichbiah's language was called "Green" after the designation of his design team). The 1995 revision of Ada (Ada 95) was developed by a small team led by Tucker Taft. In both cases, the design underwent a public comment period where the designers responded to public questions and suggestions. Ada 95 is the first internationally standardized OO language (ISO, ANSI, FIPS) as it supports the essential features of object orientation ... including full inheritance and run-time polymorphism in addition to the abstraction and encapsulation always supported. Current StandardsFollowing the success of the original release of the Ada language in 1983, a standards committee was formed to review the language and update it if necessary. Since the time of the original release of Ada, there has been a large increase in the knowledge of better ways to structure code for both efficiency and correctness, so changes were made to the Ada language to incorporate some of this additional knowledge. The new standard was completed and approved in 1995, and it has been called Ada 95. The original language is often referred to as Ada 83, but either can appropriately be called Ada.Why Study Ada?Ada was developed to be more than just another programming language, since it embodies many of the modern principles of software engineering. For example, the concept of information hiding is designed into the language, rather than being implemented as a programming technique. Ada was also designed for developing large software systems, and is therefore applicable for use in programs containing hundreds of thousands, or even millions of lines of source code. It is obvious that a single person could not write such a large program in a reasonably short time, a team effort being required to complete it. Ada is especially well suited for use in a team environment.Ada BasicsThe following examples illustrates the basic elements of a simple Ada program.
-- Print a simple message to demonstrate
-- a trivial Ada program.
with Ada.Text_IO;
procedure Hello is
begin
Ada.Text_IO.Put_Line("Hello, world!");
end Hello;
The primary aspects of this example include statement termination, case
sensitivity, use of whitespace (e.g., spaces), comments, and compound
statements (or blocks).
Termination. The semicolon is a statement terminator in Ada. It tells the compiler that this particular statement is complete at this point. The semicolon at the end of the procedure is also a statement terminator since a procedure, and hence the entire program, is defined as a complete statement in Ada. Case. Ada allows you to use either case for alphabetic characters in an identifier and you can freely mix them up in any way you desire. Good programming practice, however, would lead you to select a convention for where to use upper case and where to use lower case. A good selection of case could be an aid to understanding the program since it would convey some information about what the identifiers. Ada Comments. Comments convey no information to the computer, they only aid the writer and reader of the program to understand what the original writer was trying to do within the program. All comments in Ada begin with a double minus sign, or double dash if you prefer, and continue to the end of that line. No spaces are allowed between the dashes, they must be adjacent. There is no provision for middle-of-line comments in the Ada language, only end-of-line, although they can be an entire line as illustrated in the first two lines of the example program. Comments can be placed nearly anywhere in an Ada program, including prior to the program, and following the end of it. The example programs shown on this page provide some illustrations of where comments can pe placed. White Space. Ada programming is free form, allowing you to add spaces and blank lines anywhere you wish to make the program clear and easy to understand, provided of course that you do not split up an identifier. Ada Statements. Statements are the means by which Ada allows the programmer to manipulate data. Statements are executed. Conventional Ada statements include:
Compound Statements. Ada allows you to define the equivalent of a procedure and execute it as inline code. Such a section of code is called a block and is constructed by using three reserved words, declare, begin, and end, with declarations between the declare and begin, and executable statements between the begin and end. Any new types, subtypes, variables, constants, and even subprograms can be declared in the declaration part of the block and used in the executable part. The scope of the declarations begin where they are declared, and end at the end of the block. A block is a single statement and because it is, it can be put anywhere that it is legal to put any other executable statement. It could be used within a loop, in one branch of an if statement, or even as one of the cases of a case statement. If no declarations are needed, you can declare a block without the reserved word declare, using only the execution block delimiters begin and end. Here is another version of the "Hello World" program.
with Ada.Text_IO;
use Ada.Text_IO;
procedure Hello1 is
-- no variables needed here
begin
Put ("Hello world from Ada!");
end Hello1;
Can you identify what is different from the previous one?
More about this program later.
Program OrganizationThe basic syntax for an Ada program is illustrated below:
procedure <Program_Name> is
<Declaration_Part>
begin
<Executable_Part>
end <optional repeat of Program_Name>;
There are two portions of any Ada program, a Declaration_Part, which is
contained between the reserved words is and begin, and
the Executable_Part which is contained between the reserved words
begin and end.
The Program_Name must match the file name for your Ada program. For example, if your Program_Name is HelloWorld, the file name must be HelloWorld.adb. For the class server and the GNAT compiler that we are using, there is no case sensativity, e.g., HelloWorld in the program file is considered to be the same as helloworld.adb in the file name. Following the reserved word end is a repeat of the Program_Name and a semicolon. Repeating the Program_Name is optional but, as a matter of style, you should get into the habit of including it at the end of every program. The semicolon is required to end the program. An Ada program is composed of one or more program units. A program unit can be a:
The package is structurally the most important kind of program unit. Most Ada programs are basically a set of a large number of packages, with one procedure used as the "main" procedure to start the Ada program. Packages can represent an abstract data type or a set of data objects shared among subprograms. Including the notion of packages, a more accurate specification of a basic Ada program would include the package statements at the top of the program file. This is illustrated below:
with <Package1_Name>; use <Package1_Name>;
with <Package2_Name>; use <Package2_Name>;
.
.
.
with <PackageN_Name>; use <PackageN_Name>;
procedure <Program_Name> is
Variable1 : <Some_Type>;
Variable2 : <Some_Type>;
.
.
.
VariableM : <Some_Type>;
begin
<Statement_1>;
<Statement_2>;
.
.
.
<Statement_K>;
end <Program_Name>;
Some programs might be very wordy if you had to always specify where a
procedure is defined in order to use it. Thus, Ada provides the use clause.
Whenever you use a procedure (or something else) but do not specify where
it is defined, the Ada compiler will search all units listed in applicable
use clauses. Use clauses follow the with clause, begin with the keyword use,
and then list the library units to be searched. Here is how that first
program would look with a use clause:
-- Print a simple message to demonstrate a
-- trivial Ada program.
with Ada.Text_IO;
-- use clause - automatically search Ada.Text_IO.
use Ada.Text_IO;
procedure Hello2 is
begin
-- Note: No longer has "Ada.Text_IO" in front.
Put_Line("Hello, world!");
end Hello2;
The following program uses the previous "Hello world!" program as a package.
with Hello; -- refer to compiled version of Hello procedure Hello3 is begin Hello; Hello; Hello; end Hello3;Here is another example Ada program, one which includes a Declaration_Part for a string constant.
-- filename UltimateAnswer.adb
-- Introductory Ada 95 program
-- for Chapter 12, Section 12.1.
-- October 22, 1999
with Ada.Text_IO;
use Ada.Text_IO;
procedure UltimateAnswer is
The_Answer : constant STRING := "forty two.";
begin -- UltimateAnswer
Put_Line( "The answer to life, the universe");
Put_Line( "and everything else is - ");
Put_Line( The_Answer );
end UltimateAnswer;
Compare this program to the previous examples.
Now lets look at an example which illustrates how to get input from the user.
with Ada.Text_IO; use Ada.Text_IO;
procedure Get_Name is
Name : String (1..80);
Length : Integer;
begin
Put("Enter your first name> ");
Get_Line(Name, Length);
New_Line;
Put("Hello ");
Put(Name (1..Length));
Put(", we hope that you enjoy learning Ada!");
end Get_Name;
Note that New_Line is used in the program above to enter a "carriage
return, line feed" into the output stream.
Variables and IdentifiersAn identifier is a name we use to refer to any object in Ada and it must be formed by following some fairly rigid rules. We will list the rules for forming a valid identifier, then make up a few for illustrative purposes.
Ada -- A perfectly valid identifier ADA -- The same one, case doesn't matter Ada_Compiler -- A very descriptive identifier The_Year_1776 -- Another descriptive identifier a1b2c3d4e5f6 -- Very nondescript, but validand a few invalid identifiers: 12_times_each -- Can't start with a number This__is__neat -- Multiple underlines illegal This is neat -- blanks illegal Ada_"tutorial" -- special characters illegalBy this time you should be able to recognize a valid Ada identifier. It may seem like a lot of effort to define a valid identifier in such detail, However, you will be naming everything you use in Ada, so you must know how to name things before you can do anything meaningful with the language. In addition to an identifier being correct, it should also be easy to use and meaningful. For example, consider the following list of valid Ada identifiers and see which convey to you some idea of what they represent. GrossPay Time_Of_Day dateofbirth Final_Score Get_the_Current_Temperature X24 atx w t2Ada was designed to be written once and read many times. This is truly what happens with any non-trivial program designed and developed by a group of persons. As such, little attention is paid to the fact that it may be a bit tedious to key in long identifiers when the program is being written. The extra effort pays off when it is read repeatedly, since it is so easy to follow the logic of the program. The first three identifiers above are preferred because of the information they convey to the reader, and the last three are to be considered of little value in defining the program logic. Of course, if you were using a mathematical relationship that used the variable named "t" in its calculations, that particular name for a variable might be a good choice.
-- Demonstrate a trivial procedure, with
-- another nested inside.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Compute is
procedure Double(Item : in out Integer) is
begin -- procedure Double.
Item := Item * 2;
end Double;
-- Local variable X of type Integer.
X : Integer := 1;
begin -- procedure Compute
loop
Put(X);
New_Line;
Double(X);
end loop;
end Compute;
Note that the local variable called X is of type Integer with an
initial value of one. Integers are used when you want to store possibly
signed integers, and you don't care what the minimum and maximum range
is. As we will see later, there are other things you should do if you do
care what the minimum and/or maximum range is.
Inside this new procedure is a local procedure called "Double," which takes a value and doubles it. A local procedure, like a local variable, can only be used inside the procedure surrounding it. This capability to nest procedures inside other procedures is useful in larger programs and is a standard capability in Pascal (though not in C or C++). The phrase in out means that the value is both received and changed in the procedure. The phrase with .. Ada.Integer_Text_IO permits use of a predefined Ada 95 package for performing text input and output on Integers. It includes an operation named Put that will print an Integer sent to it. The second begin statement defines the Compute procedure itself. Compute has an infinite loop, which prints the current value and doubles it repeatedly. Put prints out the number, and New_Line causes the text to go to the next line. Computers cannot really compute an infinitely large value; sooner or later they will run out of space to store the number. What will happen in this case? Some programming languages (notably C) simply permit garbage to be computed. Ada has a better approach: when a computation (such as doubling) cannot be performed, Ada raises an "exception." Thus, sooner or later this program will halt with a message explaining why and where it halted. As we will learn later, these exceptions can be caught and handled inside the program. KeywordsAda 95 uses 69 identifiers which are called reserved words or keywords. Note that Ada 83, by contrast, only had 63 keywords. Keywords are reserved for specific uses within an Ada program and cannot be used for any other purpose. As you study the language, you will see very clearly how to use each of the keywords and why these particular words were chosen. Since Ada is a large language containing many options and cross checks, writing an Ada compiler is an enormous job, but the use of keywords simplifies that job. The keywords also make the final program much easier to read and understand.The 69 Ada 95 keywords include the following: abstract abort accept access aliased all and array at begin body case constant declare delay delta digits do else elsif end entry exception exit for function generic goto if in is limited loop mod new not null of or others out package pragma private procedure protected raise range record rem renames requeue return reverse select separate subtype tagged task terminate then type until use when while with xorRemember, Ada keywords may not be used as identifiers. DeclarationsProgram units (including subprograms and packages) normally consist of two parts:
These separate parts of program units are usually stored in separate files. This explicit distinction between declaration and body allows a program to be designed, written, and tested as a set of largely independent software components. There are two special cases to help make programming easier:
Data TypesEvery variable (object) has an associated type. The type defines a set of values which the variable can have assigned to it, and it also defines a set of operations that can be performed on the variable. Ada is a strongly typed language since it has very strict rules limiting how a variable can be used. The compiler will not give you a usable program unless you follow all of the rules very carefully. A major part of the study of Ada involves the study of types and how to use typing as a programming aid.Ada is a block-structured language in which the scope of declarations, including object and type declarations, is static. Static scoping means that the visibility of names does not depend on the input data when the program is run, but only on the textual structure of the program. Static properties such as visibility can be changed only by modifying and recompiling the source code. Types in Ada can be categorized in a number of different ways. There are elementary types, which cannot be decomposed further, and composite types which, as the term implies, are composed of a number of components. The most important form of composite type is the record which comprises a number of named components themselves of arbitrary and possibly different types. A broad hierarchical classification of Ada data types is illustrated below:
Ada Types
|
+-------------+-----------+
| |
Elementary Types Composite Types
| |
+------+----+ +-------+--------+------+
| | | | | |
access scalar array record protected task
|
+-------+--------------+
| |
discrete real
| |
+----+-------+ +----+-------+
| | | |
enumeration integer float fixed
| |
+-----+-------+ +-----+-----+
| | | |
signed modular decimal ordinary
Types. A type is either an elementary type or a composite type. Elementary types
cannot be decomposed further whereas the composite types have an inner
structure. The elementary types can be further categorized into the
scalar types and access types. The composite types comprise familiar
array and record types plus the protected and task types which are
concerned with multitasking.
Scalar Type. Scalar types are themselves subdivided into the discrete types and the real types. The discrete types have certain important common properties; for example, they may be used to index array types. The discrete types are the enumeration types and the integer types. The integer types in turn comprise signed integer types and modular (unsigned) types. The real types comprise the other forms of numeric types. Enumeration Type. An enumeration type defines an ordered set of distinct enumeration literals, for example a list of states or an alphabet of characters. The enumeration types Boolean, Character (the 8-bit ISO standard character set) and Wide_Character (the 16-bit ISO standard character set) are predefined. Numeric Type. The numeric types do not exactly fit the hierarchy as presented, but there are certain properties that are common to all numeric types (such as the availability of arithmetic operations), so we have indicated this by a double box surrounding the numeric types. Numeric types provide a means of performing approximate or exact numerical computations. Approximate computations may be performed using either fixed point types with absolute error bounds, or floating point types with relative error bounds. Exact computations may be performed with either integer types, which denote sets of consecutive integers, or with decimal fixed point types. The numeric types Integer, Float and Duration are predefined. with Ada.Float_Text_IO; use Ada.Float_Text_IO; procedure Think is -- A and B initially zero, -- note the decimal point A, B : Float := 0.0; I, J : Integer := 1; begin A := B + 7.0; I := J * 3; B := Float(I) + A; Put(B); end Think;Access Type. Access types, the remaining form of elementary type, allow the construction of linked data structures. The value of an access type is, in essence, a pointer to an object of another type, the accessed type. The accessed type may be any type. In particular the accessed type may be a class-wide type thereby allowing the construction of heterogeneous linked data structures. Access types may be used to designate objects created by allocators, declared objects (provided they are marked as aliased) and subprograms. Array Type. Array types allow definitions of composite objects with indexable components all of the same subtype. Array types may be of one or more dimensions. The types of the indexes must be discrete. The array types String and Wide_String are predefined. Record Type. Record types are composite types with named components not necessarily of the same type. Record types may be tagged or untagged. A tagged record type may be extended upon derivation and gives rise to a class-wide type which forms the basis for dynamic polymorphism. A tagged record type may also be marked abstract in which case no objects of the type may be declared; an abstract type may have abstract subprograms (these have no body). Abstract types and subprograms form a foundation from which concrete types may be derived. Protected Type. Protected types are composite types that provide synchronized access to their inner components via a number of protected operations. Objects of protected types are passive and do not have a distinct thread of control; the mutual exclusion is provided automatically. Task Type. Task types are composite types which are used to define active units of processing. Each object of a task type has its own thread of control. Type Compatibility & ConversionExplicit Type Conversion. Explicit type conversion is permitted only between closely related types.
Here are some example type declarations: type count is range 1..5000; type current is delta 0.025 range -100.0..100.0; type voltage is delta 0.01 range -500..500.0; type power is digits 6; type weight is digits 6; type matrix3 is array(1..3, 1..3) of float; type index is new count; type rot_matrix is new matrix3; type vector is array(integer range <>) of float; subtype vector6 is vector(1..6); type long_vector is array(1..36) of float; no_rounds : count; reading : index; cur_meas : current; volt_meas : voltage; rotate_1 : matrix3; rotate_2 : rot_matrix; temp_vector : long_vector;Using the type declarations above, the following examples illustrate explicit type conversion:
integer(cur_meas) - real value is rounded
to nearest integer
weight(3 * no_rounds) - integer value is
converted to floating
point
index(no_rounds) - converts value of type
count to value of type
index. Both count and
index are distinct
integer types.
power(cur_meas*volt_meas) - converts universal_
fixed result to float-
ing point type power.
The above conversions would occur as part of an assignment statement.
Implicit Type Conversion. There are som subtle implicit type conversions in Ada, relating to the anonymous predefined types universal_integer, universal_real, and universal_fixed. Universal types in Ada are predefined anonymous types with no bounds, and for universal_real or universal_fixed types, an infinite precision. Obviously, these data types are conceptual in nature and values of these universal types must be converted to some numeric type before they can be assigned. Such conversion is implicit in the case of a numeric literal, a named number, or an attribute. In other cases, the conversion must be explicit (as described above). Using the previous type declarations, the following list illustrates several implicit type conversions:
index := 32; - conversion from universal_
integer to integer type
count.
flow_rate := 0.8; - conversion from universal_
real to floating point type
value.
Assignment StatementsAn assignment statement causes the value of an identifier or variable to be replaced by that of an expression of the same type. Assignment is normally performed by a simple bit copy of the value provided by the expression. However, in the case of nonlimited controlled types, assignment can be redefined by the user.Zero := 0; C := Zero; C := C + 1; Index := 25;The combination := can be read as "gets the value of." Thus, the last example above can be read as, "Index gets the value of 25." The equal sign alone is reserved for another use. Actually, if we say that Index = 25, it is only mathematically correct if Index is never changed, but since it is a variable and will be changed, the equality is not true. ConstantsConstants should be used in a program wherever there is a value which is not going to change during program execution. In fact, if you attempt to change the value of a constant during program execution, Ada will give you an error message at compile time.A constant must have an initial value assigned to it when it is declared. The examples below illustrate the syntax for declaring and initializing constants. Dozen : constant integer := 12; Gross : constant integer := 12 * Dozen; Big_No : constant := 32_24_7; Two : constant := Big_No - 3_22_45;The following list illustrates some meaningful names for constants, their values and their type. Pi 3.14159 float MilesToYards 1760 integer MilesToFeet 5280 integer Weeks_In_Year 52 integer Labor_Hours 2080 integer NumStudents 50 integer LitersToPints 0.568 float PintsToLiters 1.7605 float RunAgain 'y' character User_Name "dsmith" stringConstants have two advantages. The first is that where they are used in calculations, the name of the constant can be used in place of the value allowing the meaning of the program to become clearer: EnglishVolume := MetricVolume * 0.568; EnglishVolume := MetricVolume * LitersToPints;Without fully understanding the meanings of these statements, it should be clear that the second version of the assignment statement above is more meaningful than the first version. The second advantage of constants is that if the value of a constant needs to be changed, it can be accomplished with only a single change to the program text. For the example above, if the number of students is increased to 75, then only the declaration of the constant NumStudents will have to be changed. The program should be written so that changing the value in the constant declaration will result in other changes in the program, but will ensure that the program still works correctly. One of the principles which is used to differentiate between well written programs and poorly written programs is the ease with which the program can be modified when a change is required. The appropriate use of constants will allow such modifications to be more easily accomplished. Constants are only constant during program execution. In the examples above some of the constants will never change, for example the number of yards in a mile. Other constants may change during the lifecycle of the application. For example, greater accuracy may be required in the value of Pi, or the number of students in a course may increase. However, while a program is executing the value of a constant used by the program cannot be changed. ExpressionsAn expression is a program element formed of operations and operands that can be evaluated to yield a value. In Ada, an expression has a type which depends upon the operands and operations contained in the expression. Ada's strong typing riles apply both in the evaluation of an expression and in the assignment of the resulting value.pi : constant := 3.141_592_655; sum : float; line_count : integer; vel : float; evaluation : threat; hex_count : hexdigit; armed : boolean;The term primaries is used to refer to the fundamental constituents of an expression. The only names permitted are named numbers, attributes that yield values, and names denoting objects or denoting values. Here are some examples of primaries: pi - a named number sum - a name for an object integer'last - an attribute 4.7 - a numeric literal null - the value null (1..25 => '*') - an array aggregate (line-count+1) - a parenthesized expression new threat - an allocatorThe following list illustrates a few example Ada expressions which are based on the declarations immediately above: vel - a primary not armed - a factor 16.2 * vel - a term 4.7 - a simple expression -3.2 * vel - a simple expression vel**2 + 10.0 * sum - a simple expression line_count in 3..20 - a relationThe symbols used by Ada to represent the basic arithmetic operators are shown below. They are listed in order of their priority (top to bottom).
Comparisons are made with the use of Ada relational operators. The table below summarizes these operators.
The program below illustrates a number of Ada comparisons using Boolean and integer variables.
with Ada.Text_IO;
use Ada.Text_IO;
procedure Compare is
package Enum_IO is
new Ada.Text_IO.Enumeration_IO(BOOLEAN);
use Enum_IO;
One : INTEGER := 1;
Two : INTEGER := 2;
Three : INTEGER := 3;
Is_It : BOOLEAN := TRUE; -- initialized
Which : BOOLEAN; -- uninitialized
begin
Which := TRUE;
Put("Which now has the value of ");
Put(Which);
New_Line;
Which := FALSE;
Put("Which now has the value of ");
Put(Which);
New_Line;
Is_It := (One + 1) = Two;
Is_It := One /= Two;
Is_It := One + Two >= Three;
end Compare;
| ||||||||||||||||||||||||||||||||||||||
| |
| Added to the Web: October 20, 1999. Last updated: October 27, 1999. Web page design by Dan Solarek. |
![]() http://cset.sp.utoledo.edu/cset4250/ |