An array type can be declared which is an array of arrays:
type week is array (1..7) of integer;
type month is array (1..5) of week;
may : month;
in which case we would refer to the second week, seventh day of may as
may(2)(7);
The simplest assignment statement has the form
variable := constant;
For instance, we might have
N := 1;
We can also have
variable := expression;
And in a declaration statement, we can include an initialization
assignment:
variable : type := expression;
In particular, note the following assignment of a logical variable:
LOG1 := LOG2 and not LOG3;
Note that complex, derived datatypes can be copied wholesale. As long as A, B, and TEMP have the same type, the following code swaps them:
temp := a;
a := b;
b := temp;
It's best to think of attributes as properties of a type, not of an individual value of that type.
For instance, given that we are working with the type INTEGER, it makes sense to ask what the largest and smallest integers are, the number of bits used to store an integer, the maximum number of characters it takes to print an integer, and so on.
The sort of attributes you can ask for depends on the kind of type - scalar or array, discrete or continuous, enumeration, and so on.
For an array type, such as
type MYARRAY is array (1..10) of REAL;
we can request the following attributes:
integer := MYARRAY'FIRST;
returns the lower bound of the first index range.
integer := MYARRAY'FIRST(N);
returns the lower bound of the N-th index range.
integer := MYARRAY'LAST;
returns the upper bound of the first index range.
integer := MYARRAY'LAST(N);
returns the upper bound of the N-th index range.
integer := MYARRAY'RANGE;
returns the range of the first index, that is, the
range MYARRAY'FIRST..MYARRAY'LAST.
integer := MYARRAY'RANGE(N);
returns the range of the N-th index.
integer := MYARRAY'LENGTH;
returns the length of the first index range, that is, the
value MYARRAY'LAST-MYARRAY'FIRST.
integer := MYARRAY'LENGTH(N);
returns the length of the N-th index range.
Here are some of the things we can ask for the INTEGER type, or for any of its subtypes or for any ENUMERATION type (which the integer essentially is):
integer := INTEGER'FIRST;
returns the first integer.
string := INTEGER'IMAGE(int_val);
returns a string containing the integer representation.
integer := INTEGER'LAST;
returns the last integer.
integer := INTEGER'POS(int_val);
returns the index of the integer.
integer := INTEGER'PRED(int_val);
returns the predecessor of the integer.
integer := INTEGER'SIZE;
returns the number of bits used to represent the integer.
integer := INTEGER'SUCC(int_val);
returns the successor of the integer.
integer := INTEGER'VAL(index);
returns the integer of the given index.
integer := INTEGER'VALUE(string_val);
returns the integer represented by the string.
integer := INTEGER'WIDTH;
returns the maximum number of spaces needed to represent any integer.
For a string, declared, for instance, as
MYSTRING : STRING(1..80);
we essentially regard the name "MYSTRING" as a type. We can ask
for the values of:
The CASE statement allows the programmer to select a set of statements to execute, based on the value of a variable which must be of enumeration type.
A set of statements may be selected by specifying a single value of the variable, a range, a set of values (separated by |), or the final choice of "others" representing all values not so far specified.
case TODAY is
when MON..THU =>
Work;
when FRI | SAT =>
Work;
Party;
when others =>
null;
end case;
A constant is a data item which is given an initial value that cannot be changed. Here are some sample declarations:
hundred : constant INTEGER := 100;
SQRT2 : CONSTANT FLOAT := 1.414;
PI : CONSTANT := 3.14159265;
The arithmetic type of PI is not declared, so it is taken as a universal
real based on the literal value it is assigned.
The value of a constant can never be changed. An attempt to do so will cause a run time error.
Here is another constant declaration, which declares the type of the variable DISC, and uses a formula rather than a literal for the value:
procedure roots(a : in REAL; b : in REAL; c : in REAL;
root1 : out REAL; root2 : out REAL) is
disc : constant real := b**2 - 4.0*a*c;
begin
end roots;
Here, DISC is a "constant" in the sense that it resides in a procedure which has input values of A, B, and C. DISC is evaluated at the time that the procedure itself is "elaborated", and is assigned a "constant" value at that time.
Simple declarations:
threats : array(1..10) of integer;
falling : boolean;
twenty : constant integer := 20;
hundred : constant integer := 5*twenty;
fifty : constant := 50;
More complex declarations:
type weekday_type is (Sunday, Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday);
day : weekday_type;
type quadrant_type is ( front_left, front_right, back_left, back_right);
onoff lights is array(quadrant_type) of boolean;
The user can specify the actual internal codes used to represent the literals of the enumeration type, using an enumeration representation clause:
FOR enumeration_type USE aggregate;
For example,
type SUITS is (Hearts, Clubs, Diamonds, Spades);
for SUITS use (Hearts=>1, Clubs=>2, Diamonds=>3, Spades=>99);
The integer codes must be distinct, and must satisfy any predefined
ordering relations for the type.
An EXIT statement can be used to jump out of a plain loop, a FOR loop, or a WHILE loop.
loop
n := n+1;
if n > 44 then
exit;
end if;
end loop;
...the EXIT statement jumps here.
You can also put a condition on the EXIT statement:
loop
n := n+1;
exit when n > 44;
end loop;
...the EXIT statement jumps here.
When loops are nested, an EXIT statement may use a loop label to specify which loop or set of loops is to be escaped:
harold: loop
george: loop
n := n+1;
exit harold when n > 44;
end loop george;
end loop harold;
...the EXIT statement jumps here.
For completeness, you can also exit a particular loop:
harold: loop
george: loop
n := n+1;
exit george;
end loop george;
...the EXIT statement jumps here.
end loop harold;
Note that the EXIT statement terminates the loop, not just the particular iteration during which it occurs.
The ADA FOR loop carries out one or more statements a given number of times.
for i in 1..10 loop
n := n+1;
end loop;
A FOR loop can be traversed in reverse order:
for i in reverse 1..10 loop
n := n+1;
end loop;
but (PECULIARITY) you cannot specify an arbitrary increment.
Moreover, apparently, the FOR loop index cannot take on negative values. The following loop will not compile:
for i in -10..10 loop
n := n+1;
end loop;
The "in" in the loop declaration is a test for membership. The FOR loop index consists of all the elements of a set. The set need not be a count from 1 to 10. It can be of any enumeration type:
type color is (VIOLET, INDIGO, BLUE, GREEN, YELLOW, ORANGE, RED);
i : color;
for i in VIOLET..RED
n := n+1;
end loop;
A typical function might be declared as
function fred ( I : in INTEGER; X : inout REAL; Z : out CHARACTER)
return BOOLEAN;
The GOTO statement has the form
goto LABEL;
The goto statement transfers control to the statement following
the given label.
For instance, the following code
for i in 1..N
if x(i) = 1 then
goto FOUNDIT;
end if;
end loop;
<>
To print out integers, you will need, in your specification area:
with text_io; use text_io;
package int_io is new integer_io(integer);
INT_IO is a temporary local name you are making up. INTEGER_IO is the name of a generic package inside of TEXT_IO, which can accept INTEGER (or reasonable variations) arguments for the kind of quantity to be printed.
To print floating point values also, add
package real_io is new float_io(float);
Then, to print stuff, try things like:
angle : float = 3.14159265;
iangle : int = 180;
text_io.put("Angle (radians) = ");
real_io.put(angle);
text_io.put(", while rounded angle in degrees is ");
int_io.put(iangle);
text_io.new_line;
Similarly, for an enumeration data type, try:
type suits is (HEARTS, CLUBS, DIAMONDS, SPADES);
package suits_io is new enumeration_io(suits);
Now the statements
mysuit := CLUBS;
suits_io.put(mysuit);
will actually result in the desired output
CLUBS
rather than some sad numeric obfuscation.
The simplest IF statement:
if n < 1 then
n := 1;
end if;
The second simplest IF statement includes an ELSE:
if n <= 1 then
n :=1;
else
n := n+1;
end if;
Compound IF statements require the ELSE and the (PECULIARITY) misspelled ELSIF statements:
if a >= 0 then
c := b / a;
elsif b /= 0 then
c := 0;
else
c := 1;
end if;
PECULIARITY: "end if" cannot be written as "endif".
Literal values are representations of particular numbers, vectors, strings, or other items.
An array or vector value can be represented by the component values, separated by commas, and delimited by parentheses.
a := (1, 2, 3);
To represent a vector of all zeroes, write:
a := ( others=>0 );
A single vector entry is a scalar:
a(1) := 7;
A matrix must be thought of as an array of rows. Thus, a matrix value can be represented as:
a := ( (1,2,3), (4,5,6), (7,8,9) );
You may indicate the individual rows in a literal, in
which case the rows may be listed in any order:
a := ( 1=>(1,2,3), 3=>(7,8,9), 2=>(4,5,6) );
or you may list rows and columns. Here is one possibility:
a := ( 1=> ( 1=>1, 2=>2, 3=>3 ),
2=> ( 1=>4, 2=>5, 3=>6 ),
3=> ( 1=>7, 2=>8, 3=>9 ) );
To represent a matrix all of whose entries are 17, write
a := ( others => ( others => 17 ) );
A single matrix entry is a scalar:
a(1,2) := 17;
The tokens TRUE and FALSE are reserved words, and are the two legal values for a BOOLEAN quantity.
The alphabet is 'a' through 'z', and so on. Other printable characters are also themselves, in single quotes. Non-printable characters have special names.
char1 := 'a';
char2 := ASCII.ESC;
The values assignable to an enumeration variable are exactly those listed in the type statement. Thus, if the type statement is
type suits is ( Hearts, Clubs, Diamonds, Spades);
and the variable MYSUIT is declared as
mysuit : suit;
then a legal assignment of MYSUIT is
mysuit := Clubs;
If an enumeration type SUITS is declared in package FRED, then you can use that enumeration type in another package, such as GEORGE, by prefixing the package name to the enumeration type. For instance, you might declare
mysuit := FRED.SUIT;
and assign
mysuit := FRED.CLUBS;
Fixed point and floating point literals are called "decimal literals", and require a decimal point. They may include an exponent, with the marker "E" written in upper or lower case. Underscores may be inserted to group digits for readability:
12.0, 0.0, 0.456, 3.14159_26, 1.34E-12, 1.0E+6.
At least for integer literals, there's not much to say. Seventeen is just plain old '17'. Note, however, that you can specify a base, so that #2#101# means 101 base 2, that is, '5'.
A "record" is a datatype with "parts". Presumably, we know how to specify literal values for each of the parts, so the main question is how we put those values together to specify a literal record value.
Supposing that we have defined:
type FREDTYPE is record
temp : FLOAT;
age : INTEGER;
name : STRING(20);
end record;
FRED : FREDTYPE;
then a literal value for FRED could be specified as:
FRED := ( 98.6, 23, "Fred Smith" );
where order is important, or
FRED := ( name => "Fred Smith", age=>23, temp=>98.6 );
where order is not important, since the record component names
are used as keywords to label each value.
A string literal is simply text delimited by double quotes. If a double quote should actually appear as part of the string, then it must be doubled in the literal.
s1 := "alligator";
s2 := "Contains a "" double quote!";
s3 := "c";
s4 := ""; -- A null string is allowed.
A string must fit on one line, or be patched together from smaller strings using the "&" operator:
e := "A really....long " &
"string that goes over two lines."
Unprintable characters have symbolic names defined in the package ASCII:
f := "String that includes the " & ASCII.ACK & " control character."
ADA has a very stupid attitude toward strings. Strings can not have individual elements altered. ALL elements must be set. And you can't do the following:
fred : string(80);
fred := "Hello!";
because you are saying that an 80 character string equals a 6
character string. You have to do something more complicated and
unnatural. If you are lucky enought that FRED is a constant, then you
can get away with:
fred : string(1:6) := "Hello!";
or
fred : constant string := "Hello!";
Or, if only a few characters of fred have to be set:
fred := ( 1..6 => '*', others=> ' ');
But in general, the handling of strings in ADA is a real LOONEY TUNE!
The simplest loop has no control:
loop
n := n+1;
end loop;
An EXIT statement might be very useful here:
loop
n := n+1;
if n > 44 then
exit;
end if;
end loop;
You can also put a condition on the EXIT statement:
loop
n := n+1;
exit when n > 44;
end loop;
A loop can be labeled:
harold: loop
n := n+1;
end loop harold;
When loops are nested, an EXIT statement may use a loop label to specify which loop or set of loops is to be escaped:
harold: loop
george: loop
n := n+1;
exit harold when n > 44;
end loop george;
end loop harold;
Variables, procedures, functions, and other entities must be named. Names must begin with a letter, and may contain letters, numbers, and underscores.
Names are not case sensitive.
The NULL statement can be used as a filler for otherwise-empty IF/THEN/ELSE statements, CASE statements, package and procedure bodies, and so on, where it's actually illegal, or merely confusing, to leave a null body.
if n < 1 then
null;
else
n := n+1;
end if;
All data types have the logical equality and nonequality operators:
if n = 1 then...
if n /= 1 then...
Numeric or enumeration or character or string or boolean data has ordering relations as well:
if n > 1 then...
if n >= 1 then...
if n < 1 then...
if n <= 1 then...
A variable that is of enumeration type can have the membership operator:
if n in a then
if n not in a then
A variable of logical type can be used in Boolean operations:
if a and b then
if a or b then
if a xor b then
if not a then
There are also "SHORT CIRCUIT" forms:
if x /= 0.0 AND THEN sin ( 1.0 / x ) == 1.0 then
string := string1 & string2 -- String concatenation.
string := char1 & string1 -- Prefix string with given character.
string := string1 & char1 -- Append character to string.
string := char1 & char2 -- Create string from characters.
array := array1 & array2; <-- concatenate two arrays.
array := entry1 & array1; <-- preprend an entry to an array.
array := array1 & entry1; <-- append an entry to an array.
array := entry1 & entry2; <-- construct an array from two entries.
a+b;
a-b;
-b;
a*b;
a/b;
a**b; b must be an integer.
a mod b; has the sign of b and absolute value less than abs(b);
a and b must be integers.
a rem b; has the sign of a and absolute value less than abs(b);
a and b must be integers.
abs(a);
PECULIARITY: No square root function!
Consider the following package:
package fred is
procedure bob;
end fred;
package body fred is
procedure bob is
begin
end bob;
begin
text_io.put("Hi there mom!");
end fred;
The "executable" code portion of fred will actually be invoked each time that a package or procedure containg the statement with fred is elaborated. So you can use this chunk to do initializations, to call other procedures, print out titles, whatever you wish.
Representation clauses specify how the types of the language are to be mapped onto the underlying machine.
A representation clause has the form:
representation_clause ::= type_representation_clause OR address_clause
A type representation clause has the form:
type_representation_clause ::= length_clause OR
enumeration_representation_clause OR record_representation_clause
A length clause specifies an amount of storage associated with a type:
length_clause ::= FOR attribute USE simple_expression;
The only allowed attributes are SIZE, STORAGE_SIZE and SMALL, prefixed
by the name of the type or subtype.
An enumeration representation clause has the form:
FOR type_simple_name USE aggregate;
The aggregate used to specify this mapping is written as a
one dimensional aggregate, for which the index subtype is the
enumeration type.
A record representation clause has the form:
record_representation_clause ::=
FOR type_simple_name USE
RECORD [alignment_clause]
{component_clause}
END RECORD;
An address clause specifies a required address in storage for an entity:
address_clause ::= FOR simple_name USE AT simple_expression;
The expression given after AT must be of the type ADDRESS defined in
the package SYSTEM, and SYSTEM must be named by a WITH clause.
A representation clause is used to impose certain characteristics of the mapping of an entity onto the underlying machine, pragmas can be used to provide an implementation with criteria for its selection of such a mapping. In particular, the pragma PACK specifies that storage minimization should be the main criterion when selecting the representation of a record or array type:
pragma PACK(type_simple_name);
A reserved word must not be used as an identifier. Note that when a reserved word is used for an identifier name, the compiler may not be able to clearly report the error.
abort
abs
accept
access
all
and
array
at <-- used in certain representation clauses.
begin
body
case
constant
declare
delay
delta
digits
do
else
elsif
end
entry
exception
exit
for
function
generic
goto <-- Cannot be spelled "GO TO" with a space!
if
in
is
limited
loop
mod
new
not
null
of
or
others
out
package
pragma
private
procedure
raise
range
record
rem
renames
return
reverse
select
separate
subtype
task
terminate
then
type
use
when
while
with
xor
A RETURN statement completes the execution of a function or procedure (or ACCEPT statement). A RETURN statement in a procedure must not include an expression, and hence simply has the form:
return;
A RETURN statement in a function must include an expression, specifying
the value computed by the function, for instance:
return x+y;
Statements may be labeled. It is possible for a labeled statement to consist of a label and nothing else. More typically, a labeled statement consists of a label and an associated executable statement. A label is an identifier enclosed in double angle brackets.
A labeled statement may be the target of an EXIT or GOTO statement.
<>
if A(I) < ELEMENT then
if LEFT(I) /= 0 then
I := LEFT(I);
goto COMPARE;
end if;
...more statements...
end if;
The names of the predefined types can be used, in most cases, to convert data from other types, if it is a sensible operation. Thus,
x : float;
i : integer;
x := 3.14159265;
i := integer(x);
Note, in particular, that the INTEGER conversion operator rounds to
nearest, rather than truncating to -infinity or to zero.
A WHILE loop is easy to fashion:
while angle < 0.0 loop
angle := angle + 360.0
end loop;
If the test condition holds, then the body of the loop will be executed.
If the "bottom" of the loop is reached, that is, the end loop,
then execution will return to the WHILE, where the condition will
be checked again, for a possible repeat of the loop.
If ADA routines in a file FILE1 refer to routines or datatypes or other quantities defined in file FILE2, then in the specifications part of the program there should be at least the statement:
with file2;
In that case, when a quantity from FILE2 is referenced in FILE1,
its name must be preceded by "FILE2.". However, (assuming there
will be no ambiguity) this prefix can be dropped by adding the
USE statement:
with file2; use file2;
You can return to the HTML web page.